Cumulative patch from commit f89c32e63f71e66d7b55e284016762b603ae02db

f89c32e Android: Fix max number of sched scan SSIDs based on driver capability
f1a5a34 binder: Implement interface add/remove methods
7b4bbb9 binder: Add binder skeletal code for Android
5914ebf Remove struct ieee80211_mgmt::u.probe_req
094e949 atheros: Do not use struct ieee80211_mgmt::u.probe_req
c01120a wpa_supplicant: Do not use struct ieee80211_mgmt::u.probe_req
e1b9962 AP: Do not use struct ieee80211_mgmt::u.probe_req
5cd317d Do not clear PMKSA entry or EAP session cache if config does not change
f933216 Revert "Assign QCA vendor command and attribute for Tx/Rx aggregation"
bde9a4e Comment out UDP/UNIX socket code from common ctrl_iface based on build
a6fbff2 Fix CONFIG_CTRL_IFACE=udp6/udp6-remote builds
0741c48 SAE: Check SHA256-PRF operation result
ea86a34 SAE: Remove dead code in FFC pwd-value derivation
87faf1f nl80211: Fix libnl-tiny build with CONFIG_LIBNL20=y
31afdd2 Use TIOCOUTQ instead of SIOCOUTQ to avoid need for linux/sockios.h
6d07e76 wlantest: Use local ETH_P_IP define instead of linux/if_ether.h
795abc8 Drop USE_KERNEL_HEADERS define
9b7cd57 Use a separate header file for Linux bridge interface definitions
c815fab Use own header file for defining Linux VLAN kernel interface
81606ab vlan: Fix musl libc conflict with Linux kernel headers
f347429 P2P: Fix persistent group for 60 GHz networks
e868599 vlan: Move if_nametoindex() use out of vlan_init.c
7c03c08 vlan: Move ifconfig helpers to a separate file
59d6390 vlan: Move CONFIG_FULL_DYNAMIC_VLAN functionality into a separate file
0fe28dd vlan: Remove unnecessary header includes from netlink implementation
84d6755 vlan: Clean up netlink vs. ioctl API implementation
cb38bc8 vlan: Fix musl build error
954e10e Make it a bit easier to roam from 2.4 GHz to 5 GHz within ESS
585141b Fix a typo in a comment
1126c07 nl80211: Ignore deauth/disassoc event during Connect reassociation
6a5ee81 Include previous BSSID in connection request to indicate reassociation
00c3c4a nl80211: Add NL80211_ATTR_PREV_BSSID with Connect command
cbc3d6f WNM: Verify BSS TM target match against the current network profile
8854f90 mesh: Simplify wpa_auth_pmksa_set_to_sm()
32d4fe9 privsep: Fix a compiler warning on unsigned/signed comparison
2e997ee Add interface matching support with -M, guarded by CONFIG_MATCH_IFACE
45e3fc7 Find correct driver for interface additions/removals
9037702 wpa_supplicant: Fix CONFIG_IBSS_RSN=y build without CONFIG_AP=y
5ae65de wpa_supplicant: Fix p2p_group_add when UDP-based ctrl_iface is used
24bce46 FST: Fix a compiler warning
e567c58 Fix nfc_pw_token build with CONFIG_FST=y
d774c46 mesh: Use appropriate BLOCKED state duration
9f2cf23 mesh: Add support for PMKSA caching
4c522c7 PMKSA: Flush AP/mesh PMKSA cache by PMKSA_FLUSH command
b8daac1 PMKSA: Show AP/mesh PMKSA list in PMKSA command
2604edb mesh: Add MESH_PEER_ADD command
e174ef3 mesh: Add MESH_PEER_REMOVE command
f7648c8 P2P: Advertise IP Address Allocation only if it is enabled on GO
7f46ad9 BSD: Only down the interface once we are sure we can work with it
192964d Handle OSEN IE in Assoc Request info if req_ies exists
29eddc3 nl80211: Fix error path in if_indices_reason reallocation
ee298f1 nl80211: Do not add NL80211_ATTR_SMPS_MODE attribute if HT is disabled
4ca16b5 Assign QCA vendor command and attribute for Tx/Rx aggregation
64ce590 libxml2: Check for xmlDocDumpFormatMemory() error case
8b827c3 BoringSSL: Keep static analyzers happier with X509_get0_pubkey_bitstr()
42a9553 hs20-osu-client: Fix pol_upd command line parsing
ec1eae8 hs20-osu-client: Remove dead code from sub_rem command line parsing
c3dc68e Do not invalidate EAP session cache on all network block parameter changes
9231c24 wlantest: Fix bip_protect() memory allocation
c6c29be Interworking: Add credential realm to EAP-TLS identity

Change-Id: I870f325171d00fed9c4fcd82a695fe5e2efee792
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 96500b6..67ca129 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -175,12 +175,18 @@
 L_CFLAGS += -DCONFIG_NO_VLAN
 else
 OBJS += src/ap/vlan_init.c
+OBJS += src/ap/vlan_ifconfig.c
 OBJS += src/ap/vlan.c
-ifdef CONFIG_VLAN_NETLINK
 ifdef CONFIG_FULL_DYNAMIC_VLAN
+# Define CONFIG_FULL_DYNAMIC_VLAN to have hostapd manipulate bridges
+# and VLAN interfaces for the VLAN feature.
+L_CFLAGS += -DCONFIG_FULL_DYNAMIC_VLAN
+OBJS += src/ap/vlan_full.c
+ifdef CONFIG_VLAN_NETLINK
 OBJS += src/ap/vlan_util.c
+else
+OBJS += src/ap/vlan_ioctl.c
 endif
-L_CFLAGS += -DCONFIG_VLAN_NETLINK
 endif
 endif
 
@@ -869,12 +875,6 @@
 L_CFLAGS += -DCONFIG_DRIVER_RADIUS_ACL
 endif
 
-ifdef CONFIG_FULL_DYNAMIC_VLAN
-# define CONFIG_FULL_DYNAMIC_VLAN to have hostapd manipulate bridges
-# and vlan interfaces for the vlan feature.
-L_CFLAGS += -DCONFIG_FULL_DYNAMIC_VLAN
-endif
-
 ifdef NEED_BASE64
 OBJS += src/utils/base64.c
 endif
diff --git a/hostapd/Makefile b/hostapd/Makefile
index e0d25a5..fa4af82 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -27,10 +27,6 @@
 
 export BINDIR ?= /usr/local/bin/
 
-# Uncomment following line and set the path to your kernel tree include
-# directory if your C library does not include all header files.
-# CFLAGS += -DUSE_KERNEL_HEADERS -I/usr/src/linux/include
-
 -include .config
 
 ifndef CONFIG_NO_GITVER
@@ -194,12 +190,18 @@
 CFLAGS += -DCONFIG_NO_VLAN
 else
 OBJS += ../src/ap/vlan_init.o
+OBJS += ../src/ap/vlan_ifconfig.o
 OBJS += ../src/ap/vlan.o
-ifdef CONFIG_VLAN_NETLINK
 ifdef CONFIG_FULL_DYNAMIC_VLAN
+# Define CONFIG_FULL_DYNAMIC_VLAN to have hostapd manipulate bridges
+# and VLAN interfaces for the VLAN feature.
+CFLAGS += -DCONFIG_FULL_DYNAMIC_VLAN
+OBJS += ../src/ap/vlan_full.o
+ifdef CONFIG_VLAN_NETLINK
 OBJS += ../src/ap/vlan_util.o
+else
+OBJS += ../src/ap/vlan_ioctl.o
 endif
-CFLAGS += -DCONFIG_VLAN_NETLINK
 endif
 endif
 
@@ -907,12 +909,6 @@
 CFLAGS += -DCONFIG_DRIVER_RADIUS_ACL
 endif
 
-ifdef CONFIG_FULL_DYNAMIC_VLAN
-# define CONFIG_FULL_DYNAMIC_VLAN to have hostapd manipulate bridges
-# and vlan interfaces for the vlan feature.
-CFLAGS += -DCONFIG_FULL_DYNAMIC_VLAN
-endif
-
 ifdef NEED_BASE64
 OBJS += ../src/utils/base64.o
 endif
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 6f525d9..c35d5ae 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -3468,7 +3468,8 @@
 			return -1;
 		}
 		val = strtol(pos, &endp, 0);
-		if (*endp || val < 1 || val > FST_MAX_LLT_MS) {
+		if (*endp || val < 1 ||
+		    (unsigned long int) val > FST_MAX_LLT_MS) {
 			wpa_printf(MSG_ERROR,
 				   "Line %d: Invalid fst_llt %ld (%s) (expected 1..%u)",
 				   line, val, pos, FST_MAX_LLT_MS);
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 33819ab..a87f117 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -2307,6 +2307,11 @@
 		reply_len = hostapd_ctrl_iface_track_sta_list(
 			hapd, reply, reply_size);
 #endif /* NEED_AP_MLME */
+	} else if (os_strcmp(buf, "PMKSA") == 0) {
+		reply_len = hostapd_ctrl_iface_pmksa_list(hapd, reply,
+							  reply_size);
+	} else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
+		hostapd_ctrl_iface_pmksa_flush(hapd);
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index fc8a72e..bf86d37 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -1130,6 +1130,19 @@
 }
 
 
+static int hostapd_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "PMKSA");
+}
+
+
+static int hostapd_cli_cmd_pmksa_flush(struct wpa_ctrl *ctrl, int argc,
+				       char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "PMKSA_FLUSH");
+}
+
+
 struct hostapd_cli_cmd {
 	const char *cmd;
 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
@@ -1189,6 +1202,8 @@
 	{ "disable", hostapd_cli_cmd_disable },
 	{ "erp_flush", hostapd_cli_cmd_erp_flush },
 	{ "log_level", hostapd_cli_cmd_log_level },
+	{ "pmksa", hostapd_cli_cmd_pmksa },
+	{ "pmksa_flush", hostapd_cli_cmd_pmksa_flush },
 	{ NULL, NULL }
 };
 
diff --git a/hostapd/main.c b/hostapd/main.c
index 25dc20b..1d9e63e 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -171,7 +171,8 @@
 
 		if (global.drv_priv[i] == NULL &&
 		    wpa_drivers[i]->global_init) {
-			global.drv_priv[i] = wpa_drivers[i]->global_init();
+			global.drv_priv[i] =
+				wpa_drivers[i]->global_init(iface->interfaces);
 			if (global.drv_priv[i] == NULL) {
 				wpa_printf(MSG_ERROR, "Failed to initialize "
 					   "driver '%s'",
diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c
index 2907c64..c05c57d 100644
--- a/hs20/client/osu_client.c
+++ b/hs20/client/osu_client.c
@@ -2570,7 +2570,7 @@
 	if (!pps_fname) {
 		char buf[256];
 		wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information");
-		if (os_strncmp(address, "fqdn=", 5) == 0) {
+		if (address && os_strncmp(address, "fqdn=", 5) == 0) {
 			wpa_printf(MSG_INFO, "Use requested FQDN from command line");
 			os_snprintf(buf, sizeof(buf), "%s", address + 5);
 			address = NULL;
@@ -3133,20 +3133,12 @@
 			usage();
 			exit(0);
 		}
-		if (argc - optind < 2)
-			wpa_printf(MSG_ERROR, "Server URL missing from command line");
-		else
-			ret = cmd_sub_rem(&ctx, argv[optind + 1],
-					  argc > optind + 2 ?
-					  argv[optind + 2] : NULL,
-					  argc > optind + 3 ?
-					  argv[optind + 3] : NULL);
+		ret = cmd_sub_rem(&ctx, argv[optind + 1],
+				  argc > optind + 2 ? argv[optind + 2] : NULL,
+				  argc > optind + 3 ? argv[optind + 3] : NULL);
 	} else if (strcmp(argv[optind], "pol_upd") == 0) {
-		if (argc - optind < 2) {
-			usage();
-			exit(0);
-		}
-		ret = cmd_pol_upd(&ctx, argc > 2 ? argv[optind + 1] : NULL,
+		ret = cmd_pol_upd(&ctx,
+				  argc > optind + 1 ? argv[optind + 1] : NULL,
 				  argc > optind + 2 ? argv[optind + 2] : NULL,
 				  argc > optind + 3 ? argv[optind + 3] : NULL);
 	} else if (strcmp(argv[optind], "prov") == 0) {
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 0720e14..19bff7b 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -685,12 +685,12 @@
 	u16 csa_offs[2];
 	size_t csa_offs_len;
 
-	ie = mgmt->u.probe_req.variable;
-	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
+	if (len < IEEE80211_HDRLEN)
 		return;
+	ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN;
 	if (hapd->iconf->track_sta_max_num)
 		sta_track_add(hapd->iface, mgmt->sa);
-	ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
+	ie_len = len - IEEE80211_HDRLEN;
 
 	for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
 		if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 9f249d7..317b238 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -568,3 +568,16 @@
 {
 	return hostapd_drv_stop_ap(hapd);
 }
+
+
+int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
+				  size_t len)
+{
+	return wpa_auth_pmksa_list(hapd->wpa_auth, buf, len);
+}
+
+
+void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd)
+{
+	wpa_auth_pmksa_flush(hapd->wpa_auth);
+}
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
index e5297d0..3ad622f 100644
--- a/src/ap/ctrl_iface_ap.h
+++ b/src/ap/ctrl_iface_ap.h
@@ -24,5 +24,8 @@
 int hostapd_parse_csa_settings(const char *pos,
 			       struct csa_settings *settings);
 int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd);
+int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
+				  size_t len);
+void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd);
 
 #endif /* CTRL_IFACE_AP_H */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index f54d1ad..3ab5bf3 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -1353,4 +1353,31 @@
 	}
 }
 
+
+void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
+				 union wpa_event_data *data)
+{
+	struct hapd_interfaces *interfaces = ctx;
+	struct hostapd_data *hapd;
+
+	if (event != EVENT_INTERFACE_STATUS)
+		return;
+
+	hapd = hostapd_get_iface(interfaces, data->interface_status.ifname);
+	if (hapd && hapd->driver && hapd->driver->get_ifindex &&
+	    hapd->drv_priv) {
+		unsigned int ifindex;
+
+		ifindex = hapd->driver->get_ifindex(hapd->drv_priv);
+		if (ifindex != data->interface_status.ifindex) {
+			wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+				"interface status ifindex %d mismatch (%d)",
+				ifindex, data->interface_status.ifindex);
+			return;
+		}
+	}
+	if (hapd)
+		wpa_supplicant_event(hapd, event, data);
+}
+
 #endif /* HOSTAPD */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 55ca9e8..ee80f4f 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -3090,6 +3090,8 @@
 	hostapd_enable_iface(iface);
 }
 
+#endif /* NEED_AP_MLME */
+
 
 struct hostapd_data * hostapd_get_iface(struct hapd_interfaces *interfaces,
 					const char *ifname)
@@ -3110,8 +3112,6 @@
 	return NULL;
 }
 
-#endif /* NEED_AP_MLME */
-
 
 void hostapd_periodic_iface(struct hostapd_iface *iface)
 {
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 60516ae..75a7c04 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -262,6 +262,7 @@
 				 struct sta_info *sta);
 	struct wpabuf *mesh_pending_auth;
 	struct os_reltime mesh_pending_auth_time;
+	u8 mesh_required_peer[ETH_ALEN];
 #endif /* CONFIG_MESH */
 
 #ifdef CONFIG_SQLITE
diff --git a/src/ap/iapp.c b/src/ap/iapp.c
index 99aa04d..07672ce 100644
--- a/src/ap/iapp.c
+++ b/src/ap/iapp.c
@@ -34,11 +34,7 @@
 #include "utils/includes.h"
 #include <net/if.h>
 #include <sys/ioctl.h>
-#ifdef USE_KERNEL_HEADERS
-#include <linux/if_packet.h>
-#else /* USE_KERNEL_HEADERS */
 #include <netpacket/packet.h>
-#endif /* USE_KERNEL_HEADERS */
 
 #include "utils/common.h"
 #include "utils/eloop.h"
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 134ae06..6a373c5 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -554,6 +554,18 @@
 }
 
 
+void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	sta->flags |= WLAN_STA_AUTH;
+	sta->auth_alg = WLAN_AUTH_SAE;
+	mlme_authenticate_indication(hapd, sta);
+	wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+	sta->sae->state = SAE_ACCEPTED;
+	wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
+			       sta->sae->pmk, sta->sae->pmkid);
+}
+
+
 static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
 		       const u8 *bssid, u8 auth_transaction)
 {
@@ -676,13 +688,7 @@
 
 			sae_set_retransmit_timer(hapd, sta);
 		} else {
-			sta->flags |= WLAN_STA_AUTH;
-			sta->auth_alg = WLAN_AUTH_SAE;
-			mlme_authenticate_indication(hapd, sta);
-			wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
-			sta->sae->state = SAE_ACCEPTED;
-			wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
-					       sta->sae->pmk, sta->sae->pmkid);
+			sae_accept_sta(hapd, sta);
 		}
 		break;
 	case SAE_ACCEPTED:
@@ -691,6 +697,7 @@
 				   ") doing reauthentication",
 				   MAC2STR(sta->addr));
 			ap_free_sta(hapd, sta);
+			wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
 		} else {
 			if (sae_check_big_sync(sta))
 				return WLAN_STATUS_SUCCESS;
@@ -733,6 +740,13 @@
 		sta->sae->sync = 0;
 	}
 
+	if (sta->mesh_sae_pmksa_caching) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE: Cancel use of mesh PMKSA caching because peer starts SAE authentication");
+		wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+		sta->mesh_sae_pmksa_caching = 0;
+	}
+
 	if (auth_transaction == 1) {
 		const u8 *token = NULL, *pos, *end;
 		size_t token_len = 0;
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 1a96b33..71b3b49 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -102,6 +102,7 @@
 #ifdef CONFIG_SAE
 void sae_clear_retransmit_timer(struct hostapd_data *hapd,
 				struct sta_info *sta);
+void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta);
 #else /* CONFIG_SAE */
 static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd,
 					      struct sta_info *sta)
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index 30be30c..d610e7e 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -92,6 +92,20 @@
 }
 
 
+/**
+ * pmksa_cache_auth_flush - Flush all PMKSA cache entries
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ */
+void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa)
+{
+	while (pmksa->pmksa) {
+		wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry for "
+			   MACSTR, MAC2STR(pmksa->pmksa->spa));
+		pmksa_cache_free_entry(pmksa, pmksa->pmksa);
+	}
+}
+
+
 static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
 {
 	struct rsn_pmksa_cache *pmksa = eloop_ctx;
@@ -167,8 +181,6 @@
 			       struct rsn_pmksa_cache_entry *entry,
 			       struct eapol_state_machine *eapol)
 {
-	struct sta_info *sta;
-
 	if (entry == NULL || eapol == NULL)
 		return;
 
@@ -199,8 +211,9 @@
 	}
 
 	eapol->eap_type_authsrv = entry->eap_type_authsrv;
-	sta = (struct sta_info *) eapol->sta;
-	ap_sta_set_vlan(hapd, sta, entry->vlan_desc);
+#ifndef CONFIG_NO_VLAN
+	ap_sta_set_vlan(hapd, eapol->sta, entry->vlan_desc);
+#endif /* CONFIG_NO_VLAN */
 
 	eapol->acct_multi_session_id = entry->acct_multi_session_id;
 }
@@ -547,3 +560,48 @@
 
 	return found ? 0 : -1;
 }
+
+
+/**
+ * pmksa_cache_auth_list - Dump text list of entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @buf: Buffer for the list
+ * @len: Length of the buffer
+ * Returns: Number of bytes written to buffer
+ *
+ * This function is used to generate a text format representation of the
+ * current PMKSA cache contents for the ctrl_iface PMKSA command.
+ */
+int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
+{
+	int i, ret;
+	char *pos = buf;
+	struct rsn_pmksa_cache_entry *entry;
+	struct os_reltime now;
+
+	os_get_reltime(&now);
+	ret = os_snprintf(pos, buf + len - pos,
+			  "Index / SPA / PMKID / expiration (in seconds) / opportunistic\n");
+	if (os_snprintf_error(buf + len - pos, ret))
+		return pos - buf;
+	pos += ret;
+	i = 0;
+	entry = pmksa->pmksa;
+	while (entry) {
+		ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
+				  i, MAC2STR(entry->spa));
+		if (os_snprintf_error(buf + len - pos, ret))
+			return pos - buf;
+		pos += ret;
+		pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
+					PMKID_LEN);
+		ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
+				  (int) (entry->expiration - now.sec),
+				  entry->opportunistic);
+		if (os_snprintf_error(buf + len - pos, ret))
+			return pos - buf;
+		pos += ret;
+		entry = entry->next;
+	}
+	return pos - buf;
+}
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index 3e60277..d8d9c5a 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -63,5 +63,7 @@
 			    struct rsn_pmksa_cache_entry *entry);
 int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
 					   struct radius_das_attrs *attr);
+int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
+void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa);
 
 #endif /* PMKSA_CACHE_H */
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 70a59cc..3d9a928 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -183,6 +183,7 @@
 
 #ifdef CONFIG_SAE
 	struct sae_data *sae;
+	unsigned int mesh_sae_pmksa_caching:1;
 #endif /* CONFIG_SAE */
 
 	u32 session_timeout; /* valid only if session_timeout_set == 1 */
diff --git a/src/ap/vlan_full.c b/src/ap/vlan_full.c
new file mode 100644
index 0000000..aa42335
--- /dev/null
+++ b/src/ap/vlan_full.c
@@ -0,0 +1,752 @@
+/*
+ * hostapd / VLAN initialization - full dynamic VLAN
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <net/if.h>
+/* Avoid conflicts due to NetBSD net/if.h if_type define with driver.h */
+#undef if_type
+#include <sys/ioctl.h>
+
+#include "utils/common.h"
+#include "drivers/priv_netlink.h"
+#include "common/linux_bridge.h"
+#include "common/linux_vlan.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "wpa_auth.h"
+#include "vlan_init.h"
+#include "vlan_util.h"
+
+
+struct full_dynamic_vlan {
+	int s; /* socket on which to listen for new/removed interfaces. */
+};
+
+#define DVLAN_CLEAN_BR         0x1
+#define DVLAN_CLEAN_VLAN       0x2
+#define DVLAN_CLEAN_VLAN_PORT  0x4
+
+struct dynamic_iface {
+	char ifname[IFNAMSIZ + 1];
+	int usage;
+	int clean;
+	struct dynamic_iface *next;
+};
+
+
+/* Increment ref counter for ifname and add clean flag.
+ * If not in list, add it only if some flags are given.
+ */
+static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname,
+			  int clean)
+{
+	struct dynamic_iface *next, **dynamic_ifaces;
+	struct hapd_interfaces *interfaces;
+
+	interfaces = hapd->iface->interfaces;
+	dynamic_ifaces = &interfaces->vlan_priv;
+
+	for (next = *dynamic_ifaces; next; next = next->next) {
+		if (os_strcmp(ifname, next->ifname) == 0)
+			break;
+	}
+
+	if (next) {
+		next->usage++;
+		next->clean |= clean;
+		return;
+	}
+
+	if (!clean)
+		return;
+
+	next = os_zalloc(sizeof(*next));
+	if (!next)
+		return;
+	os_strlcpy(next->ifname, ifname, sizeof(next->ifname));
+	next->usage = 1;
+	next->clean = clean;
+	next->next = *dynamic_ifaces;
+	*dynamic_ifaces = next;
+}
+
+
+/* Decrement reference counter for given ifname.
+ * Return clean flag iff reference counter was decreased to zero, else zero
+ */
+static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname)
+{
+	struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces;
+	struct hapd_interfaces *interfaces;
+	int clean;
+
+	interfaces = hapd->iface->interfaces;
+	dynamic_ifaces = &interfaces->vlan_priv;
+
+	for (next = *dynamic_ifaces; next; next = next->next) {
+		if (os_strcmp(ifname, next->ifname) == 0)
+			break;
+		prev = next;
+	}
+
+	if (!next)
+		return 0;
+
+	next->usage--;
+	if (next->usage)
+		return 0;
+
+	if (prev)
+		prev->next = next->next;
+	else
+		*dynamic_ifaces = next->next;
+	clean = next->clean;
+	os_free(next);
+
+	return clean;
+}
+
+
+static int ifconfig_down(const char *if_name)
+{
+	wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
+	return ifconfig_helper(if_name, 0);
+}
+
+
+/* This value should be 256 ONLY. If it is something else, then hostapd
+ * might crash!, as this value has been hard-coded in 2.4.x kernel
+ * bridging code.
+ */
+#define MAX_BR_PORTS      		256
+
+static int br_delif(const char *br_name, const char *if_name)
+{
+	int fd;
+	struct ifreq ifr;
+	unsigned long args[2];
+	int if_index;
+
+	wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	if_index = if_nametoindex(if_name);
+
+	if (if_index == 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
+			   "interface index for '%s'",
+			   __func__, if_name);
+		close(fd);
+		return -1;
+	}
+
+	args[0] = BRCTL_DEL_IF;
+	args[1] = if_index;
+
+	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (void *) args;
+
+	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) {
+		/* No error if interface already removed. */
+		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
+			   "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: "
+			   "%s", __func__, br_name, if_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+/*
+	Add interface 'if_name' to the bridge 'br_name'
+
+	returns -1 on error
+	returns 1 if the interface is already part of the bridge
+	returns 0 otherwise
+*/
+static int br_addif(const char *br_name, const char *if_name)
+{
+	int fd;
+	struct ifreq ifr;
+	unsigned long args[2];
+	int if_index;
+
+	wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	if_index = if_nametoindex(if_name);
+
+	if (if_index == 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
+			   "interface index for '%s'",
+			   __func__, if_name);
+		close(fd);
+		return -1;
+	}
+
+	args[0] = BRCTL_ADD_IF;
+	args[1] = if_index;
+
+	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (void *) args;
+
+	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+		if (errno == EBUSY) {
+			/* The interface is already added. */
+			close(fd);
+			return 1;
+		}
+
+		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
+			   "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: "
+			   "%s", __func__, br_name, if_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+static int br_delbr(const char *br_name)
+{
+	int fd;
+	unsigned long arg[2];
+
+	wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	arg[0] = BRCTL_DEL_BRIDGE;
+	arg[1] = (unsigned long) br_name;
+
+	if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) {
+		/* No error if bridge already removed. */
+		wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for "
+			   "%s: %s", __func__, br_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+/*
+	Add a bridge with the name 'br_name'.
+
+	returns -1 on error
+	returns 1 if the bridge already exists
+	returns 0 otherwise
+*/
+static int br_addbr(const char *br_name)
+{
+	int fd;
+	unsigned long arg[4];
+	struct ifreq ifr;
+
+	wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	arg[0] = BRCTL_ADD_BRIDGE;
+	arg[1] = (unsigned long) br_name;
+
+	if (ioctl(fd, SIOCGIFBR, arg) < 0) {
+ 		if (errno == EEXIST) {
+			/* The bridge is already added. */
+			close(fd);
+			return 1;
+		} else {
+			wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE "
+				   "failed for %s: %s",
+				   __func__, br_name, strerror(errno));
+			close(fd);
+			return -1;
+		}
+	}
+
+	/* Decrease forwarding delay to avoid EAPOL timeouts. */
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ);
+	arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
+	arg[1] = 1;
+	arg[2] = 0;
+	arg[3] = 0;
+	ifr.ifr_data = (char *) &arg;
+	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: "
+			   "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for "
+			   "%s: %s", __func__, br_name, strerror(errno));
+		/* Continue anyway */
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+static int br_getnumports(const char *br_name)
+{
+	int fd;
+	int i;
+	int port_cnt = 0;
+	unsigned long arg[4];
+	int ifindices[MAX_BR_PORTS];
+	struct ifreq ifr;
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	arg[0] = BRCTL_GET_PORT_LIST;
+	arg[1] = (unsigned long) ifindices;
+	arg[2] = MAX_BR_PORTS;
+	arg[3] = 0;
+
+	os_memset(ifindices, 0, sizeof(ifindices));
+	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+	ifr.ifr_data = (void *) arg;
+
+	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST "
+			   "failed for %s: %s",
+			   __func__, br_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	for (i = 1; i < MAX_BR_PORTS; i++) {
+		if (ifindices[i] > 0) {
+			port_cnt++;
+		}
+	}
+
+	close(fd);
+	return port_cnt;
+}
+
+
+static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface,
+				const char *br_name, int vid,
+				struct hostapd_data *hapd)
+{
+	char vlan_ifname[IFNAMSIZ];
+	int clean;
+
+	if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
+		os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
+			    tagged_interface, vid);
+	else
+		os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid);
+
+	clean = 0;
+	ifconfig_up(tagged_interface);
+	if (!vlan_add(tagged_interface, vid, vlan_ifname))
+		clean |= DVLAN_CLEAN_VLAN;
+
+	if (!br_addif(br_name, vlan_ifname))
+		clean |= DVLAN_CLEAN_VLAN_PORT;
+
+	dyn_iface_get(hapd, vlan_ifname, clean);
+
+	ifconfig_up(vlan_ifname);
+}
+
+
+static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd, int vid)
+{
+	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+
+	if (hapd->conf->vlan_bridge[0]) {
+		os_snprintf(br_name, IFNAMSIZ, "%s%d",
+			    hapd->conf->vlan_bridge, vid);
+	} else if (tagged_interface) {
+		os_snprintf(br_name, IFNAMSIZ, "br%s.%d",
+			    tagged_interface, vid);
+	} else {
+		os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid);
+	}
+}
+
+
+static void vlan_get_bridge(const char *br_name, struct hostapd_data *hapd,
+			    int vid)
+{
+	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+	int vlan_naming = hapd->conf->ssid.vlan_naming;
+
+	dyn_iface_get(hapd, br_name, br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR);
+
+	ifconfig_up(br_name);
+
+	if (tagged_interface)
+		vlan_newlink_tagged(vlan_naming, tagged_interface, br_name,
+				    vid, hapd);
+}
+
+
+void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
+{
+	char br_name[IFNAMSIZ];
+	struct hostapd_vlan *vlan;
+	int untagged, *tagged, i, notempty;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
+
+	for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+		if (vlan->configured ||
+		    os_strcmp(ifname, vlan->ifname) != 0)
+			continue;
+		break;
+	}
+	if (!vlan)
+		return;
+
+	vlan->configured = 1;
+
+	notempty = vlan->vlan_desc.notempty;
+	untagged = vlan->vlan_desc.untagged;
+	tagged = vlan->vlan_desc.tagged;
+
+	if (!notempty) {
+		/* Non-VLAN STA */
+		if (hapd->conf->bridge[0] &&
+		    !br_addif(hapd->conf->bridge, ifname))
+			vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
+	} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
+		vlan_bridge_name(br_name, hapd, untagged);
+
+		vlan_get_bridge(br_name, hapd, untagged);
+
+		if (!br_addif(br_name, ifname))
+			vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
+	}
+
+	for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
+		if (tagged[i] == untagged ||
+		    tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
+		    (i > 0 && tagged[i] == tagged[i - 1]))
+			continue;
+		vlan_bridge_name(br_name, hapd, tagged[i]);
+		vlan_get_bridge(br_name, hapd, tagged[i]);
+		vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
+				    ifname, br_name, tagged[i], hapd);
+	}
+
+	ifconfig_up(ifname);
+}
+
+
+static void vlan_dellink_tagged(int vlan_naming, const char *tagged_interface,
+				const char *br_name, int vid,
+				struct hostapd_data *hapd)
+{
+	char vlan_ifname[IFNAMSIZ];
+	int clean;
+
+	if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
+		os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
+			    tagged_interface, vid);
+	else
+		os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid);
+
+	clean = dyn_iface_put(hapd, vlan_ifname);
+
+	if (clean & DVLAN_CLEAN_VLAN_PORT)
+		br_delif(br_name, vlan_ifname);
+
+	if (clean & DVLAN_CLEAN_VLAN) {
+		ifconfig_down(vlan_ifname);
+		vlan_rem(vlan_ifname);
+	}
+}
+
+
+static void vlan_put_bridge(const char *br_name, struct hostapd_data *hapd,
+			    int vid)
+{
+	int clean;
+	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+	int vlan_naming = hapd->conf->ssid.vlan_naming;
+
+	if (tagged_interface)
+		vlan_dellink_tagged(vlan_naming, tagged_interface, br_name,
+				    vid, hapd);
+
+	clean = dyn_iface_put(hapd, br_name);
+	if ((clean & DVLAN_CLEAN_BR) && br_getnumports(br_name) == 0) {
+		ifconfig_down(br_name);
+		br_delbr(br_name);
+	}
+}
+
+
+void vlan_dellink(const char *ifname, struct hostapd_data *hapd)
+{
+	struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
+
+	first = prev = vlan;
+
+	while (vlan) {
+		if (os_strcmp(ifname, vlan->ifname) != 0) {
+			prev = vlan;
+			vlan = vlan->next;
+			continue;
+		}
+		break;
+	}
+	if (!vlan)
+		return;
+
+	if (vlan->configured) {
+		int notempty = vlan->vlan_desc.notempty;
+		int untagged = vlan->vlan_desc.untagged;
+		int *tagged = vlan->vlan_desc.tagged;
+		char br_name[IFNAMSIZ];
+		int i;
+
+		for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
+			if (tagged[i] == untagged ||
+			    tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
+			    (i > 0 && tagged[i] == tagged[i - 1]))
+				continue;
+			vlan_bridge_name(br_name, hapd, tagged[i]);
+			vlan_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
+					    ifname, br_name, tagged[i], hapd);
+			vlan_put_bridge(br_name, hapd, tagged[i]);
+		}
+
+		if (!notempty) {
+			/* Non-VLAN STA */
+			if (hapd->conf->bridge[0] &&
+			    (vlan->clean & DVLAN_CLEAN_WLAN_PORT))
+				br_delif(hapd->conf->bridge, ifname);
+		} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
+			vlan_bridge_name(br_name, hapd, untagged);
+
+			if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
+				br_delif(br_name, vlan->ifname);
+
+			vlan_put_bridge(br_name, hapd, untagged);
+		}
+	}
+
+	/*
+	 * Ensure this VLAN interface is actually removed even if
+	 * NEWLINK message is only received later.
+	 */
+	if (if_nametoindex(vlan->ifname) && vlan_if_remove(hapd, vlan))
+		wpa_printf(MSG_ERROR,
+			   "VLAN: Could not remove VLAN iface: %s: %s",
+			   vlan->ifname, strerror(errno));
+
+	if (vlan == first)
+		hapd->conf->vlan = vlan->next;
+	else
+		prev->next = vlan->next;
+
+	os_free(vlan);
+}
+
+
+static void
+vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
+		  struct hostapd_data *hapd)
+{
+	struct ifinfomsg *ifi;
+	int attrlen, nlmsg_len, rta_len;
+	struct rtattr *attr;
+	char ifname[IFNAMSIZ + 1];
+
+	if (len < sizeof(*ifi))
+		return;
+
+	ifi = NLMSG_DATA(h);
+
+	nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
+
+	attrlen = h->nlmsg_len - nlmsg_len;
+	if (attrlen < 0)
+		return;
+
+	attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
+
+	os_memset(ifname, 0, sizeof(ifname));
+	rta_len = RTA_ALIGN(sizeof(struct rtattr));
+	while (RTA_OK(attr, attrlen)) {
+		if (attr->rta_type == IFLA_IFNAME) {
+			int n = attr->rta_len - rta_len;
+			if (n < 0)
+				break;
+
+			if ((size_t) n >= sizeof(ifname))
+				n = sizeof(ifname) - 1;
+			os_memcpy(ifname, ((char *) attr) + rta_len, n);
+
+		}
+
+		attr = RTA_NEXT(attr, attrlen);
+	}
+
+	if (!ifname[0])
+		return;
+	if (del && if_nametoindex(ifname)) {
+		 /* interface still exists, race condition ->
+		  * iface has just been recreated */
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
+		   del ? "DEL" : "NEW",
+		   ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags,
+		   (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
+		   (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
+		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
+		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+	if (del)
+		vlan_dellink(ifname, hapd);
+	else
+		vlan_newlink(ifname, hapd);
+}
+
+
+static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	char buf[8192];
+	int left;
+	struct sockaddr_nl from;
+	socklen_t fromlen;
+	struct nlmsghdr *h;
+	struct hostapd_data *hapd = eloop_ctx;
+
+	fromlen = sizeof(from);
+	left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
+			(struct sockaddr *) &from, &fromlen);
+	if (left < 0) {
+		if (errno != EINTR && errno != EAGAIN)
+			wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s",
+				   __func__, strerror(errno));
+		return;
+	}
+
+	h = (struct nlmsghdr *) buf;
+	while (NLMSG_OK(h, left)) {
+		int len, plen;
+
+		len = h->nlmsg_len;
+		plen = len - sizeof(*h);
+		if (len > left || plen < 0) {
+			wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink "
+				   "message: len=%d left=%d plen=%d",
+				   len, left, plen);
+			break;
+		}
+
+		switch (h->nlmsg_type) {
+		case RTM_NEWLINK:
+			vlan_read_ifnames(h, plen, 0, hapd);
+			break;
+		case RTM_DELLINK:
+			vlan_read_ifnames(h, plen, 1, hapd);
+			break;
+		}
+
+		h = NLMSG_NEXT(h, left);
+	}
+
+	if (left > 0) {
+		wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of "
+			   "netlink message", __func__, left);
+	}
+}
+
+
+struct full_dynamic_vlan *
+full_dynamic_vlan_init(struct hostapd_data *hapd)
+{
+	struct sockaddr_nl local;
+	struct full_dynamic_vlan *priv;
+
+	priv = os_zalloc(sizeof(*priv));
+	if (priv == NULL)
+		return NULL;
+
+	vlan_set_name_type(hapd->conf->ssid.vlan_naming ==
+			   DYNAMIC_VLAN_NAMING_WITH_DEVICE ?
+			   VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD :
+			   VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
+
+	priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (priv->s < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW,"
+			   "NETLINK_ROUTE) failed: %s",
+			   __func__, strerror(errno));
+		os_free(priv);
+		return NULL;
+	}
+
+	os_memset(&local, 0, sizeof(local));
+	local.nl_family = AF_NETLINK;
+	local.nl_groups = RTMGRP_LINK;
+	if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s",
+			   __func__, strerror(errno));
+		close(priv->s);
+		os_free(priv);
+		return NULL;
+	}
+
+	if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL))
+	{
+		close(priv->s);
+		os_free(priv);
+		return NULL;
+	}
+
+	return priv;
+}
+
+
+void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv)
+{
+	if (priv == NULL)
+		return;
+	eloop_unregister_read_sock(priv->s);
+	close(priv->s);
+	os_free(priv);
+}
diff --git a/src/ap/vlan_ifconfig.c b/src/ap/vlan_ifconfig.c
new file mode 100644
index 0000000..ef953a5
--- /dev/null
+++ b/src/ap/vlan_ifconfig.c
@@ -0,0 +1,69 @@
+/*
+ * hostapd / VLAN ifconfig helpers
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <net/if.h>
+#include <sys/ioctl.h>
+
+#include "utils/common.h"
+#include "vlan_util.h"
+
+
+int ifconfig_helper(const char *if_name, int up)
+{
+	int fd;
+	struct ifreq ifr;
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
+
+	if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed "
+			   "for interface %s: %s",
+			   __func__, if_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	if (up)
+		ifr.ifr_flags |= IFF_UP;
+	else
+		ifr.ifr_flags &= ~IFF_UP;
+
+	if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed "
+			   "for interface %s (up=%d): %s",
+			   __func__, if_name, up, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+int ifconfig_up(const char *if_name)
+{
+	wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name);
+	return ifconfig_helper(if_name, 1);
+}
+
+
+int iface_exists(const char *ifname)
+{
+	return if_nametoindex(ifname);
+}
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index 45d1a1c..31e4fc6 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -18,158 +18,6 @@
 #include "vlan_init.h"
 #include "vlan_util.h"
 
-#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>
-#endif /* CONFIG_FULL_DYNAMIC_VLAN */
-
-
-#ifdef CONFIG_FULL_DYNAMIC_VLAN
-
-#include "drivers/priv_netlink.h"
-#include "utils/eloop.h"
-
-
-struct full_dynamic_vlan {
-	int s; /* socket on which to listen for new/removed interfaces. */
-};
-
-#define DVLAN_CLEAN_BR         0x1
-#define DVLAN_CLEAN_VLAN       0x2
-#define DVLAN_CLEAN_VLAN_PORT  0x4
-
-struct dynamic_iface {
-	char ifname[IFNAMSIZ + 1];
-	int usage;
-	int clean;
-	struct dynamic_iface *next;
-};
-
-
-/* Increment ref counter for ifname and add clean flag.
- * If not in list, add it only if some flags are given.
- */
-static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname,
-			  int clean)
-{
-	struct dynamic_iface *next, **dynamic_ifaces;
-	struct hapd_interfaces *interfaces;
-
-	interfaces = hapd->iface->interfaces;
-	dynamic_ifaces = &interfaces->vlan_priv;
-
-	for (next = *dynamic_ifaces; next; next = next->next) {
-		if (os_strcmp(ifname, next->ifname) == 0)
-			break;
-	}
-
-	if (next) {
-		next->usage++;
-		next->clean |= clean;
-		return;
-	}
-
-	if (!clean)
-		return;
-
-	next = os_zalloc(sizeof(*next));
-	if (!next)
-		return;
-	os_strlcpy(next->ifname, ifname, sizeof(next->ifname));
-	next->usage = 1;
-	next->clean = clean;
-	next->next = *dynamic_ifaces;
-	*dynamic_ifaces = next;
-}
-
-
-/* Decrement reference counter for given ifname.
- * Return clean flag iff reference counter was decreased to zero, else zero
- */
-static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname)
-{
-	struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces;
-	struct hapd_interfaces *interfaces;
-	int clean;
-
-	interfaces = hapd->iface->interfaces;
-	dynamic_ifaces = &interfaces->vlan_priv;
-
-	for (next = *dynamic_ifaces; next; next = next->next) {
-		if (os_strcmp(ifname, next->ifname) == 0)
-			break;
-		prev = next;
-	}
-
-	if (!next)
-		return 0;
-
-	next->usage--;
-	if (next->usage)
-		return 0;
-
-	if (prev)
-		prev->next = next->next;
-	else
-		*dynamic_ifaces = next->next;
-	clean = next->clean;
-	os_free(next);
-
-	return clean;
-}
-
-#endif /* CONFIG_FULL_DYNAMIC_VLAN */
-
-
-static int ifconfig_helper(const char *if_name, int up)
-{
-	int fd;
-	struct ifreq ifr;
-
-	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
-			   "failed: %s", __func__, strerror(errno));
-		return -1;
-	}
-
-	os_memset(&ifr, 0, sizeof(ifr));
-	os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
-
-	if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed "
-			   "for interface %s: %s",
-			   __func__, if_name, strerror(errno));
-		close(fd);
-		return -1;
-	}
-
-	if (up)
-		ifr.ifr_flags |= IFF_UP;
-	else
-		ifr.ifr_flags &= ~IFF_UP;
-
-	if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed "
-			   "for interface %s (up=%d): %s",
-			   __func__, if_name, up, strerror(errno));
-		close(fd);
-		return -1;
-	}
-
-	close(fd);
-	return 0;
-}
-
-
-static int ifconfig_up(const char *if_name)
-{
-	wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name);
-	return ifconfig_helper(if_name, 1);
-}
-
 
 static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
 		       int existsok)
@@ -185,7 +33,7 @@
 		return -1;
 	}
 
-	if (!if_nametoindex(vlan->ifname))
+	if (!iface_exists(vlan->ifname))
 		ret = hostapd_vlan_if_add(hapd, vlan->ifname);
 	else if (!existsok)
 		return -1;
@@ -216,7 +64,7 @@
 }
 
 
-static int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
 {
 	int ret;
 
@@ -230,798 +78,6 @@
 }
 
 
-#ifdef CONFIG_FULL_DYNAMIC_VLAN
-
-static int ifconfig_down(const char *if_name)
-{
-	wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
-	return ifconfig_helper(if_name, 0);
-}
-
-
-/*
- * These are only available in recent linux headers (without the leading
- * underscore).
- */
-#define _GET_VLAN_REALDEV_NAME_CMD	8
-#define _GET_VLAN_VID_CMD		9
-
-/* This value should be 256 ONLY. If it is something else, then hostapd
- * might crash!, as this value has been hard-coded in 2.4.x kernel
- * bridging code.
- */
-#define MAX_BR_PORTS      		256
-
-static int br_delif(const char *br_name, const char *if_name)
-{
-	int fd;
-	struct ifreq ifr;
-	unsigned long args[2];
-	int if_index;
-
-	wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name);
-	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
-			   "failed: %s", __func__, strerror(errno));
-		return -1;
-	}
-
-	if_index = if_nametoindex(if_name);
-
-	if (if_index == 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
-			   "interface index for '%s'",
-			   __func__, if_name);
-		close(fd);
-		return -1;
-	}
-
-	args[0] = BRCTL_DEL_IF;
-	args[1] = if_index;
-
-	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
-	ifr.ifr_data = (__caddr_t) args;
-
-	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) {
-		/* No error if interface already removed. */
-		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
-			   "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: "
-			   "%s", __func__, br_name, if_name, strerror(errno));
-		close(fd);
-		return -1;
-	}
-
-	close(fd);
-	return 0;
-}
-
-
-/*
-	Add interface 'if_name' to the bridge 'br_name'
-
-	returns -1 on error
-	returns 1 if the interface is already part of the bridge
-	returns 0 otherwise
-*/
-static int br_addif(const char *br_name, const char *if_name)
-{
-	int fd;
-	struct ifreq ifr;
-	unsigned long args[2];
-	int if_index;
-
-	wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name);
-	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
-			   "failed: %s", __func__, strerror(errno));
-		return -1;
-	}
-
-	if_index = if_nametoindex(if_name);
-
-	if (if_index == 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
-			   "interface index for '%s'",
-			   __func__, if_name);
-		close(fd);
-		return -1;
-	}
-
-	args[0] = BRCTL_ADD_IF;
-	args[1] = if_index;
-
-	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
-	ifr.ifr_data = (__caddr_t) args;
-
-	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
-		if (errno == EBUSY) {
-			/* The interface is already added. */
-			close(fd);
-			return 1;
-		}
-
-		wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
-			   "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: "
-			   "%s", __func__, br_name, if_name, strerror(errno));
-		close(fd);
-		return -1;
-	}
-
-	close(fd);
-	return 0;
-}
-
-
-static int br_delbr(const char *br_name)
-{
-	int fd;
-	unsigned long arg[2];
-
-	wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name);
-	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
-			   "failed: %s", __func__, strerror(errno));
-		return -1;
-	}
-
-	arg[0] = BRCTL_DEL_BRIDGE;
-	arg[1] = (unsigned long) br_name;
-
-	if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) {
-		/* No error if bridge already removed. */
-		wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for "
-			   "%s: %s", __func__, br_name, strerror(errno));
-		close(fd);
-		return -1;
-	}
-
-	close(fd);
-	return 0;
-}
-
-
-/*
-	Add a bridge with the name 'br_name'.
-
-	returns -1 on error
-	returns 1 if the bridge already exists
-	returns 0 otherwise
-*/
-static int br_addbr(const char *br_name)
-{
-	int fd;
-	unsigned long arg[4];
-	struct ifreq ifr;
-
-	wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name);
-	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
-			   "failed: %s", __func__, strerror(errno));
-		return -1;
-	}
-
-	arg[0] = BRCTL_ADD_BRIDGE;
-	arg[1] = (unsigned long) br_name;
-
-	if (ioctl(fd, SIOCGIFBR, arg) < 0) {
- 		if (errno == EEXIST) {
-			/* The bridge is already added. */
-			close(fd);
-			return 1;
-		} else {
-			wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE "
-				   "failed for %s: %s",
-				   __func__, br_name, strerror(errno));
-			close(fd);
-			return -1;
-		}
-	}
-
-	/* Decrease forwarding delay to avoid EAPOL timeouts. */
-	os_memset(&ifr, 0, sizeof(ifr));
-	os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ);
-	arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
-	arg[1] = 1;
-	arg[2] = 0;
-	arg[3] = 0;
-	ifr.ifr_data = (char *) &arg;
-	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: "
-			   "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for "
-			   "%s: %s", __func__, br_name, strerror(errno));
-		/* Continue anyway */
-	}
-
-	close(fd);
-	return 0;
-}
-
-
-static int br_getnumports(const char *br_name)
-{
-	int fd;
-	int i;
-	int port_cnt = 0;
-	unsigned long arg[4];
-	int ifindices[MAX_BR_PORTS];
-	struct ifreq ifr;
-
-	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
-			   "failed: %s", __func__, strerror(errno));
-		return -1;
-	}
-
-	arg[0] = BRCTL_GET_PORT_LIST;
-	arg[1] = (unsigned long) ifindices;
-	arg[2] = MAX_BR_PORTS;
-	arg[3] = 0;
-
-	os_memset(ifindices, 0, sizeof(ifindices));
-	os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
-	ifr.ifr_data = (__caddr_t) arg;
-
-	if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST "
-			   "failed for %s: %s",
-			   __func__, br_name, strerror(errno));
-		close(fd);
-		return -1;
-	}
-
-	for (i = 1; i < MAX_BR_PORTS; i++) {
-		if (ifindices[i] > 0) {
-			port_cnt++;
-		}
-	}
-
-	close(fd);
-	return port_cnt;
-}
-
-
-#ifndef CONFIG_VLAN_NETLINK
-
-int vlan_rem(const char *if_name)
-{
-	int fd;
-	struct vlan_ioctl_args if_request;
-
-	wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name);
-	if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
-		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
-			   if_name);
-		return -1;
-	}
-
-	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
-			   "failed: %s", __func__, strerror(errno));
-		return -1;
-	}
-
-	os_memset(&if_request, 0, sizeof(if_request));
-
-	os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
-	if_request.cmd = DEL_VLAN_CMD;
-
-	if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: "
-			   "%s", __func__, if_name, strerror(errno));
-		close(fd);
-		return -1;
-	}
-
-	close(fd);
-	return 0;
-}
-
-
-/*
-	Add a vlan interface with VLAN ID 'vid' and tagged interface
-	'if_name'.
-
-	returns -1 on error
-	returns 1 if the interface already exists
-	returns 0 otherwise
-*/
-int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
-{
-	int fd;
-	struct vlan_ioctl_args if_request;
-
-	wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)",
-		   if_name, vid);
-	ifconfig_up(if_name);
-
-	if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
-		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
-			   if_name);
-		return -1;
-	}
-
-	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
-			   "failed: %s", __func__, strerror(errno));
-		return -1;
-	}
-
-	os_memset(&if_request, 0, sizeof(if_request));
-
-	/* Determine if a suitable vlan device already exists. */
-
-	os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d",
-		    vid);
-
-	if_request.cmd = _GET_VLAN_VID_CMD;
-
-	if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0) {
-
-		if (if_request.u.VID == vid) {
-			if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD;
-
-			if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 &&
-			    os_strncmp(if_request.u.device2, if_name,
-				       sizeof(if_request.u.device2)) == 0) {
-				close(fd);
-				wpa_printf(MSG_DEBUG, "VLAN: vlan_add: "
-					   "if_name %s exists already",
-					   if_request.device1);
-				return 1;
-			}
-		}
-	}
-
-	/* A suitable vlan device does not already exist, add one. */
-
-	os_memset(&if_request, 0, sizeof(if_request));
-	os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
-	if_request.u.VID = vid;
-	if_request.cmd = ADD_VLAN_CMD;
-
-	if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: ADD_VLAN_CMD failed for %s: "
-			   "%s",
-			   __func__, if_request.device1, strerror(errno));
-		close(fd);
-		return -1;
-	}
-
-	close(fd);
-	return 0;
-}
-
-
-static int vlan_set_name_type(unsigned int name_type)
-{
-	int fd;
-	struct vlan_ioctl_args if_request;
-
-	wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)",
-		   name_type);
-	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
-			   "failed: %s", __func__, strerror(errno));
-		return -1;
-	}
-
-	os_memset(&if_request, 0, sizeof(if_request));
-
-	if_request.u.name_type = name_type;
-	if_request.cmd = SET_VLAN_NAME_TYPE_CMD;
-	if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: SET_VLAN_NAME_TYPE_CMD "
-			   "name_type=%u failed: %s",
-			   __func__, name_type, strerror(errno));
-		close(fd);
-		return -1;
-	}
-
-	close(fd);
-	return 0;
-}
-
-#endif /* CONFIG_VLAN_NETLINK */
-
-
-static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface,
-				const char *br_name, int vid,
-				struct hostapd_data *hapd)
-{
-	char vlan_ifname[IFNAMSIZ];
-	int clean;
-
-	if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
-		os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
-			    tagged_interface, vid);
-	else
-		os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid);
-
-	clean = 0;
-	ifconfig_up(tagged_interface);
-	if (!vlan_add(tagged_interface, vid, vlan_ifname))
-		clean |= DVLAN_CLEAN_VLAN;
-
-	if (!br_addif(br_name, vlan_ifname))
-		clean |= DVLAN_CLEAN_VLAN_PORT;
-
-	dyn_iface_get(hapd, vlan_ifname, clean);
-
-	ifconfig_up(vlan_ifname);
-}
-
-
-static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd, int vid)
-{
-	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
-
-	if (hapd->conf->vlan_bridge[0]) {
-		os_snprintf(br_name, IFNAMSIZ, "%s%d",
-			    hapd->conf->vlan_bridge, vid);
-	} else if (tagged_interface) {
-		os_snprintf(br_name, IFNAMSIZ, "br%s.%d",
-			    tagged_interface, vid);
-	} else {
-		os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid);
-	}
-}
-
-
-static void vlan_get_bridge(const char *br_name, struct hostapd_data *hapd,
-			    int vid)
-{
-	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
-	int vlan_naming = hapd->conf->ssid.vlan_naming;
-
-	dyn_iface_get(hapd, br_name, br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR);
-
-	ifconfig_up(br_name);
-
-	if (tagged_interface)
-		vlan_newlink_tagged(vlan_naming, tagged_interface, br_name,
-				    vid, hapd);
-}
-
-
-static void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
-{
-	char br_name[IFNAMSIZ];
-	struct hostapd_vlan *vlan;
-	int untagged, *tagged, i, notempty;
-
-	wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
-
-	for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
-		if (vlan->configured ||
-		    os_strcmp(ifname, vlan->ifname) != 0)
-			continue;
-		break;
-	}
-	if (!vlan)
-		return;
-
-	vlan->configured = 1;
-
-	notempty = vlan->vlan_desc.notempty;
-	untagged = vlan->vlan_desc.untagged;
-	tagged = vlan->vlan_desc.tagged;
-
-	if (!notempty) {
-		/* Non-VLAN STA */
-		if (hapd->conf->bridge[0] &&
-		    !br_addif(hapd->conf->bridge, ifname))
-			vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
-	} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
-		vlan_bridge_name(br_name, hapd, untagged);
-
-		vlan_get_bridge(br_name, hapd, untagged);
-
-		if (!br_addif(br_name, ifname))
-			vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
-	}
-
-	for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
-		if (tagged[i] == untagged ||
-		    tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
-		    (i > 0 && tagged[i] == tagged[i - 1]))
-			continue;
-		vlan_bridge_name(br_name, hapd, tagged[i]);
-		vlan_get_bridge(br_name, hapd, tagged[i]);
-		vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
-				    ifname, br_name, tagged[i], hapd);
-	}
-
-	ifconfig_up(ifname);
-}
-
-
-static void vlan_dellink_tagged(int vlan_naming, const char *tagged_interface,
-				const char *br_name, int vid,
-				struct hostapd_data *hapd)
-{
-	char vlan_ifname[IFNAMSIZ];
-	int clean;
-
-	if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
-		os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
-			    tagged_interface, vid);
-	else
-		os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid);
-
-	clean = dyn_iface_put(hapd, vlan_ifname);
-
-	if (clean & DVLAN_CLEAN_VLAN_PORT)
-		br_delif(br_name, vlan_ifname);
-
-	if (clean & DVLAN_CLEAN_VLAN) {
-		ifconfig_down(vlan_ifname);
-		vlan_rem(vlan_ifname);
-	}
-}
-
-
-static void vlan_put_bridge(const char *br_name, struct hostapd_data *hapd,
-			    int vid)
-{
-	int clean;
-	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
-	int vlan_naming = hapd->conf->ssid.vlan_naming;
-
-	if (tagged_interface)
-		vlan_dellink_tagged(vlan_naming, tagged_interface, br_name,
-				    vid, hapd);
-
-	clean = dyn_iface_put(hapd, br_name);
-	if ((clean & DVLAN_CLEAN_BR) && br_getnumports(br_name) == 0) {
-		ifconfig_down(br_name);
-		br_delbr(br_name);
-	}
-}
-
-
-static void vlan_dellink(const char *ifname, struct hostapd_data *hapd)
-{
-	struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
-
-	wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
-
-	first = prev = vlan;
-
-	while (vlan) {
-		if (os_strcmp(ifname, vlan->ifname) != 0) {
-			prev = vlan;
-			vlan = vlan->next;
-			continue;
-		}
-		break;
-	}
-	if (!vlan)
-		return;
-
-	if (vlan->configured) {
-		int notempty = vlan->vlan_desc.notempty;
-		int untagged = vlan->vlan_desc.untagged;
-		int *tagged = vlan->vlan_desc.tagged;
-		char br_name[IFNAMSIZ];
-		int i;
-
-		for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
-			if (tagged[i] == untagged ||
-			    tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
-			    (i > 0 && tagged[i] == tagged[i - 1]))
-				continue;
-			vlan_bridge_name(br_name, hapd, tagged[i]);
-			vlan_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
-					    ifname, br_name, tagged[i], hapd);
-			vlan_put_bridge(br_name, hapd, tagged[i]);
-		}
-
-		if (!notempty) {
-			/* Non-VLAN STA */
-			if (hapd->conf->bridge[0] &&
-			    (vlan->clean & DVLAN_CLEAN_WLAN_PORT))
-				br_delif(hapd->conf->bridge, ifname);
-		} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
-			vlan_bridge_name(br_name, hapd, untagged);
-
-			if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
-				br_delif(br_name, vlan->ifname);
-
-			vlan_put_bridge(br_name, hapd, untagged);
-		}
-	}
-
-	/*
-	 * Ensure this VLAN interface is actually removed even if
-	 * NEWLINK message is only received later.
-	 */
-	if (if_nametoindex(vlan->ifname) && vlan_if_remove(hapd, vlan))
-		wpa_printf(MSG_ERROR,
-			   "VLAN: Could not remove VLAN iface: %s: %s",
-			   vlan->ifname, strerror(errno));
-
-	if (vlan == first)
-		hapd->conf->vlan = vlan->next;
-	else
-		prev->next = vlan->next;
-
-	os_free(vlan);
-}
-
-
-static void
-vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
-		  struct hostapd_data *hapd)
-{
-	struct ifinfomsg *ifi;
-	int attrlen, nlmsg_len, rta_len;
-	struct rtattr *attr;
-	char ifname[IFNAMSIZ + 1];
-
-	if (len < sizeof(*ifi))
-		return;
-
-	ifi = NLMSG_DATA(h);
-
-	nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
-
-	attrlen = h->nlmsg_len - nlmsg_len;
-	if (attrlen < 0)
-		return;
-
-	attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
-
-	os_memset(ifname, 0, sizeof(ifname));
-	rta_len = RTA_ALIGN(sizeof(struct rtattr));
-	while (RTA_OK(attr, attrlen)) {
-		if (attr->rta_type == IFLA_IFNAME) {
-			int n = attr->rta_len - rta_len;
-			if (n < 0)
-				break;
-
-			if ((size_t) n >= sizeof(ifname))
-				n = sizeof(ifname) - 1;
-			os_memcpy(ifname, ((char *) attr) + rta_len, n);
-
-		}
-
-		attr = RTA_NEXT(attr, attrlen);
-	}
-
-	if (!ifname[0])
-		return;
-	if (del && if_nametoindex(ifname)) {
-		 /* interface still exists, race condition ->
-		  * iface has just been recreated */
-		return;
-	}
-
-	wpa_printf(MSG_DEBUG,
-		   "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
-		   del ? "DEL" : "NEW",
-		   ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags,
-		   (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
-		   (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
-		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
-		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
-
-	if (del)
-		vlan_dellink(ifname, hapd);
-	else
-		vlan_newlink(ifname, hapd);
-}
-
-
-static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
-{
-	char buf[8192];
-	int left;
-	struct sockaddr_nl from;
-	socklen_t fromlen;
-	struct nlmsghdr *h;
-	struct hostapd_data *hapd = eloop_ctx;
-
-	fromlen = sizeof(from);
-	left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
-			(struct sockaddr *) &from, &fromlen);
-	if (left < 0) {
-		if (errno != EINTR && errno != EAGAIN)
-			wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s",
-				   __func__, strerror(errno));
-		return;
-	}
-
-	h = (struct nlmsghdr *) buf;
-	while (NLMSG_OK(h, left)) {
-		int len, plen;
-
-		len = h->nlmsg_len;
-		plen = len - sizeof(*h);
-		if (len > left || plen < 0) {
-			wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink "
-				   "message: len=%d left=%d plen=%d",
-				   len, left, plen);
-			break;
-		}
-
-		switch (h->nlmsg_type) {
-		case RTM_NEWLINK:
-			vlan_read_ifnames(h, plen, 0, hapd);
-			break;
-		case RTM_DELLINK:
-			vlan_read_ifnames(h, plen, 1, hapd);
-			break;
-		}
-
-		h = NLMSG_NEXT(h, left);
-	}
-
-	if (left > 0) {
-		wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of "
-			   "netlink message", __func__, left);
-	}
-}
-
-
-static struct full_dynamic_vlan *
-full_dynamic_vlan_init(struct hostapd_data *hapd)
-{
-	struct sockaddr_nl local;
-	struct full_dynamic_vlan *priv;
-
-	priv = os_zalloc(sizeof(*priv));
-	if (priv == NULL)
-		return NULL;
-
-#ifndef CONFIG_VLAN_NETLINK
-	vlan_set_name_type(hapd->conf->ssid.vlan_naming ==
-			   DYNAMIC_VLAN_NAMING_WITH_DEVICE ?
-			   VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD :
-			   VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
-#endif /* CONFIG_VLAN_NETLINK */
-
-	priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
-	if (priv->s < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW,"
-			   "NETLINK_ROUTE) failed: %s",
-			   __func__, strerror(errno));
-		os_free(priv);
-		return NULL;
-	}
-
-	os_memset(&local, 0, sizeof(local));
-	local.nl_family = AF_NETLINK;
-	local.nl_groups = RTMGRP_LINK;
-	if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) {
-		wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s",
-			   __func__, strerror(errno));
-		close(priv->s);
-		os_free(priv);
-		return NULL;
-	}
-
-	if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL))
-	{
-		close(priv->s);
-		os_free(priv);
-		return NULL;
-	}
-
-	return priv;
-}
-
-
-static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv)
-{
-	if (priv == NULL)
-		return;
-	eloop_unregister_read_sock(priv->s);
-	close(priv->s);
-	os_free(priv);
-}
-#endif /* CONFIG_FULL_DYNAMIC_VLAN */
-
-
 static int vlan_dynamic_add(struct hostapd_data *hapd,
 			    struct hostapd_vlan *vlan)
 {
diff --git a/src/ap/vlan_ioctl.c b/src/ap/vlan_ioctl.c
new file mode 100644
index 0000000..987b612
--- /dev/null
+++ b/src/ap/vlan_ioctl.c
@@ -0,0 +1,155 @@
+/*
+ * hostapd / VLAN ioctl API
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <sys/ioctl.h>
+
+#include "utils/common.h"
+#include "common/linux_vlan.h"
+#include "vlan_util.h"
+
+
+int vlan_rem(const char *if_name)
+{
+	int fd;
+	struct vlan_ioctl_args if_request;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name);
+	if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
+		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+			   if_name);
+		return -1;
+	}
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	os_memset(&if_request, 0, sizeof(if_request));
+
+	os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
+	if_request.cmd = DEL_VLAN_CMD;
+
+	if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: "
+			   "%s", __func__, if_name, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+/*
+	Add a vlan interface with VLAN ID 'vid' and tagged interface
+	'if_name'.
+
+	returns -1 on error
+	returns 1 if the interface already exists
+	returns 0 otherwise
+*/
+int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
+{
+	int fd;
+	struct vlan_ioctl_args if_request;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)",
+		   if_name, vid);
+	ifconfig_up(if_name);
+
+	if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
+		wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+			   if_name);
+		return -1;
+	}
+
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+			   "failed: %s", __func__, strerror(errno));
+		return -1;
+	}
+
+	os_memset(&if_request, 0, sizeof(if_request));
+
+	/* Determine if a suitable vlan device already exists. */
+
+	os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d",
+		    vid);
+
+	if_request.cmd = GET_VLAN_VID_CMD;
+
+	if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 &&
+	    if_request.u.VID == vid) {
+		if_request.cmd = GET_VLAN_REALDEV_NAME_CMD;
+
+		if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 &&
+		    os_strncmp(if_request.u.device2, if_name,
+			       sizeof(if_request.u.device2)) == 0) {
+			close(fd);
+			wpa_printf(MSG_DEBUG,
+				   "VLAN: vlan_add: if_name %s exists already",
+				   if_request.device1);
+			return 1;
+		}
+	}
+
+	/* A suitable vlan device does not already exist, add one. */
+
+	os_memset(&if_request, 0, sizeof(if_request));
+	os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
+	if_request.u.VID = vid;
+	if_request.cmd = ADD_VLAN_CMD;
+
+	if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "VLAN: %s: ADD_VLAN_CMD failed for %s: %s",
+			   __func__, if_request.device1, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
+
+
+int vlan_set_name_type(unsigned int name_type)
+{
+	int fd;
+	struct vlan_ioctl_args if_request;
+
+	wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)",
+		   name_type);
+	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "VLAN: %s: socket(AF_INET,SOCK_STREAM) failed: %s",
+			   __func__, strerror(errno));
+		return -1;
+	}
+
+	os_memset(&if_request, 0, sizeof(if_request));
+
+	if_request.u.name_type = name_type;
+	if_request.cmd = SET_VLAN_NAME_TYPE_CMD;
+	if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "VLAN: %s: SET_VLAN_NAME_TYPE_CMD name_type=%u failed: %s",
+			   __func__, name_type, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	close(fd);
+	return 0;
+}
diff --git a/src/ap/vlan_util.c b/src/ap/vlan_util.c
index 313d89f..56d1d3d 100644
--- a/src/ap/vlan_util.c
+++ b/src/ap/vlan_util.c
@@ -7,18 +7,10 @@
  */
 
 #include "utils/includes.h"
-#include <sys/ioctl.h>
-#include <linux/sockios.h>
-#include <linux/if_vlan.h>
-#include <netlink/genl/genl.h>
-#include <netlink/genl/family.h>
-#include <netlink/genl/ctrl.h>
 #include <netlink/route/link.h>
 #include <netlink/route/link/vlan.h>
 
 #include "utils/common.h"
-#include "utils/eloop.h"
-#include "hostapd.h"
 #include "vlan_util.h"
 
 /*
@@ -174,3 +166,9 @@
 		nl_socket_free(handle);
 	return ret;
 }
+
+
+int vlan_set_name_type(unsigned int name_type)
+{
+	return 0;
+}
diff --git a/src/ap/vlan_util.h b/src/ap/vlan_util.h
index bef5a16..2446859 100644
--- a/src/ap/vlan_util.h
+++ b/src/ap/vlan_util.h
@@ -1,5 +1,5 @@
 /*
- * hostapd / VLAN netlink api
+ * hostapd / VLAN netlink/ioctl api
  * Copyright (c) 2012, Michael Braun <michael-dev@fami-braun.de>
  *
  * This software may be distributed under the terms of the BSD license.
@@ -9,7 +9,23 @@
 #ifndef VLAN_UTIL_H
 #define VLAN_UTIL_H
 
+struct hostapd_data;
+struct hostapd_vlan;
+struct full_dynamic_vlan;
+
 int vlan_add(const char *if_name, int vid, const char *vlan_if_name);
 int vlan_rem(const char *if_name);
+int vlan_set_name_type(unsigned int name_type);
+
+int ifconfig_helper(const char *if_name, int up);
+int ifconfig_up(const char *if_name);
+int iface_exists(const char *ifname);
+int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan);
+
+struct full_dynamic_vlan *
+full_dynamic_vlan_init(struct hostapd_data *hapd);
+void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv);
+void vlan_newlink(const char *ifname, struct hostapd_data *hapd);
+void vlan_dellink(const char *ifname, struct hostapd_data *hapd);
 
 #endif /* VLAN_UTIL_H */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 4ded9bb..3587086 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -3359,6 +3359,46 @@
 }
 
 
+int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
+			size_t len)
+{
+	if (!wpa_auth || !wpa_auth->pmksa)
+		return 0;
+	return pmksa_cache_auth_list(wpa_auth->pmksa, buf, len);
+}
+
+
+void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth)
+{
+	if (wpa_auth && wpa_auth->pmksa)
+		pmksa_cache_auth_flush(wpa_auth->pmksa);
+}
+
+
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
+{
+	if (!wpa_auth || !wpa_auth->pmksa)
+		return NULL;
+	return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
+}
+
+
+void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
+			      struct wpa_state_machine *sm,
+			      struct wpa_authenticator *wpa_auth,
+			      u8 *pmkid, u8 *pmk)
+{
+	if (!sm)
+		return;
+
+	sm->pmksa = pmksa;
+	os_memcpy(pmk, pmksa->pmk, PMK_LEN);
+	os_memcpy(pmkid, pmksa->pmkid, PMKID_LEN);
+	os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmksa->pmkid, PMKID_LEN);
+}
+
+
 /*
  * Remove and free the group from wpa_authenticator. This is triggered by a
  * callback to make sure nobody is currently iterating the group list while it
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 2b60114..0de8d97 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -298,6 +298,15 @@
 			   const u8 *pmk, const u8 *pmkid);
 void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
 			   const u8 *sta_addr);
+int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
+			size_t len);
+void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth);
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr);
+void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
+			      struct wpa_state_machine *sm,
+			      struct wpa_authenticator *wpa_auth,
+			      u8 *pmkid, u8 *pmk);
 int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
 void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
 				  struct wpa_state_machine *sm, int ack);
diff --git a/src/common/ctrl_iface_common.c b/src/common/ctrl_iface_common.c
index acd2410..ebbe6ff 100644
--- a/src/common/ctrl_iface_common.c
+++ b/src/common/ctrl_iface_common.c
@@ -17,15 +17,15 @@
 static int sockaddr_compare(struct sockaddr_storage *a, socklen_t a_len,
 			    struct sockaddr_storage *b, socklen_t b_len)
 {
-	struct sockaddr_in *in_a, *in_b;
-	struct sockaddr_in6 *in6_a, *in6_b;
-	struct sockaddr_un *u_a, *u_b;
-
 	if (a->ss_family != b->ss_family)
 		return 1;
 
 	switch (a->ss_family) {
+#ifdef CONFIG_CTRL_IFACE_UDP
 	case AF_INET:
+	{
+		struct sockaddr_in *in_a, *in_b;
+
 		in_a = (struct sockaddr_in *) a;
 		in_b = (struct sockaddr_in *) b;
 
@@ -34,7 +34,11 @@
 		if (in_a->sin_addr.s_addr != in_b->sin_addr.s_addr)
 			return 1;
 		break;
+	}
 	case AF_INET6:
+	{
+		struct sockaddr_in6 *in6_a, *in6_b;
+
 		in6_a = (struct sockaddr_in6 *) a;
 		in6_b = (struct sockaddr_in6 *) b;
 
@@ -44,7 +48,13 @@
 			      sizeof(in6_a->sin6_addr)) != 0)
 			return 1;
 		break;
+	}
+#endif /* CONFIG_CTRL_IFACE_UDP */
+#ifdef CONFIG_CTRL_IFACE_UNIX
 	case AF_UNIX:
+	{
+		struct sockaddr_un *u_a, *u_b;
+
 		u_a = (struct sockaddr_un *) a;
 		u_b = (struct sockaddr_un *) b;
 
@@ -54,6 +64,8 @@
 		    != 0)
 			return 1;
 		break;
+	}
+#endif /* CONFIG_CTRL_IFACE_UNIX */
 	default:
 		return 1;
 	}
@@ -65,13 +77,14 @@
 void sockaddr_print(int level, const char *msg, struct sockaddr_storage *sock,
 		    socklen_t socklen)
 {
-	char host[NI_MAXHOST] = { 0 };
-	char service[NI_MAXSERV] = { 0 };
-	char addr_txt[200];
-
 	switch (sock->ss_family) {
+#ifdef CONFIG_CTRL_IFACE_UDP
 	case AF_INET:
 	case AF_INET6:
+	{
+		char host[NI_MAXHOST] = { 0 };
+		char service[NI_MAXSERV] = { 0 };
+
 		getnameinfo((struct sockaddr *) sock, socklen,
 			    host, sizeof(host),
 			    service, sizeof(service),
@@ -79,12 +92,20 @@
 
 		wpa_printf(level, "%s %s:%s", msg, host, service);
 		break;
+	}
+#endif /* CONFIG_CTRL_IFACE_UDP */
+#ifdef CONFIG_CTRL_IFACE_UNIX
 	case AF_UNIX:
+	{
+		char addr_txt[200];
+
 		printf_encode(addr_txt, sizeof(addr_txt),
 			      (u8 *) ((struct sockaddr_un *) sock)->sun_path,
 			      socklen - offsetof(struct sockaddr_un, sun_path));
 		wpa_printf(level, "%s %s", msg, addr_txt);
 		break;
+	}
+#endif /* CONFIG_CTRL_IFACE_UNIX */
 	default:
 		wpa_printf(level, "%s", msg);
 		break;
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 73ffc99..e1a8ef7 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -527,10 +527,7 @@
 			 * FH Params, DS Params, CF Params, IBSS Params, TIM */
 			u8 variable[];
 		} STRUCT_PACKED beacon;
-		struct {
-			/* only variable items: SSID, Supported rates */
-			u8 variable[0];
-		} STRUCT_PACKED probe_req;
+		/* probe_req: only variable items: SSID, Supported rates */
 		struct {
 			u8 timestamp[8];
 			le16 beacon_int;
diff --git a/src/common/linux_bridge.h b/src/common/linux_bridge.h
new file mode 100644
index 0000000..7b76846
--- /dev/null
+++ b/src/common/linux_bridge.h
@@ -0,0 +1,24 @@
+/*
+ * Linux bridge configuration kernel interface
+ * Copyright (c) 2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef LINUX_BRIDGE_H
+#define LINUX_BRIDGE_H
+
+/* This interface is defined in linux/if_bridge.h */
+
+#define BRCTL_GET_VERSION 0
+#define BRCTL_GET_BRIDGES 1
+#define BRCTL_ADD_BRIDGE 2
+#define BRCTL_DEL_BRIDGE 3
+#define BRCTL_ADD_IF 4
+#define BRCTL_DEL_IF 5
+#define BRCTL_GET_BRIDGE_INFO 6
+#define BRCTL_GET_PORT_LIST 7
+#define BRCTL_SET_BRIDGE_FORWARD_DELAY 8
+
+#endif /* LINUX_BRIDGE_H */
diff --git a/src/common/linux_vlan.h b/src/common/linux_vlan.h
new file mode 100644
index 0000000..8a1dd6e
--- /dev/null
+++ b/src/common/linux_vlan.h
@@ -0,0 +1,52 @@
+/*
+ * Linux VLAN configuration kernel interface
+ * Copyright (c) 2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef LINUX_VLAN_H
+#define LINUX_VLAN_H
+
+/* This ioctl is defined in linux/sockios.h */
+
+#ifndef SIOCSIFVLAN
+#define SIOCSIFVLAN 0x8983
+#endif /* SIOCSIFVLAN */
+
+/* This interface is defined in linux/if_vlan.h */
+
+#define ADD_VLAN_CMD 0
+#define DEL_VLAN_CMD 1
+#define SET_VLAN_INGRESS_PRIORITY_CMD 2
+#define SET_VLAN_EGRESS_PRIORITY_CMD 3
+#define GET_VLAN_INGRESS_PRIORITY_CMD 4
+#define GET_VLAN_EGRESS_PRIORITY_CMD 5
+#define SET_VLAN_NAME_TYPE_CMD 6
+#define SET_VLAN_FLAG_CMD 7
+#define GET_VLAN_REALDEV_NAME_CMD 8
+#define GET_VLAN_VID_CMD 9
+
+#define VLAN_NAME_TYPE_PLUS_VID 0
+#define VLAN_NAME_TYPE_RAW_PLUS_VID 1
+#define VLAN_NAME_TYPE_PLUS_VID_NO_PAD 2
+#define VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD 3
+
+struct vlan_ioctl_args {
+	int cmd;
+	char device1[24];
+
+	union {
+		char device2[24];
+		int VID;
+		unsigned int skb_priority;
+		unsigned int name_type;
+		unsigned int bind_type;
+		unsigned int flag;
+	} u;
+
+	short vlan_qos;
+};
+
+#endif /* LINUX_VLAN_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index 6c00a7e..9f70f03 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -1,6 +1,6 @@
 /*
  * Simultaneous authentication of equals
- * Copyright (c) 2012-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2012-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -275,8 +275,9 @@
 
 	/* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
 	bits = crypto_ec_prime_len_bits(sae->tmp->ec);
-	sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
-			prime, sae->tmp->prime_len, pwd_value, bits);
+	if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
+			    prime, sae->tmp->prime_len, pwd_value, bits) < 0)
+		return -1;
 	if (bits % 8)
 		buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
 	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
@@ -318,11 +319,10 @@
 	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
 
 	/* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
-	sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
-			sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value,
-			bits);
-	if (bits % 8)
-		buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
+	if (sha256_prf_bits(pwd_seed, SHA256_MAC_LEN, "SAE Hunting and Pecking",
+			    sae->tmp->dh->prime, sae->tmp->prime_len, pwd_value,
+			    bits) < 0)
+		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value,
 			sae->tmp->prime_len);
 
@@ -811,8 +811,9 @@
 	crypto_bignum_mod(tmp, sae->tmp->order, tmp);
 	crypto_bignum_to_bin(tmp, val, sizeof(val), sae->tmp->prime_len);
 	wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
-	sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK",
-		   val, sae->tmp->prime_len, keys, sizeof(keys));
+	if (sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK",
+		       val, sae->tmp->prime_len, keys, sizeof(keys)) < 0)
+		goto fail;
 	os_memset(keyseed, 0, sizeof(keyseed));
 	os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN);
 	os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN);
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index ee20b37..c5a28ce 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -998,13 +998,20 @@
 
 struct crypto_bignum * crypto_bignum_init(void)
 {
+	if (TEST_FAIL())
+		return NULL;
 	return (struct crypto_bignum *) BN_new();
 }
 
 
 struct crypto_bignum * crypto_bignum_init_set(const u8 *buf, size_t len)
 {
-	BIGNUM *bn = BN_bin2bn(buf, len, NULL);
+	BIGNUM *bn;
+
+	if (TEST_FAIL())
+		return NULL;
+
+	bn = BN_bin2bn(buf, len, NULL);
 	return (struct crypto_bignum *) bn;
 }
 
@@ -1023,6 +1030,9 @@
 {
 	int num_bytes, offset;
 
+	if (TEST_FAIL())
+		return -1;
+
 	if (padlen > buflen)
 		return -1;
 
@@ -1076,6 +1086,9 @@
 	int res;
 	BN_CTX *bnctx;
 
+	if (TEST_FAIL())
+		return -1;
+
 	bnctx = BN_CTX_new();
 	if (bnctx == NULL)
 		return -1;
@@ -1094,6 +1107,8 @@
 	BIGNUM *res;
 	BN_CTX *bnctx;
 
+	if (TEST_FAIL())
+		return -1;
 	bnctx = BN_CTX_new();
 	if (bnctx == NULL)
 		return -1;
@@ -1109,6 +1124,8 @@
 		      const struct crypto_bignum *b,
 		      struct crypto_bignum *c)
 {
+	if (TEST_FAIL())
+		return -1;
 	return BN_sub((BIGNUM *) c, (const BIGNUM *) a, (const BIGNUM *) b) ?
 		0 : -1;
 }
@@ -1122,6 +1139,9 @@
 
 	BN_CTX *bnctx;
 
+	if (TEST_FAIL())
+		return -1;
+
 	bnctx = BN_CTX_new();
 	if (bnctx == NULL)
 		return -1;
@@ -1142,6 +1162,9 @@
 
 	BN_CTX *bnctx;
 
+	if (TEST_FAIL())
+		return -1;
+
 	bnctx = BN_CTX_new();
 	if (bnctx == NULL)
 		return -1;
@@ -1185,6 +1208,9 @@
 	BIGNUM *exp = NULL, *tmp = NULL;
 	int res = -2;
 
+	if (TEST_FAIL())
+		return -2;
+
 	bnctx = BN_CTX_new();
 	if (bnctx == NULL)
 		return -2;
@@ -1309,6 +1335,8 @@
 
 struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
 {
+	if (TEST_FAIL())
+		return NULL;
 	if (e == NULL)
 		return NULL;
 	return (struct crypto_ec_point *) EC_POINT_new(e->group);
@@ -1355,6 +1383,9 @@
 	int ret = -1;
 	int len = BN_num_bytes(e->prime);
 
+	if (TEST_FAIL())
+		return -1;
+
 	x_bn = BN_new();
 	y_bn = BN_new();
 
@@ -1385,6 +1416,9 @@
 	EC_POINT *elem;
 	int len = BN_num_bytes(e->prime);
 
+	if (TEST_FAIL())
+		return NULL;
+
 	x = BN_bin2bn(val, len, NULL);
 	y = BN_bin2bn(val + len, len, NULL);
 	elem = EC_POINT_new(e->group);
@@ -1412,6 +1446,8 @@
 			const struct crypto_ec_point *b,
 			struct crypto_ec_point *c)
 {
+	if (TEST_FAIL())
+		return -1;
 	return EC_POINT_add(e->group, (EC_POINT *) c, (const EC_POINT *) a,
 			    (const EC_POINT *) b, e->bnctx) ? 0 : -1;
 }
@@ -1421,6 +1457,8 @@
 			const struct crypto_bignum *b,
 			struct crypto_ec_point *res)
 {
+	if (TEST_FAIL())
+		return -1;
 	return EC_POINT_mul(e->group, (EC_POINT *) res, NULL,
 			    (const EC_POINT *) p, (const BIGNUM *) b, e->bnctx)
 		? 0 : -1;
@@ -1429,6 +1467,8 @@
 
 int crypto_ec_point_invert(struct crypto_ec *e, struct crypto_ec_point *p)
 {
+	if (TEST_FAIL())
+		return -1;
 	return EC_POINT_invert(e->group, (EC_POINT *) p, e->bnctx) ? 0 : -1;
 }
 
@@ -1437,6 +1477,8 @@
 				  struct crypto_ec_point *p,
 				  const struct crypto_bignum *x, int y_bit)
 {
+	if (TEST_FAIL())
+		return -1;
 	if (!EC_POINT_set_compressed_coordinates_GFp(e->group, (EC_POINT *) p,
 						     (const BIGNUM *) x, y_bit,
 						     e->bnctx) ||
@@ -1452,6 +1494,9 @@
 {
 	BIGNUM *tmp, *tmp2, *y_sqr = NULL;
 
+	if (TEST_FAIL())
+		return NULL;
+
 	tmp = BN_new();
 	tmp2 = BN_new();
 
diff --git a/src/crypto/sha256-prf.c b/src/crypto/sha256-prf.c
index 79791c0..722cad6 100644
--- a/src/crypto/sha256-prf.c
+++ b/src/crypto/sha256-prf.c
@@ -1,6 +1,6 @@
 /*
  * SHA256-based PRF (IEEE 802.11r)
- * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -22,14 +22,16 @@
  * @data_len: Length of the data
  * @buf: Buffer for the generated pseudo-random key
  * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure
  *
  * This function is used to derive new, cryptographically separate keys from a
  * given key.
  */
-void sha256_prf(const u8 *key, size_t key_len, const char *label,
+int sha256_prf(const u8 *key, size_t key_len, const char *label,
 		const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
 {
-	sha256_prf_bits(key, key_len, label, data, data_len, buf, buf_len * 8);
+	return sha256_prf_bits(key, key_len, label, data, data_len, buf,
+			       buf_len * 8);
 }
 
 
@@ -42,15 +44,16 @@
  * @data_len: Length of the data
  * @buf: Buffer for the generated pseudo-random key
  * @buf_len: Number of bits of key to generate
+ * Returns: 0 on success, -1 on failure
  *
  * This function is used to derive new, cryptographically separate keys from a
  * given key. If the requested buf_len is not divisible by eight, the least
  * significant 1-7 bits of the last octet in the output are not part of the
  * requested output.
  */
-void sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
-		     const u8 *data, size_t data_len, u8 *buf,
-		     size_t buf_len_bits)
+int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits)
 {
 	u16 counter = 1;
 	size_t pos, plen;
@@ -75,11 +78,14 @@
 		plen = buf_len - pos;
 		WPA_PUT_LE16(counter_le, counter);
 		if (plen >= SHA256_MAC_LEN) {
-			hmac_sha256_vector(key, key_len, 4, addr, len,
-					   &buf[pos]);
+			if (hmac_sha256_vector(key, key_len, 4, addr, len,
+					       &buf[pos]) < 0)
+				return -1;
 			pos += SHA256_MAC_LEN;
 		} else {
-			hmac_sha256_vector(key, key_len, 4, addr, len, hash);
+			if (hmac_sha256_vector(key, key_len, 4, addr, len,
+					       hash) < 0)
+				return -1;
 			os_memcpy(&buf[pos], hash, plen);
 			pos += plen;
 			break;
@@ -97,4 +103,6 @@
 	}
 
 	os_memset(hash, 0, sizeof(hash));
+
+	return 0;
 }
diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h
index b15f511..5219022 100644
--- a/src/crypto/sha256.h
+++ b/src/crypto/sha256.h
@@ -1,6 +1,6 @@
 /*
  * SHA256 hash implementation and interface functions
- * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -15,11 +15,11 @@
 		       const u8 *addr[], const size_t *len, u8 *mac);
 int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
 		size_t data_len, u8 *mac);
-void sha256_prf(const u8 *key, size_t key_len, const char *label,
-	      const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
-void sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
-		     const u8 *data, size_t data_len, u8 *buf,
-		     size_t buf_len_bits);
+int sha256_prf(const u8 *key, size_t key_len, const char *label,
+	       const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+int sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
+		    const u8 *data, size_t data_len, u8 *buf,
+		    size_t buf_len_bits);
 void tls_prf_sha256(const u8 *secret, size_t secret_len,
 		    const char *label, const u8 *seed, size_t seed_len,
 		    u8 *out, size_t outlen);
diff --git a/src/crypto/tls_openssl_ocsp.c b/src/crypto/tls_openssl_ocsp.c
index 4e1c6b9..8b37b34 100644
--- a/src/crypto/tls_openssl_ocsp.c
+++ b/src/crypto/tls_openssl_ocsp.c
@@ -433,7 +433,8 @@
 	}
 
 	ikey = X509_get0_pubkey_bitstr(issuer);
-	if (!EVP_Digest(ikey->data, ikey->length, md, &len, dgst, NULL) ||
+	if (!ikey ||
+	    !EVP_Digest(ikey->data, ikey->length, md, &len, dgst, NULL) ||
 	    !ASN1_OCTET_STRING_set(hash, md, len)) {
 		ASN1_OCTET_STRING_free(hash);
 		return -1;
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 642276c..b7e0d16 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1958,6 +1958,14 @@
 	void (*poll)(void *priv);
 
 	/**
+	 * get_ifindex - Get interface index
+	 * @priv: private driver interface data
+	 *
+	 * Returns: Interface index
+	 */
+	unsigned int (*get_ifindex)(void *priv);
+
+	/**
 	 * get_ifname - Get interface name
 	 * @priv: private driver interface data
 	 *
@@ -2091,6 +2099,7 @@
 
 	/**
 	 * global_init - Global driver initialization
+	 * @ctx: wpa_global pointer
 	 * Returns: Pointer to private data (global), %NULL on failure
 	 *
 	 * This optional function is called to initialize the driver wrapper
@@ -2100,7 +2109,7 @@
 	 * use init2() function instead of init() to get the pointer to global
 	 * data available to per-interface initializer.
 	 */
-	void * (*global_init)(void);
+	void * (*global_init)(void *ctx);
 
 	/**
 	 * global_deinit - Global driver deinitialization
@@ -4269,6 +4278,7 @@
 	 * struct interface_status - Data for EVENT_INTERFACE_STATUS
 	 */
 	struct interface_status {
+		unsigned int ifindex;
 		char ifname[100];
 		enum {
 			EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED
@@ -4745,6 +4755,18 @@
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			  union wpa_event_data *data);
 
+/**
+ * wpa_supplicant_event_global - Report a driver event for wpa_supplicant
+ * @ctx: Context pointer (wpa_s); this is the ctx variable registered
+ *	with struct wpa_driver_ops::init()
+ * @event: event type (defined above)
+ * @data: possible extra data for the event
+ *
+ * Same as wpa_supplicant_event(), but we search for the interface in
+ * wpa_global.
+ */
+void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
+				 union wpa_event_data *data);
 
 /*
  * The following inline functions are provided for convenience to simplify
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index a5a379e..ba3cad0 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -855,16 +855,15 @@
 		   (int) len);
 
 	if (stype == WLAN_FC_STYPE_PROBE_REQ) {
-		if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
+		if (len < IEEE80211_HDRLEN)
 			return;
 
 		os_memset(&event, 0, sizeof(event));
 		event.rx_probe_req.sa = mgmt->sa;
 		event.rx_probe_req.da = mgmt->da;
 		event.rx_probe_req.bssid = mgmt->bssid;
-		event.rx_probe_req.ie = mgmt->u.probe_req.variable;
-		event.rx_probe_req.ie_len =
-			len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
+		event.rx_probe_req.ie = buf + IEEE80211_HDRLEN;
+		event.rx_probe_req.ie_len = len - IEEE80211_HDRLEN;
 		wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event);
 		return;
 	}
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index 99f3504..fd73f2e 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -48,6 +48,7 @@
 #include "l2_packet/l2_packet.h"
 
 struct bsd_driver_global {
+	void		*ctx;
 	int		sock;			/* socket for 802.11 ioctls */
 	int		route;			/* routing socket for events */
 	char		*event_buf;
@@ -64,6 +65,7 @@
 	char	ifname[IFNAMSIZ+1];	/* interface name */
 	int	flags;
 	unsigned int ifindex;		/* interface index */
+	int	if_removed;		/* has the interface been removed? */
 	void	*ctx;
 	struct wpa_driver_capa capa;	/* driver capability */
 	int	is_ap;			/* Access point mode */
@@ -88,13 +90,28 @@
 	return NULL;
 }
 
+#ifndef HOSTAPD
+static struct bsd_driver_data *
+bsd_get_drvname(void *priv, const char *ifname)
+{
+	struct bsd_driver_global *global = priv;
+	struct bsd_driver_data *drv;
+
+	dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) {
+		if (os_strcmp(drv->ifname, ifname) == 0)
+			return drv;
+	}
+	return NULL;
+}
+#endif /* HOSTAPD */
+
 static int
 bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len)
 {
 	struct bsd_driver_data *drv = priv;
 	struct ieee80211req ireq;
 
-	if (drv->ifindex == 0)
+	if (drv->ifindex == 0 || drv->if_removed)
 		return -1;
 
 	os_memset(&ireq, 0, sizeof(ireq));
@@ -1222,24 +1239,45 @@
 	switch (rtm->rtm_type) {
 	case RTM_IFANNOUNCE:
 		ifan = (struct if_announcemsghdr *) rtm;
-		drv = bsd_get_drvindex(global, ifan->ifan_index);
-		if (drv == NULL)
-			return;
-		os_strlcpy(event.interface_status.ifname, drv->ifname,
-			   sizeof(event.interface_status.ifname));
 		switch (ifan->ifan_what) {
 		case IFAN_DEPARTURE:
+			drv = bsd_get_drvindex(global, ifan->ifan_index);
+			if (drv)
+				drv->if_removed = 1;
 			event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
-			drv->ifindex = 0;
+			break;
+		case IFAN_ARRIVAL:
+			drv = bsd_get_drvname(global, ifan->ifan_name);
+			if (drv) {
+				drv->ifindex = ifan->ifan_index;
+				drv->if_removed = 0;
+			}
+			event.interface_status.ievent = EVENT_INTERFACE_ADDED;
 			break;
 		default:
+			wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: unknown action");
 			return;
 		}
 		wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s",
-			   event.interface_status.ifname,
+			   ifan->ifan_name,
 			   ifan->ifan_what == IFAN_DEPARTURE ?
 				"removed" : "added");
-		wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+		os_strlcpy(event.interface_status.ifname, ifan->ifan_name,
+			   sizeof(event.interface_status.ifname));
+		if (drv) {
+			wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS,
+					     &event);
+			/*
+			 * Set ifindex to zero after sending the event as the
+			 * event might query the driver to ensure a match.
+			 */
+			if (ifan->ifan_what == IFAN_DEPARTURE)
+				drv->ifindex = 0;
+		} else {
+			wpa_supplicant_event_global(global->ctx,
+						    EVENT_INTERFACE_STATUS,
+						    &event);
+		}
 		break;
 	case RTM_IEEE80211:
 		ifan = (struct if_announcemsghdr *) rtm;
@@ -1542,11 +1580,7 @@
 
 	drv->ctx = ctx;
 	drv->global = priv;
-
 	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
-	/* Down interface during setup. */
-	if (bsd_ctrl_iface(drv, 0) < 0)
-		goto fail;
 
 	if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) {
 		wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s",
@@ -1567,6 +1601,10 @@
 	if (wpa_driver_bsd_capa(drv))
 		goto fail;
 
+	/* Down interface during setup. */
+	if (bsd_ctrl_iface(drv, 0) < 0)
+		goto fail;
+
 	drv->opmode = get80211opmode(drv);
 	dl_list_add(&drv->global->ifaces, &drv->list);
 
@@ -1582,7 +1620,7 @@
 {
 	struct bsd_driver_data *drv = priv;
 
-	if (drv->ifindex != 0) {
+	if (drv->ifindex != 0 && !drv->if_removed) {
 		wpa_driver_bsd_set_wpa(drv, 0);
 
 		/* NB: mark interface down */
@@ -1615,7 +1653,7 @@
 #endif /* HOSTAPD */
 
 static void *
-bsd_global_init(void)
+bsd_global_init(void *ctx)
 {
 	struct bsd_driver_global *global;
 
@@ -1623,6 +1661,7 @@
 	if (global == NULL)
 		return NULL;
 
+	global->ctx = ctx;
 	dl_list_init(&global->ifaces);
 
 	global->sock = socket(PF_INET, SOCK_DGRAM, 0);
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 5fb6652..798e694 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -763,6 +763,15 @@
 }
 
 
+static unsigned int nl80211_get_ifindex(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	return drv->ifindex;
+}
+
+
 static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
 {
 	struct i802_bss *bss = priv;
@@ -786,11 +795,12 @@
 
 
 static void wpa_driver_nl80211_event_newlink(
-	struct wpa_driver_nl80211_data *drv, const char *ifname)
+	struct nl80211_global *global, struct wpa_driver_nl80211_data *drv,
+	int ifindex, const char *ifname)
 {
 	union wpa_event_data event;
 
-	if (os_strcmp(drv->first_bss->ifname, ifname) == 0) {
+	if (drv && os_strcmp(drv->first_bss->ifname, ifname) == 0) {
 		if (if_nametoindex(drv->first_bss->ifname) == 0) {
 			wpa_printf(MSG_DEBUG, "nl80211: Interface %s does not exist - ignore RTM_NEWLINK",
 				   drv->first_bss->ifname);
@@ -804,19 +814,25 @@
 	}
 
 	os_memset(&event, 0, sizeof(event));
+	event.interface_status.ifindex = ifindex;
 	os_strlcpy(event.interface_status.ifname, ifname,
 		   sizeof(event.interface_status.ifname));
 	event.interface_status.ievent = EVENT_INTERFACE_ADDED;
-	wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+	if (drv)
+		wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+	else
+		wpa_supplicant_event_global(global->ctx, EVENT_INTERFACE_STATUS,
+					    &event);
 }
 
 
 static void wpa_driver_nl80211_event_dellink(
-	struct wpa_driver_nl80211_data *drv, const char *ifname)
+	struct nl80211_global *global, struct wpa_driver_nl80211_data *drv,
+	int ifindex, const char *ifname)
 {
 	union wpa_event_data event;
 
-	if (os_strcmp(drv->first_bss->ifname, ifname) == 0) {
+	if (drv && os_strcmp(drv->first_bss->ifname, ifname) == 0) {
 		if (drv->if_removed) {
 			wpa_printf(MSG_DEBUG, "nl80211: if_removed already set - ignore RTM_DELLINK event for %s",
 				   ifname);
@@ -831,10 +847,15 @@
 	}
 
 	os_memset(&event, 0, sizeof(event));
+	event.interface_status.ifindex = ifindex;
 	os_strlcpy(event.interface_status.ifname, ifname,
 		   sizeof(event.interface_status.ifname));
 	event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
-	wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+	if (drv)
+		wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+	else
+		wpa_supplicant_event_global(global->ctx, EVENT_INTERFACE_STATUS,
+					    &event);
 }
 
 
@@ -908,13 +929,6 @@
 	char ifname[IFNAMSIZ + 1];
 	char extra[100], *pos, *end;
 
-	drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
-	if (!drv) {
-		wpa_printf(MSG_DEBUG, "nl80211: Ignore RTM_NEWLINK event for foreign ifindex %d",
-			   ifi->ifi_index);
-		return;
-	}
-
 	extra[0] = '\0';
 	pos = extra;
 	end = pos + sizeof(extra);
@@ -958,6 +972,10 @@
 		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
 		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
 
+	drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
+	if (!drv)
+		goto event_newlink;
+
 	if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
 		namebuf[0] = '\0';
 		if (if_indextoname(ifi->ifi_index, namebuf) &&
@@ -1052,10 +1070,12 @@
 				       -1, IF_OPER_UP);
 	}
 
+event_newlink:
 	if (ifname[0])
-		wpa_driver_nl80211_event_newlink(drv, ifname);
+		wpa_driver_nl80211_event_newlink(global, drv, ifi->ifi_index,
+						 ifname);
 
-	if (ifi->ifi_family == AF_BRIDGE && brid) {
+	if (ifi->ifi_family == AF_BRIDGE && brid && drv) {
 		struct i802_bss *bss;
 
 		/* device has been added to bridge */
@@ -1091,13 +1111,6 @@
 	char ifname[IFNAMSIZ + 1];
 	char extra[100], *pos, *end;
 
-	drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
-	if (!drv) {
-		wpa_printf(MSG_DEBUG, "nl80211: Ignore RTM_DELLINK event for foreign ifindex %d",
-			   ifi->ifi_index);
-		return;
-	}
-
 	extra[0] = '\0';
 	pos = extra;
 	end = pos + sizeof(extra);
@@ -1138,10 +1151,9 @@
 		   (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
 		   (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
 
-	if (ifname[0] && (ifi->ifi_family != AF_BRIDGE || !brid))
-		wpa_driver_nl80211_event_dellink(drv, ifname);
+	drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
 
-	if (ifi->ifi_family == AF_BRIDGE && brid) {
+	if (ifi->ifi_family == AF_BRIDGE && brid && drv) {
 		/* device has been removed from bridge */
 		char namebuf[IFNAMSIZ];
 
@@ -1156,6 +1168,10 @@
 		}
 		del_ifidx(drv, brid, ifi->ifi_index);
 	}
+
+	if (ifi->ifi_family != AF_BRIDGE || !brid)
+		wpa_driver_nl80211_event_dellink(global, drv, ifi->ifi_index,
+						 ifname);
 }
 
 
@@ -3526,24 +3542,26 @@
 	    nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, suite))
 		goto fail;
 
-	switch (params->smps_mode) {
-	case HT_CAP_INFO_SMPS_DYNAMIC:
-		wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - dynamic");
-		smps_mode = NL80211_SMPS_DYNAMIC;
-		break;
-	case HT_CAP_INFO_SMPS_STATIC:
-		wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - static");
-		smps_mode = NL80211_SMPS_STATIC;
-		break;
-	default:
-		/* invalid - fallback to smps off */
-	case HT_CAP_INFO_SMPS_DISABLED:
-		wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - off");
-		smps_mode = NL80211_SMPS_OFF;
-		break;
+	if (params->ht_opmode != -1) {
+		switch (params->smps_mode) {
+		case HT_CAP_INFO_SMPS_DYNAMIC:
+			wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - dynamic");
+			smps_mode = NL80211_SMPS_DYNAMIC;
+			break;
+		case HT_CAP_INFO_SMPS_STATIC:
+			wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - static");
+			smps_mode = NL80211_SMPS_STATIC;
+			break;
+		default:
+			/* invalid - fallback to smps off */
+		case HT_CAP_INFO_SMPS_DISABLED:
+			wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - off");
+			smps_mode = NL80211_SMPS_OFF;
+			break;
+		}
+		if (nla_put_u32(msg, NL80211_ATTR_SMPS_MODE, smps_mode))
+			goto fail;
 	}
-	if (nla_put_u32(msg, NL80211_ATTR_SMPS_MODE, smps_mode))
-		goto fail;
 
 	if (params->beacon_ies) {
 		wpa_hexdump_buf(MSG_DEBUG, "nl80211: beacon_ies",
@@ -4823,6 +4841,16 @@
 			return -1;
 	}
 
+	drv->connect_reassoc = 0;
+	if (params->prev_bssid) {
+		wpa_printf(MSG_DEBUG, "  * prev_bssid=" MACSTR,
+			   MAC2STR(params->prev_bssid));
+		if (nla_put(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN,
+			    params->prev_bssid))
+			return -1;
+		drv->connect_reassoc = 1;
+	}
+
 	return 0;
 }
 
@@ -4975,14 +5003,6 @@
 	if (ret)
 		goto fail;
 
-	if (params->prev_bssid) {
-		wpa_printf(MSG_DEBUG, "  * prev_bssid=" MACSTR,
-			   MAC2STR(params->prev_bssid));
-		if (nla_put(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN,
-			    params->prev_bssid))
-			goto fail;
-	}
-
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	msg = NULL;
 	if (ret) {
@@ -5710,7 +5730,7 @@
 		if (!old_reason)
 			drv->if_indices_reason = drv->default_if_indices_reason;
 		else
-			drv->if_indices = old_reason;
+			drv->if_indices_reason = old_reason;
 	}
 	if (!drv->if_indices || !drv->if_indices_reason) {
 		wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
@@ -6856,7 +6876,7 @@
 }
 
 
-static void * nl80211_global_init(void)
+static void * nl80211_global_init(void *ctx)
 {
 	struct nl80211_global *global;
 	struct netlink_config *cfg;
@@ -6864,6 +6884,7 @@
 	global = os_zalloc(sizeof(*global));
 	if (global == NULL)
 		return NULL;
+	global->ctx = ctx;
 	global->ioctl_sock = -1;
 	dl_list_init(&global->interfaces);
 	global->if_add_ifindex = -1;
@@ -9141,6 +9162,7 @@
 	.br_set_net_param = wpa_driver_br_set_net_param,
 	.add_tx_ts = nl80211_add_ts,
 	.del_tx_ts = nl80211_del_ts,
+	.get_ifindex = nl80211_get_ifindex,
 #ifdef CONFIG_DRIVER_NL80211_QCA
 	.do_acs = wpa_driver_do_acs,
 	.set_band = nl80211_set_band,
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index 430369f..b0d2b6d 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -25,6 +25,7 @@
 #endif /* CONFIG_LIBNL20 */
 
 struct nl80211_global {
+	void *ctx;
 	struct dl_list interfaces;
 	int if_add_ifindex;
 	u64 if_add_wdevid;
@@ -150,6 +151,7 @@
 	unsigned int get_pref_freq_list:1;
 	unsigned int set_prob_oper_freq:1;
 	unsigned int scan_vendor_cmd_avail:1;
+	unsigned int connect_reassoc:1;
 
 	u64 vendor_scan_cookie;
 	u64 remain_on_chan_cookie;
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index dae203a..bd16edb 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -285,6 +285,8 @@
 		return;
 	}
 
+	drv->connect_reassoc = 0;
+
 	status_code = status ? nla_get_u16(status) : WLAN_STATUS_SUCCESS;
 
 	if (cmd == NL80211_CMD_CONNECT) {
@@ -670,6 +672,24 @@
 			return;
 		}
 
+		if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+		    drv->connect_reassoc && drv->associated &&
+		    os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0 &&
+		    os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0) {
+			/*
+			 * Avoid issues with some roaming cases where
+			 * disconnection event for the old AP may show up after
+			 * we have started connection with the new AP.
+			 */
+			 wpa_printf(MSG_DEBUG,
+				    "nl80211: Ignore deauth/disassoc event from old AP "
+				    MACSTR
+				    " when already connecting with " MACSTR,
+				    MAC2STR(bssid),
+				    MAC2STR(drv->auth_attempt_bssid));
+			return;
+		}
+
 		if (drv->associated != 0 &&
 		    os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 &&
 		    os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) {
diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c
index 762c12f..43d4193 100644
--- a/src/drivers/driver_privsep.c
+++ b/src/drivers/driver_privsep.c
@@ -161,7 +161,7 @@
 		return NULL;
 	}
 
-	while (results->num < (size_t) num && end - pos > sizeof(int)) {
+	while (results->num < (size_t) num && end - pos > (int) sizeof(int)) {
 		int len;
 		os_memcpy(&len, pos, sizeof(int));
 		pos += sizeof(int);
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index c4f5f97..c1bfff1 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -63,7 +63,9 @@
   endif
 
   ifdef CONFIG_LIBNL20
-    DRV_LIBS += -lnl-genl
+    ifndef CONFIG_LIBNL_TINY
+      DRV_LIBS += -lnl-genl
+    endif
     DRV_CFLAGS += -DCONFIG_LIBNL20
   endif
 endif
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
index 0a05a24..0444e52 100644
--- a/src/drivers/drivers.mk
+++ b/src/drivers/drivers.mk
@@ -51,7 +51,9 @@
   endif
 
   ifdef CONFIG_LIBNL20
-    DRV_LIBS += -lnl-genl
+    ifndef CONFIG_LIBNL_TINY
+      DRV_LIBS += -lnl-genl
+    endif
     DRV_CFLAGS += -DCONFIG_LIBNL20
   endif
 endif
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index fc545b4..0feafd3 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -1716,6 +1716,12 @@
 	int freq;
 
 	/**
+	 * ip_addr_alloc - Whether IP address allocation within 4-way handshake
+	 *	is supported
+	 */
+	int ip_addr_alloc;
+
+	/**
 	 * cb_ctx - Context to use with callback functions
 	 */
 	void *cb_ctx;
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index 2cf2450..eac73ef 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -155,7 +155,8 @@
 		group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
 	if (group->num_members >= group->cfg->max_clients)
 		group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT;
-	group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION;
+	if (group->cfg->ip_addr_alloc)
+		group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION;
 	p2p_buf_add_capability(ie, dev_capab, group_capab);
 }
 
diff --git a/src/utils/common.h b/src/utils/common.h
index 0b9cc3d..6f0de69 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -313,6 +313,9 @@
 #ifndef ETH_P_ALL
 #define ETH_P_ALL 0x0003
 #endif
+#ifndef ETH_P_IP
+#define ETH_P_IP 0x0800
+#endif
 #ifndef ETH_P_80211_ENCAP
 #define ETH_P_80211_ENCAP 0x890d /* TDLS comes under this category */
 #endif
diff --git a/src/utils/xml_libxml2.c b/src/utils/xml_libxml2.c
index c928394..7b6d276 100644
--- a/src/utils/xml_libxml2.c
+++ b/src/utils/xml_libxml2.c
@@ -212,6 +212,8 @@
 	xmlDocSetRootElement(doc, n);
 	xmlDocDumpFormatMemory(doc, &buf, &bufsiz, 0);
 	xmlFreeDoc(doc);
+	if (!buf)
+		return NULL;
 	pos = (char *) buf;
 	if (strncmp(pos, "<?xml", 5) == 0) {
 		pos = strchr(pos, '>');
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 4f0319e..f65076c 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -269,6 +269,7 @@
 ifdef CONFIG_IBSS_RSN
 NEED_RSN_AUTHENTICATOR=y
 L_CFLAGS += -DCONFIG_IBSS_RSN
+L_CFLAGS += -DCONFIG_NO_VLAN
 OBJS += ibss_rsn.c
 endif
 
@@ -1345,6 +1346,16 @@
 OBJS += $(DBUS_OBJS)
 L_CFLAGS += $(DBUS_CFLAGS)
 
+ifdef CONFIG_CTRL_IFACE_BINDER
+BINDER=y
+L_CFLAGS += -DCONFIG_BINDER -DCONFIG_CTRL_IFACE_BINDER
+OBJS += binder/binder.cpp binder/binder_manager.cpp
+OBJS += binder/supplicant.cpp binder/iface.cpp
+OBJS += binder/fi/w1/wpa_supplicant/ISupplicant.aidl
+OBJS += binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl
+OBJS += binder/fi/w1/wpa_supplicant/IIface.aidl
+endif
+
 ifdef CONFIG_READLINE
 OBJS_c += src/utils/edit_readline.c
 LIBS_c += -lncurses -lreadline
@@ -1582,6 +1593,10 @@
 ifeq ($(DBUS), y)
 LOCAL_SHARED_LIBRARIES += libdbus
 endif
+ifeq ($(BINDER), y)
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/binder frameworks/native/aidl/binder
+LOCAL_SHARED_LIBRARIES += libutils libbinder
+endif
 include $(BUILD_EXECUTABLE)
 
 ########################
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 8fa2b5a..550d44b 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -312,9 +312,14 @@
 ifdef CONFIG_IBSS_RSN
 NEED_RSN_AUTHENTICATOR=y
 CFLAGS += -DCONFIG_IBSS_RSN
+CFLAGS += -DCONFIG_NO_VLAN
 OBJS += ibss_rsn.o
 endif
 
+ifdef CONFIG_MATCH_IFACE
+CFLAGS += -DCONFIG_MATCH_IFACE
+endif
+
 ifdef CONFIG_P2P
 OBJS += p2p_supplicant.o
 OBJS += p2p_supplicant_sd.o
@@ -1637,6 +1642,7 @@
 OBJS += $(FST_OBJS)
 OBJS_t += $(FST_OBJS)
 OBJS_t2 += $(FST_OBJS)
+OBJS_nfc += $(FST_OBJS)
 endif
 
 ifndef LDO
diff --git a/wpa_supplicant/README b/wpa_supplicant/README
index fefc0d3..11ab01a 100644
--- a/wpa_supplicant/README
+++ b/wpa_supplicant/README
@@ -410,7 +410,7 @@
   wpa_supplicant [-BddfhKLqqtuvW] [-P<pid file>] [-g<global ctrl>] \
         [-G<group>] \
         -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] [-p<driver_param>] \
-        [-b<br_ifname> [-N -i<ifname> -c<conf> [-C<ctrl>] [-D<driver>] \
+        [-b<br_ifname> [-MN -i<ifname> -c<conf> [-C<ctrl>] [-D<driver>] \
         [-p<driver_param>] [-b<br_ifname>] [-m<P2P Device config file>] ...
 
 options:
@@ -434,6 +434,7 @@
   -u = enable DBus control interface
   -v = show version
   -W = wait for a control interface monitor before starting
+  -M = start describing matching interface
   -N = start describing new interface
   -m = Configuration file for the P2P Device
 
@@ -476,6 +477,22 @@
 	-c wpa2.conf -i wlan1 -D wext
 
 
+If the interfaces on which wpa_supplicant is to run are not known or do
+not exist, wpa_supplicant can match an interface when it arrives. Each
+matched interface is separated with -M argument and the -i argument now
+allows for pattern matching.
+
+As an example, the following command would start wpa_supplicant for a
+specific wired interface called lan0, any interface starting with wlan
+and lastly any other interface. Each match has its own configuration
+file, and for the wired interface a specific driver has also been given.
+
+wpa_supplicant \
+	-M -c wpa_wired.conf -ilan0 -D wired \
+	-M -c wpa1.conf -iwlan* \
+	-M -c wpa2.conf
+
+
 If the interface is added in a Linux bridge (e.g., br0), the bridge
 interface needs to be configured to wpa_supplicant in addition to the
 main interface:
diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
index 190bc5a..0a8bf98 100644
--- a/wpa_supplicant/android.config
+++ b/wpa_supplicant/android.config
@@ -324,6 +324,10 @@
 # Add introspection support for new DBus control interface
 #CONFIG_CTRL_IFACE_DBUS_INTRO=y
 
+# Add support for Binder control interface
+# Only applicable for Android platforms.
+#CONFIG_CTRL_IFACE_BINDER=y
+
 # Add support for loading EAP methods dynamically as shared libraries.
 # When this option is enabled, each EAP method can be either included
 # statically (CONFIG_EAP_<method>=y) or dynamically (CONFIG_EAP_<method>=dyn).
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index fd5b03f..b133d03 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -1366,6 +1366,58 @@
 #endif /* CONFIG_CTRL_IFACE */
 
 
+int wpas_ap_pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf,
+			     size_t len)
+{
+	size_t reply_len = 0, i;
+	char ap_delimiter[] = "---- AP ----\n";
+	char mesh_delimiter[] = "---- mesh ----\n";
+	size_t dlen;
+
+	if (wpa_s->ap_iface) {
+		dlen = os_strlen(ap_delimiter);
+		if (dlen > len - reply_len)
+			return reply_len;
+		os_memcpy(&buf[reply_len], ap_delimiter, dlen);
+		reply_len += dlen;
+
+		for (i = 0; i < wpa_s->ap_iface->num_bss; i++) {
+			reply_len += hostapd_ctrl_iface_pmksa_list(
+				wpa_s->ap_iface->bss[i],
+				&buf[reply_len], len - reply_len);
+		}
+	}
+
+	if (wpa_s->ifmsh) {
+		dlen = os_strlen(mesh_delimiter);
+		if (dlen > len - reply_len)
+			return reply_len;
+		os_memcpy(&buf[reply_len], mesh_delimiter, dlen);
+		reply_len += dlen;
+
+		reply_len += hostapd_ctrl_iface_pmksa_list(
+			wpa_s->ifmsh->bss[0], &buf[reply_len],
+			len - reply_len);
+	}
+
+	return reply_len;
+}
+
+
+void wpas_ap_pmksa_cache_flush(struct wpa_supplicant *wpa_s)
+{
+	size_t i;
+
+	if (wpa_s->ap_iface) {
+		for (i = 0; i < wpa_s->ap_iface->num_bss; i++)
+			hostapd_ctrl_iface_pmksa_flush(wpa_s->ap_iface->bss[i]);
+	}
+
+	if (wpa_s->ifmsh)
+		hostapd_ctrl_iface_pmksa_flush(wpa_s->ifmsh->bss[0]);
+}
+
+
 #ifdef NEED_AP_MLME
 void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s,
 				   struct dfs_event *radar)
diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
index 2b8a1d4..5a59ddc 100644
--- a/wpa_supplicant/ap.h
+++ b/wpa_supplicant/ap.h
@@ -82,6 +82,10 @@
 
 int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s);
 
+int wpas_ap_pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf,
+			     size_t len);
+void wpas_ap_pmksa_cache_flush(struct wpa_supplicant *wpa_s);
+
 void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s,
 				   struct dfs_event *radar);
 void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s,
diff --git a/wpa_supplicant/binder/binder.cpp b/wpa_supplicant/binder/binder.cpp
new file mode 100644
index 0000000..28f7a2b
--- /dev/null
+++ b/wpa_supplicant/binder/binder.cpp
@@ -0,0 +1,108 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+
+#include "binder_manager.h"
+
+extern "C" {
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "binder.h"
+#include "binder_i.h"
+}
+
+void wpas_binder_sock_handler(int sock, void *eloop_ctx, void *sock_ctx)
+{
+	struct wpa_global *global = (wpa_global *) eloop_ctx;
+	struct wpas_binder_priv *priv = (wpas_binder_priv *) sock_ctx;
+
+	wpa_printf(MSG_DEBUG, "Processing binder events on FD %d",
+		   priv->binder_fd);
+	android::IPCThreadState::self()->handlePolledCommands();
+}
+
+
+struct wpas_binder_priv * wpas_binder_init(struct wpa_global *global)
+{
+	struct wpas_binder_priv *priv;
+	wpa_supplicant_binder::BinderManager *binder_manager;
+
+	priv = (wpas_binder_priv *) os_zalloc(sizeof(*priv));
+	if (!priv)
+		return NULL;
+	priv->global = global;
+
+	android::ProcessState::self()->setThreadPoolMaxThreadCount(0);
+	android::IPCThreadState::self()->disableBackgroundScheduling(true);
+	android::IPCThreadState::self()->setupPolling(&priv->binder_fd);
+	wpa_printf(MSG_INFO, "Process binder events on FD %d", priv->binder_fd);
+	if (priv->binder_fd < 0)
+		goto err;
+	/* Look for read events from the binder socket in the eloop. */
+	if (eloop_register_read_sock(priv->binder_fd, wpas_binder_sock_handler,
+				     global, priv) < 0)
+		goto err;
+
+	binder_manager = wpa_supplicant_binder::BinderManager::getInstance();
+	if (!binder_manager)
+		goto err;
+	binder_manager->registerBinderService(global);
+	/* We may not need to store this binder manager reference in the
+	 * global data strucure because we've made it a singleton class. */
+	priv->binder_manager = (void *) binder_manager;
+
+	return priv;
+
+err:
+	wpas_binder_deinit(priv);
+	return NULL;
+}
+
+
+void wpas_binder_deinit(struct wpas_binder_priv *priv)
+{
+	if (!priv)
+		return;
+
+	wpa_supplicant_binder::BinderManager::destroyInstance();
+	eloop_unregister_read_sock(priv->binder_fd);
+	android::IPCThreadState::shutdown();
+}
+
+
+int wpas_binder_register_interface(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->global->binder)
+		return 1;
+
+	wpa_supplicant_binder::BinderManager *binder_manager =
+		wpa_supplicant_binder::BinderManager::getInstance();
+	if (!binder_manager)
+		return 1;
+
+	return binder_manager->registerInterface(wpa_s);
+}
+
+
+int wpas_binder_unregister_interface(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->global->binder)
+		return 1;
+
+	wpa_supplicant_binder::BinderManager *binder_manager =
+		wpa_supplicant_binder::BinderManager::getInstance();
+	if (!binder_manager)
+		return 1;
+
+	return binder_manager->unregisterInterface(wpa_s);
+}
diff --git a/wpa_supplicant/binder/binder.h b/wpa_supplicant/binder/binder.h
new file mode 100644
index 0000000..a165074
--- /dev/null
+++ b/wpa_supplicant/binder/binder.h
@@ -0,0 +1,46 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BINDER_H
+#define BINDER_H
+
+#ifdef _cplusplus
+extern "C" {
+#endif /* _cplusplus */
+
+/**
+ * This is the binder RPC interface entry point to the wpa_supplicant core.
+ * This initializes the binder driver & BinderManager instance and then forwards
+ * all the notifcations from the supplicant core to the BinderManager.
+ */
+struct wpas_binder_priv;
+struct wpa_global;
+
+struct wpas_binder_priv * wpas_binder_init(struct wpa_global *global);
+void wpas_binder_deinit(struct wpas_binder_priv *priv);
+
+#ifdef CONFIG_CTRL_IFACE_BINDER
+int wpas_binder_register_interface(struct wpa_supplicant *wpa_s);
+int wpas_binder_unregister_interface(struct wpa_supplicant *wpa_s);
+#else /* CONFIG_CTRL_IFACE_BINDER */
+static inline int wpas_binder_register_interface(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+static inline int wpas_binder_unregister_interface(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+#endif /* CONFIG_CTRL_IFACE_BINDER */
+
+#ifdef _cplusplus
+}
+#endif /* _cplusplus */
+
+#endif /* BINDER_H */
diff --git a/wpa_supplicant/binder/binder_i.h b/wpa_supplicant/binder/binder_i.h
new file mode 100644
index 0000000..e8087b6
--- /dev/null
+++ b/wpa_supplicant/binder/binder_i.h
@@ -0,0 +1,27 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BINDER_I_H
+#define BINDER_I_H
+
+#ifdef _cplusplus
+extern "C" {
+#endif // _cplusplus
+
+struct wpas_binder_priv {
+	int binder_fd;
+	struct wpa_global *global;
+	void *binder_manager;
+};
+
+#ifdef _cplusplus
+}
+#endif /* _cplusplus */
+
+#endif /* BINDER_I_H */
diff --git a/wpa_supplicant/binder/binder_manager.cpp b/wpa_supplicant/binder/binder_manager.cpp
new file mode 100644
index 0000000..728f4b7
--- /dev/null
+++ b/wpa_supplicant/binder/binder_manager.cpp
@@ -0,0 +1,107 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <binder/IServiceManager.h>
+
+#include "binder_manager.h"
+
+extern "C" {
+#include "utils/includes.h"
+#include "utils/common.h"
+}
+
+namespace wpa_supplicant_binder {
+
+const char BinderManager::kBinderServiceName[] = "fi.w1.wpa_supplicant";
+BinderManager *BinderManager::instance_ = NULL;
+
+
+BinderManager * BinderManager::getInstance()
+{
+	if (!instance_)
+		instance_ = new BinderManager();
+	return instance_;
+}
+
+
+void BinderManager::destroyInstance()
+{
+	if (instance_)
+		delete instance_;
+	instance_ = NULL;
+}
+
+
+int BinderManager::registerBinderService(struct wpa_global *global)
+{
+	/* Create the main binder service object and register with
+	 * system service manager. */
+	supplicant_object_ = new Supplicant(global);
+	android::String16 service_name(kBinderServiceName);
+	android::defaultServiceManager()->addService(
+		service_name,
+		android::IInterface::asBinder(supplicant_object_));
+	return 0;
+}
+
+
+int BinderManager::registerInterface(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return 1;
+
+	/* Using the corresponding wpa_supplicant pointer as key to our
+	 * object map. */
+	const void *iface_key = wpa_s;
+
+	/* Return failure if we already have an object for that iface_key. */
+	if (iface_object_map_.find(iface_key) != iface_object_map_.end())
+		return 1;
+
+	iface_object_map_[iface_key] = new Iface(wpa_s);
+	if (!iface_object_map_[iface_key].get())
+		return 1;
+
+	wpa_s->binder_object_key = iface_key;
+
+	return 0;
+}
+
+
+int BinderManager::unregisterInterface(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s || !wpa_s->binder_object_key)
+		return 1;
+
+	const void *iface_key = wpa_s;
+	if (iface_object_map_.find(iface_key) == iface_object_map_.end())
+		return 1;
+
+	/* Delete the corresponding iface object from our map. */
+	iface_object_map_.erase(iface_key);
+	wpa_s->binder_object_key = NULL;
+	return 0;
+}
+
+
+int BinderManager::getIfaceBinderObjectByKey(
+	const void *iface_object_key,
+	android::sp<fi::w1::wpa_supplicant::IIface> *iface_object)
+{
+	if (!iface_object_key || !iface_object)
+		return 1;
+
+	if (iface_object_map_.find(iface_object_key) == iface_object_map_.end())
+		return 1;
+
+	*iface_object = iface_object_map_[iface_object_key];
+	return 0;
+}
+
+} /* namespace wpa_supplicant_binder */
diff --git a/wpa_supplicant/binder/binder_manager.h b/wpa_supplicant/binder/binder_manager.h
new file mode 100644
index 0000000..687e740
--- /dev/null
+++ b/wpa_supplicant/binder/binder_manager.h
@@ -0,0 +1,59 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BINDER_MANAGER_H
+#define BINDER_MANAGER_H
+
+#include <map>
+#include <string>
+
+#include "supplicant.h"
+#include "iface.h"
+
+struct wpa_global;
+struct wpa_supplicant;
+
+namespace wpa_supplicant_binder {
+
+/**
+ * BinderManager is responsible for managing the lifetime of all
+ * binder objects created by wpa_supplicant. This is a singleton
+ * class which is created by the supplicant core and can be used
+ * to get references to the binder objects.
+ */
+class BinderManager {
+public:
+	static const char kBinderServiceName[];
+
+	static BinderManager * getInstance();
+	static void destroyInstance();
+	int registerBinderService(struct wpa_global *global);
+	int registerInterface(struct wpa_supplicant *wpa_s);
+	int unregisterInterface(struct wpa_supplicant *wpa_s);
+	int getIfaceBinderObjectByKey(
+		const void *iface_object_key,
+		android::sp<fi::w1::wpa_supplicant::IIface> *iface_object);
+
+private:
+	BinderManager() = default;
+	~BinderManager() = default;
+
+	/* Singleton instance of this class. */
+	static BinderManager *instance_;
+	/* The main binder service object. */
+	android::sp<Supplicant> supplicant_object_;
+	/* Map of all the interface specific binder objects controlled by
+	 * wpa_supplicant. This map is keyed in by the corresponding
+	 * wpa_supplicant structure pointer. */
+	std::map<const void *, android::sp<Iface>> iface_object_map_;
+};
+
+} /* namespace wpa_supplicant_binder */
+
+#endif /* BINDER_MANAGER_H */
diff --git a/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl b/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl
new file mode 100644
index 0000000..ea11d42
--- /dev/null
+++ b/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl
@@ -0,0 +1,16 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package fi.w1.wpa_supplicant;
+
+/**
+ * Interface exposed by wpa_supplicant for each network interface it controls.
+ */
+interface IIface {
+}
diff --git a/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicant.aidl b/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicant.aidl
new file mode 100644
index 0000000..1cbee20
--- /dev/null
+++ b/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicant.aidl
@@ -0,0 +1,59 @@
+/*
+ * WPA Supplicant - binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package fi.w1.wpa_supplicant;
+
+import android.os.PersistableBundle;
+import fi.w1.wpa_supplicant.IIface;
+
+/**
+ * Interface exposed by the wpa_supplicant binder service registered
+ * with the service manager with name: fi.w1.wpa_supplicant.
+ */
+interface ISupplicant {
+	/* Error values returned by the service to RPC method calls. */
+	const int ERROR_INVALID_ARGS = 1;
+	const int ERROR_UNKNOWN = 2;
+	const int ERROR_IFACE_EXISTS = 3;
+	const int ERROR_IFACE_UNKNOWN = 4;
+
+	/**
+	 * Registers a wireless interface in wpa_supplicant.
+	 *
+	 * @param args A dictionary with arguments used to add the interface to
+	 *             wpa_supplicant.
+	 * The dictionary may contain the following entries:
+	 *   Ifname(String) Name of the network interface to control, e.g.,
+	 *   wlan0.
+	 *   BridgeIfname(String) Name of the bridge interface to control, e.g.,
+	 *   br0.
+	 *   Driver(String) Driver name which the interface uses, e.g., nl80211.
+	 *   ConfigFile(String) Configuration file path.
+	 *
+	 * @return Binder object representing the interface.
+	 */
+	IIface CreateInterface(in PersistableBundle args);
+
+	/**
+	 * Deregisters a wireless interface from wpa_supplicant.
+	 *
+	 * @param ifname Name of the network interface, e.g., wlan0
+	 */
+	void RemoveInterface(in @utf8InCpp String ifname);
+
+	/**
+	 * Gets a binder object for the interface corresponding to ifname
+	 * which wpa_supplicant already controls.
+	 *
+	 * @param ifname Name of the network interface, e.g., wlan0
+	 *
+	 * @return Binder object representing the interface.
+	 */
+	IIface GetInterface(in @utf8InCpp String ifname);
+}
diff --git a/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl b/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl
new file mode 100644
index 0000000..d624d91
--- /dev/null
+++ b/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl
@@ -0,0 +1,20 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package fi.w1.wpa_supplicant;
+
+import android.os.PersistableBundle;
+
+/**
+ * Callback Interface exposed by the wpa_supplicant service. Clients need
+ * to host an instance of this binder object and pass a reference of the object
+ * to wpa_supplicant via the registerCallbacksObject method.
+ */
+interface ISupplicantCallbacks {
+}
diff --git a/wpa_supplicant/binder/iface.cpp b/wpa_supplicant/binder/iface.cpp
new file mode 100644
index 0000000..af2548d
--- /dev/null
+++ b/wpa_supplicant/binder/iface.cpp
@@ -0,0 +1,19 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "iface.h"
+
+namespace wpa_supplicant_binder {
+
+Iface::Iface(struct wpa_supplicant *wpa_s)
+	: wpa_s_(wpa_s)
+{
+}
+
+} /* namespace wpa_supplicant_binder */
diff --git a/wpa_supplicant/binder/iface.h b/wpa_supplicant/binder/iface.h
new file mode 100644
index 0000000..acc8e55
--- /dev/null
+++ b/wpa_supplicant/binder/iface.h
@@ -0,0 +1,42 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IFACE_H
+#define IFACE_H
+
+#include "fi/w1/wpa_supplicant/BnIface.h"
+
+extern "C" {
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "../wpa_supplicant_i.h"
+}
+
+namespace wpa_supplicant_binder {
+
+/**
+ * Implementation of Iface binder object. Each unique binder
+ * object is used for control operations on a specific interface
+ * controlled by wpa_supplicant.
+ */
+class Iface : public fi::w1::wpa_supplicant::BnIface
+{
+public:
+	Iface(struct wpa_supplicant *wpa_s);
+	virtual ~Iface() = default;
+
+private:
+	/* Raw pointer to the structure maintained by the core for this
+	 * interface. */
+	struct wpa_supplicant *wpa_s_;
+};
+
+} /* namespace wpa_supplicant_binder */
+
+#endif /* IFACE_H */
diff --git a/wpa_supplicant/binder/supplicant.cpp b/wpa_supplicant/binder/supplicant.cpp
new file mode 100644
index 0000000..6844e5a
--- /dev/null
+++ b/wpa_supplicant/binder/supplicant.cpp
@@ -0,0 +1,125 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "binder_manager.h"
+#include "supplicant.h"
+
+namespace wpa_supplicant_binder {
+
+Supplicant::Supplicant(struct wpa_global *global)
+	: wpa_global_(global)
+{
+}
+
+
+android::binder::Status Supplicant::CreateInterface(
+	const android::os::PersistableBundle &params,
+	android::sp<fi::w1::wpa_supplicant::IIface> *aidl_return)
+{
+	android::String16 driver, ifname, confname, bridge_ifname;
+
+	/* Check if required Ifname argument is missing */
+	if (!params.getString(android::String16("Ifname"), &ifname))
+		return android::binder::Status::fromServiceSpecificError(
+			ERROR_INVALID_ARGS,
+			android::String8("Ifname missing in params."));
+	/* Retrieve the remaining params from the dictionary */
+	params.getString(android::String16("Driver"), &driver);
+	params.getString(android::String16("ConfigFile"), &confname);
+	params.getString(android::String16("BridgeIfname"), &bridge_ifname);
+
+	/*
+	 * Try to get the wpa_supplicant record for this iface, return
+	 * an error if we already control it.
+	 */
+	if (wpa_supplicant_get_iface(wpa_global_,
+				     android::String8(ifname).string()) != NULL)
+		return android::binder::Status::fromServiceSpecificError(
+			ERROR_IFACE_EXISTS,
+			android::String8("wpa_supplicant already controls this interface."));
+
+	android::binder::Status status;
+	struct wpa_supplicant *wpa_s = NULL;
+	struct wpa_interface iface;
+
+	os_memset(&iface, 0, sizeof(iface));
+	iface.driver = os_strdup(android::String8(driver).string());
+	iface.ifname = os_strdup(android::String8(ifname).string());
+	iface.confname = os_strdup(android::String8(confname).string());
+	iface.bridge_ifname = os_strdup(
+		android::String8(bridge_ifname).string());
+	/* Otherwise, have wpa_supplicant attach to it. */
+	wpa_s = wpa_supplicant_add_iface(wpa_global_, &iface, NULL);
+	/* The supplicant core creates a corresponding binder object via
+	 * BinderManager when |wpa_supplicant_add_iface| is called. */
+	if (!wpa_s || !wpa_s->binder_object_key) {
+		status = android::binder::Status::fromServiceSpecificError(
+			ERROR_UNKNOWN,
+			android::String8("wpa_supplicant couldn't grab this interface."));
+	} else {
+		BinderManager *binder_manager = BinderManager::getInstance();
+
+		if (!binder_manager ||
+		    binder_manager->getIfaceBinderObjectByKey(
+			    wpa_s->binder_object_key, aidl_return))
+			status = android::binder::Status::fromServiceSpecificError(
+				ERROR_UNKNOWN,
+				android::String8("wpa_supplicant encountered a binder error."));
+		else
+			status = android::binder::Status::ok();
+	}
+	os_free((void *) iface.driver);
+	os_free((void *) iface.ifname);
+	os_free((void *) iface.confname);
+	os_free((void *) iface.bridge_ifname);
+	return status;
+}
+
+
+android::binder::Status Supplicant::RemoveInterface(const std::string &ifname)
+{
+	struct wpa_supplicant *wpa_s;
+
+	wpa_s = wpa_supplicant_get_iface(wpa_global_, ifname.c_str());
+	if (!wpa_s || !wpa_s->binder_object_key)
+		return android::binder::Status::fromServiceSpecificError(
+			ERROR_IFACE_UNKNOWN,
+			android::String8("wpa_supplicant does not control this interface."));
+	if (wpa_supplicant_remove_iface(wpa_global_, wpa_s, 0))
+		return android::binder::Status::fromServiceSpecificError(
+			ERROR_UNKNOWN,
+			android::String8("wpa_supplicant couldn't remove this interface."));
+	return android::binder::Status::ok();
+}
+
+
+android::binder::Status Supplicant::GetInterface(
+	const std::string &ifname,
+	android::sp<fi::w1::wpa_supplicant::IIface> *aidl_return)
+{
+	struct wpa_supplicant *wpa_s;
+
+	wpa_s = wpa_supplicant_get_iface(wpa_global_, ifname.c_str());
+	if (!wpa_s || !wpa_s->binder_object_key)
+		return android::binder::Status::fromServiceSpecificError(
+			ERROR_IFACE_UNKNOWN,
+			android::String8("wpa_supplicant does not control this interface."));
+
+	BinderManager *binder_manager = BinderManager::getInstance();
+	if (!binder_manager ||
+	    binder_manager->getIfaceBinderObjectByKey(wpa_s->binder_object_key,
+						      aidl_return))
+		return android::binder::Status::fromServiceSpecificError(
+			ERROR_UNKNOWN,
+			android::String8("wpa_supplicant encountered a binder error."));
+
+	return android::binder::Status::ok();
+}
+
+} /* namespace wpa_supplicant_binder */
diff --git a/wpa_supplicant/binder/supplicant.h b/wpa_supplicant/binder/supplicant.h
new file mode 100644
index 0000000..b96f4e6
--- /dev/null
+++ b/wpa_supplicant/binder/supplicant.h
@@ -0,0 +1,57 @@
+/*
+ * binder interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SUPPLICANT_H
+#define SUPPLICANT_H
+
+#include "fi/w1/wpa_supplicant/BnSupplicant.h"
+#include "fi/w1/wpa_supplicant/IIface.h"
+#include "fi/w1/wpa_supplicant/ISupplicantCallbacks.h"
+
+extern "C" {
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "../wpa_supplicant_i.h"
+}
+
+namespace wpa_supplicant_binder {
+
+/**
+ * Implementation of the supplicant binder object. This binder
+ * object is used core for global control operations on
+ * wpa_supplicant.
+ */
+class Supplicant : public fi::w1::wpa_supplicant::BnSupplicant
+{
+public:
+	Supplicant(struct wpa_global *global);
+	virtual ~Supplicant() = default;
+
+	android::binder::Status CreateInterface(
+		const android::os::PersistableBundle &params,
+		android::sp<fi::w1::wpa_supplicant::IIface> *aidl_return)
+		override;
+	android::binder::Status RemoveInterface(
+		const std::string &ifname) override;
+	android::binder::Status GetInterface(
+		const std::string &ifname,
+		android::sp<fi::w1::wpa_supplicant::IIface> *aidl_return)
+		override;
+
+private:
+	/* Raw pointer to the global structure maintained by the core. */
+	struct wpa_global *wpa_global_;
+	/* All the callback objects registered by the clients. */
+	std::vector<android::sp<fi::w1::wpa_supplicant::ISupplicantCallbacks>>
+		callbacks_;
+};
+
+} /* namespace wpa_supplicant_binder */
+
+#endif /* SUPPLICANT_H */
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 796977b..4376676 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -32,7 +32,11 @@
 	/* Configuration variable name */
 	char *name;
 
-	/* Parser function for this variable */
+	/* Parser function for this variable. The parser functions return 0 or 1
+	 * to indicate success. Value 0 indicates that the parameter value may
+	 * have changed while value 1 means that the value did not change.
+	 * Error cases (failure to parse the string) are indicated by returning
+	 * -1. */
 	int (*parser)(const struct parse_data *data, struct wpa_ssid *ssid,
 		      int line, const char *value);
 
@@ -59,7 +63,7 @@
 				struct wpa_ssid *ssid,
 				int line, const char *value)
 {
-	size_t res_len, *dst_len;
+	size_t res_len, *dst_len, prev_len;
 	char **dst, *tmp;
 
 	if (os_strcmp(value, "NULL") == 0) {
@@ -105,6 +109,21 @@
 set:
 	dst = (char **) (((u8 *) ssid) + (long) data->param1);
 	dst_len = (size_t *) (((u8 *) ssid) + (long) data->param2);
+
+	if (data->param2)
+		prev_len = *dst_len;
+	else if (*dst)
+		prev_len = os_strlen(*dst);
+	else
+		prev_len = 0;
+	if ((*dst == NULL && tmp == NULL) ||
+	    (*dst && tmp && prev_len == res_len &&
+	     os_memcmp(*dst, tmp, res_len) == 0)) {
+		/* No change to the previously configured value */
+		os_free(tmp);
+		return 1;
+	}
+
 	os_free(*dst);
 	*dst = tmp;
 	if (data->param2)
@@ -190,6 +209,9 @@
 			   line, value);
 		return -1;
 	}
+
+	if (*dst == val)
+		return 1;
 	*dst = val;
 	wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst);
 
@@ -457,8 +479,10 @@
 		wpa_hexdump_ascii_key(MSG_MSGDUMP, "PSK (ASCII passphrase)",
 				      (u8 *) value, len);
 		if (ssid->passphrase && os_strlen(ssid->passphrase) == len &&
-		    os_memcmp(ssid->passphrase, value, len) == 0)
-			return 0;
+		    os_memcmp(ssid->passphrase, value, len) == 0) {
+			/* No change to the previously configured value */
+			return 1;
+		}
 		ssid->psk_set = 0;
 		str_clear_free(ssid->passphrase);
 		ssid->passphrase = dup_binstr(value, len);
@@ -569,6 +593,8 @@
 		errors++;
 	}
 
+	if (!errors && ssid->proto == val)
+		return 1;
 	wpa_printf(MSG_MSGDUMP, "proto: 0x%x", val);
 	ssid->proto = val;
 	return errors ? -1 : 0;
@@ -705,6 +731,8 @@
 		errors++;
 	}
 
+	if (!errors && ssid->key_mgmt == val)
+		return 1;
 	wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", val);
 	ssid->key_mgmt = val;
 	return errors ? -1 : 0;
@@ -953,6 +981,8 @@
 		return -1;
 	}
 
+	if (ssid->pairwise_cipher == val)
+		return 1;
 	wpa_printf(MSG_MSGDUMP, "pairwise: 0x%x", val);
 	ssid->pairwise_cipher = val;
 	return 0;
@@ -989,6 +1019,8 @@
 		return -1;
 	}
 
+	if (ssid->group_cipher == val)
+		return 1;
 	wpa_printf(MSG_MSGDUMP, "group: 0x%x", val);
 	ssid->group_cipher = val;
 	return 0;
@@ -1050,6 +1082,8 @@
 		errors++;
 	}
 
+	if (!errors && ssid->auth_alg == val)
+		return 1;
 	wpa_printf(MSG_MSGDUMP, "auth_alg: 0x%x", val);
 	ssid->auth_alg = val;
 	return errors ? -1 : 0;
@@ -1304,6 +1338,32 @@
 	methods[num_methods].method = EAP_TYPE_NONE;
 	num_methods++;
 
+	if (!errors && ssid->eap.eap_methods) {
+		struct eap_method_type *prev_m;
+		size_t i, j, prev_methods, match = 0;
+
+		prev_m = ssid->eap.eap_methods;
+		for (i = 0; prev_m[i].vendor != EAP_VENDOR_IETF ||
+			     prev_m[i].method != EAP_TYPE_NONE; i++) {
+			/* Count the methods */
+		}
+		prev_methods = i + 1;
+
+		for (i = 0; prev_methods == num_methods && i < prev_methods;
+		     i++) {
+			for (j = 0; j < num_methods; j++) {
+				if (prev_m[i].vendor == methods[j].vendor &&
+				    prev_m[i].method == methods[j].method) {
+					match++;
+					break;
+				}
+			}
+		}
+		if (match == num_methods) {
+			os_free(methods);
+			return 1;
+		}
+	}
 	wpa_hexdump(MSG_MSGDUMP, "eap methods",
 		    (u8 *) methods, num_methods * sizeof(*methods));
 	os_free(ssid->eap.eap_methods);
@@ -1356,6 +1416,8 @@
 	u8 *hash;
 
 	if (os_strcmp(value, "NULL") == 0) {
+		if (!ssid->eap.password)
+			return 1; /* Already unset */
 		wpa_printf(MSG_DEBUG, "Unset configuration string 'password'");
 		bin_clear_free(ssid->eap.password, ssid->eap.password_len);
 		ssid->eap.password = NULL;
@@ -1419,6 +1481,12 @@
 
 	wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16);
 
+	if (ssid->eap.password && ssid->eap.password_len == 16 &&
+	    os_memcmp(ssid->eap.password, hash, 16) == 0 &&
+	    (ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) {
+		bin_clear_free(hash, 16);
+		return 1;
+	}
 	bin_clear_free(ssid->eap.password, ssid->eap.password_len);
 	ssid->eap.password = hash;
 	ssid->eap.password_len = 16;
@@ -2472,7 +2540,8 @@
  * @var: Variable name, e.g., "ssid"
  * @value: Variable value
  * @line: Line number in configuration file or 0 if not used
- * Returns: 0 on success, -1 on failure
+ * Returns: 0 on success with possible change in the value, 1 on success with
+ * no change to previously configured value, or -1 on failure
  *
  * This function can be used to set network configuration variables based on
  * both the configuration file and management interface input. The value
@@ -2493,7 +2562,8 @@
 		if (os_strcmp(var, field->name) != 0)
 			continue;
 
-		if (field->parser(field, ssid, line, value)) {
+		ret = field->parser(field, ssid, line, value);
+		if (ret < 0) {
 			if (line) {
 				wpa_printf(MSG_ERROR, "Line %d: failed to "
 					   "parse %s '%s'.", line, var, value);
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 4e16987..8574437 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -2727,6 +2727,40 @@
 	return 0;
 }
 
+
+static int wpa_supplicant_ctrl_iface_mesh_peer_remove(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 addr[ETH_ALEN];
+
+	if (hwaddr_aton(cmd, addr) < 0)
+		return -1;
+
+	return wpas_mesh_peer_remove(wpa_s, addr);
+}
+
+
+static int wpa_supplicant_ctrl_iface_mesh_peer_add(
+	struct wpa_supplicant *wpa_s, char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	int duration;
+	char *pos;
+
+	pos = os_strstr(cmd, " duration=");
+	if (pos) {
+		*pos = '\0';
+		duration = atoi(pos + 10);
+	} else {
+		duration = -1;
+	}
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+
+	return wpas_mesh_peer_add(wpa_s, addr, duration);
+}
+
 #endif /* CONFIG_MESH */
 
 
@@ -2969,22 +3003,29 @@
 	struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 	char *name, char *value)
 {
-	if (wpa_config_set(ssid, name, value, 0) < 0) {
+	int ret;
+
+	ret = wpa_config_set(ssid, name, value, 0);
+	if (ret < 0) {
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network "
 			   "variable '%s'", name);
 		return -1;
 	}
+	if (ret == 1)
+		return 0; /* No change to the previously configured value */
 
 	if (os_strcmp(name, "bssid") != 0 &&
-	    os_strcmp(name, "priority") != 0)
+	    os_strcmp(name, "priority") != 0) {
 		wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
 
-	if (wpa_s->current_ssid == ssid || wpa_s->current_ssid == NULL) {
-		/*
-		 * Invalidate the EAP session cache if anything in the current
-		 * or previously used configuration changes.
-		 */
-		eapol_sm_invalidate_cached_session(wpa_s->eapol);
+		if (wpa_s->current_ssid == ssid ||
+		    wpa_s->current_ssid == NULL) {
+			/*
+			 * Invalidate the EAP session cache if anything in the
+			 * current or previously used configuration changes.
+			 */
+			eapol_sm_invalidate_cached_session(wpa_s->eapol);
+		}
 	}
 
 	if ((os_strcmp(name, "psk") == 0 &&
@@ -8328,6 +8369,29 @@
 }
 
 
+static int wpas_ctrl_iface_pmksa(struct wpa_supplicant *wpa_s,
+				 char *buf, size_t buflen)
+{
+	size_t reply_len;
+
+	reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, buf, buflen);
+#ifdef CONFIG_AP
+	reply_len += wpas_ap_pmksa_cache_list(wpa_s, &buf[reply_len],
+					      buflen - reply_len);
+#endif /* CONFIG_AP */
+	return reply_len;
+}
+
+
+static void wpas_ctrl_iface_pmksa_flush(struct wpa_supplicant *wpa_s)
+{
+	wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
+#ifdef CONFIG_AP
+	wpas_ap_pmksa_cache_flush(wpa_s);
+#endif /* CONFIG_AP */
+}
+
+
 static int wpas_ctrl_cmd_debug_level(const char *cmd)
 {
 	if (os_strcmp(cmd, "PING") == 0 ||
@@ -8399,10 +8463,9 @@
 		reply_len = wpa_supplicant_ctrl_iface_status(
 			wpa_s, buf + 6, reply, reply_size);
 	} else if (os_strcmp(buf, "PMKSA") == 0) {
-		reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, reply,
-						    reply_size);
+		reply_len = wpas_ctrl_iface_pmksa(wpa_s, reply, reply_size);
 	} else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
-		wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
+		wpas_ctrl_iface_pmksa_flush(wpa_s);
 	} else if (os_strncmp(buf, "SET ", 4) == 0) {
 		if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4))
 			reply_len = -1;
@@ -8570,6 +8633,12 @@
 		if (wpa_supplicant_ctrl_iface_mesh_group_remove(wpa_s,
 								buf + 18))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "MESH_PEER_REMOVE ", 17) == 0) {
+		if (wpa_supplicant_ctrl_iface_mesh_peer_remove(wpa_s, buf + 17))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "MESH_PEER_ADD ", 14) == 0) {
+		if (wpa_supplicant_ctrl_iface_mesh_peer_add(wpa_s, buf + 14))
+			reply_len = -1;
 #endif /* CONFIG_MESH */
 #ifdef CONFIG_P2P
 	} else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) {
diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c
index 503052a..0dc0937 100644
--- a/wpa_supplicant/ctrl_iface_udp.c
+++ b/wpa_supplicant/ctrl_iface_udp.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / UDP socket -based control interface
- * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -378,6 +378,7 @@
 wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
 {
 	struct ctrl_iface_priv *priv;
+	char port_str[40];
 	int port = WPA_CTRL_IFACE_PORT;
 	char *pos;
 #ifdef CONFIG_CTRL_IFACE_UDP_IPV6
@@ -439,13 +440,21 @@
 #endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
 		port--;
-		if ((WPA_CTRL_IFACE_PORT - port) < WPA_CTRL_IFACE_PORT_LIMIT &&
-		    !pos)
+		if ((WPA_CTRL_IFACE_PORT - port) < WPA_CTRL_IFACE_PORT_LIMIT)
 			goto try_again;
 		wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
 		goto fail;
 	}
 
+	/* Update the ctrl_interface value to match the selected port */
+	os_snprintf(port_str, sizeof(port_str), "udp:%d", port);
+	os_free(wpa_s->conf->ctrl_interface);
+	wpa_s->conf->ctrl_interface = os_strdup(port_str);
+	if (!wpa_s->conf->ctrl_interface) {
+		wpa_msg(wpa_s, MSG_ERROR, "Failed to malloc ctrl_interface");
+		goto fail;
+	}
+
 #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
 	wpa_msg(wpa_s, MSG_DEBUG, "ctrl_iface_init UDP port: %d", port);
 #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
@@ -593,7 +602,11 @@
 	struct ctrl_iface_global_priv *priv = sock_ctx;
 	char buf[256], *pos;
 	int res;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+	struct sockaddr_in6 from;
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	struct sockaddr_in from;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 	socklen_t fromlen = sizeof(from);
 	char *reply = NULL;
 	size_t reply_len;
@@ -608,6 +621,7 @@
 	}
 
 #ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
+#ifndef CONFIG_CTRL_IFACE_UDP_IPV6
 	if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
 		/*
 		 * The OS networking stack is expected to drop this kind of
@@ -619,6 +633,7 @@
 			   "source %s", inet_ntoa(from.sin_addr));
 		return;
 	}
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 
 	buf[res] = '\0';
diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c
index 2fc89a9..4db712f 100644
--- a/wpa_supplicant/ctrl_iface_unix.c
+++ b/wpa_supplicant/ctrl_iface_unix.c
@@ -15,7 +15,6 @@
 #include <fcntl.h>
 #ifdef __linux__
 #include <sys/ioctl.h>
-#include <linux/sockios.h>
 #endif /* __linux__ */
 #ifdef ANDROID
 #include <cutils/sockets.h>
@@ -90,7 +89,7 @@
 	if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, &optlen) < 0)
 		sndbuf = -1;
 
-	if (ioctl(sock, SIOCOUTQ, &outq) < 0)
+	if (ioctl(sock, TIOCOUTQ, &outq) < 0)
 		outq = -1;
 
 	wpa_printf(level,
@@ -289,7 +288,7 @@
 	optlen = sizeof(sndbuf);
 	sndbuf = 0;
 	if (getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, &optlen) < 0 ||
-	    ioctl(sock, SIOCOUTQ, &outq) < 0 ||
+	    ioctl(sock, TIOCOUTQ, &outq) < 0 ||
 	    sndbuf <= 0 || outq < 0)
 		return 0;
 	return outq > sndbuf / 2;
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 79632e6..1d05198 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -467,6 +467,9 @@
 # Hotspot 2.0
 #CONFIG_HS20=y
 
+# Enable interface matching in wpa_supplicant
+#CONFIG_MATCH_IFACE=y
+
 # Disable roaming in wpa_supplicant
 #CONFIG_NO_ROAMING=y
 
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index a84ba2d..6b3a2c0 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -1346,6 +1346,7 @@
 	struct wpa_bss *current_bss = NULL;
 #ifndef CONFIG_NO_ROAMING
 	int min_diff;
+	int to_5ghz;
 #endif /* CONFIG_NO_ROAMING */
 
 	if (wpa_s->reassociate)
@@ -1401,7 +1402,10 @@
 		return 1;
 	}
 
-	if (current_bss->level < 0 && current_bss->level > selected->level) {
+	to_5ghz = selected->freq > 4000 && current_bss->freq < 4000;
+
+	if (current_bss->level < 0 &&
+	    current_bss->level > selected->level + to_5ghz * 2) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better "
 			"signal level");
 		return 0;
@@ -1420,6 +1424,13 @@
 		else
 			min_diff = 5;
 	}
+	if (to_5ghz) {
+		/* Make it easier to move to 5 GHz band */
+		if (min_diff > 2)
+			min_diff -= 2;
+		else
+			min_diff = 0;
+	}
 	if (abs(current_bss->level - selected->level) < min_diff) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - too small difference "
 			"in signal level");
@@ -1984,6 +1995,8 @@
 		}
 		if ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
 		     (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) ||
+		    (p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 4 &&
+		     (os_memcmp(&p[2], "\x50\x6F\x9A\x12", 4) == 0)) ||
 		    (p[0] == WLAN_EID_RSN && p[1] >= 2)) {
 			if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len))
 				break;
@@ -2760,6 +2773,13 @@
 		}
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_MATCH_IFACE
+		if (wpa_s->matched) {
+			wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0);
+			break;
+		}
+#endif /* CONFIG_MATCH_IFACE */
+
 #ifdef CONFIG_TERMINATE_ONLASTIF
 		/* check if last interface */
 		if (!any_interfaces(wpa_s->global->ifaces))
@@ -3690,12 +3710,14 @@
 #endif /* CONFIG_AP */
 #ifdef CONFIG_P2P
 			if (stype == WLAN_FC_STYPE_PROBE_REQ &&
-			    data->rx_mgmt.frame_len > 24) {
+			    data->rx_mgmt.frame_len > IEEE80211_HDRLEN) {
 				const u8 *src = mgmt->sa;
-				const u8 *ie = mgmt->u.probe_req.variable;
-				size_t ie_len = data->rx_mgmt.frame_len -
-					(mgmt->u.probe_req.variable -
-					 data->rx_mgmt.frame);
+				const u8 *ie;
+				size_t ie_len;
+
+				ie = data->rx_mgmt.frame + IEEE80211_HDRLEN;
+				ie_len = data->rx_mgmt.frame_len -
+					IEEE80211_HDRLEN;
 				wpas_p2p_probe_req_rx(
 					wpa_s, src, mgmt->da,
 					mgmt->bssid, ie, ie_len,
@@ -3735,11 +3757,12 @@
 		}
 
 		if (stype == WLAN_FC_STYPE_PROBE_REQ &&
-		    data->rx_mgmt.frame_len > 24) {
-			const u8 *ie = mgmt->u.probe_req.variable;
-			size_t ie_len = data->rx_mgmt.frame_len -
-				(mgmt->u.probe_req.variable -
-				 data->rx_mgmt.frame);
+		    data->rx_mgmt.frame_len > IEEE80211_HDRLEN) {
+			const u8 *ie;
+			size_t ie_len;
+
+			ie = data->rx_mgmt.frame + IEEE80211_HDRLEN;
+			ie_len = data->rx_mgmt.frame_len - IEEE80211_HDRLEN;
 
 			wpas_notify_preq(wpa_s, mgmt->sa, mgmt->da,
 					 mgmt->bssid, ie, ie_len,
@@ -4010,3 +4033,43 @@
 		break;
 	}
 }
+
+
+void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
+				 union wpa_event_data *data)
+{
+	struct wpa_supplicant *wpa_s;
+
+	if (event != EVENT_INTERFACE_STATUS)
+		return;
+
+	wpa_s = wpa_supplicant_get_iface(ctx, data->interface_status.ifname);
+	if (wpa_s && wpa_s->driver->get_ifindex) {
+		unsigned int ifindex;
+
+		ifindex = wpa_s->driver->get_ifindex(wpa_s->drv_priv);
+		if (ifindex != data->interface_status.ifindex) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"interface status ifindex %d mismatch (%d)",
+				ifindex, data->interface_status.ifindex);
+			return;
+		}
+	}
+#ifdef CONFIG_MATCH_IFACE
+	else if (data->interface_status.ievent == EVENT_INTERFACE_ADDED) {
+		struct wpa_interface *wpa_i;
+
+		wpa_i = wpa_supplicant_match_iface(
+			ctx, data->interface_status.ifname);
+		if (!wpa_i)
+			return;
+		wpa_s = wpa_supplicant_add_iface(ctx, wpa_i, NULL);
+		os_free(wpa_i);
+		if (wpa_s)
+			wpa_s->matched = 1;
+	}
+#endif /* CONFIG_MATCH_IFACE */
+
+	if (wpa_s)
+		wpa_supplicant_event(wpa_s, event, data);
+}
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index 9df1607..589ee57 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -1451,7 +1451,24 @@
 		os_free(anon);
 	}
 
-	if (cred->username && cred->username[0] &&
+	if (!ttls && cred->username && cred->username[0] && cred->realm &&
+	    !os_strchr(cred->username, '@')) {
+		char *id;
+		size_t buflen;
+		int res;
+
+		buflen = os_strlen(cred->username) + 1 +
+			os_strlen(cred->realm) + 1;
+
+		id = os_malloc(buflen);
+		if (!id)
+			return -1;
+		os_snprintf(id, buflen, "%s@%s", cred->username, cred->realm);
+		res = wpa_config_set_quoted(ssid, "identity", id);
+		os_free(id);
+		if (res < 0)
+			return -1;
+	} else if (cred->username && cred->username[0] &&
 	    wpa_config_set_quoted(ssid, "identity", cred->username) < 0)
 		return -1;
 
diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c
index 3ab80a5..e08c2fd 100644
--- a/wpa_supplicant/main.c
+++ b/wpa_supplicant/main.c
@@ -81,6 +81,9 @@
 #ifdef CONFIG_P2P
 	       "  -m = Configuration file for the P2P Device interface\n"
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_MATCH_IFACE
+	       "  -M = start describing new matching interface\n"
+#endif /* CONFIG_MATCH_IFACE */
 	       "  -N = start describing new interface\n"
 	       "  -o = override driver parameter for new interfaces\n"
 	       "  -O = override ctrl_interface parameter for new interfaces\n"
@@ -153,6 +156,28 @@
 }
 
 
+#ifdef CONFIG_MATCH_IFACE
+static int wpa_supplicant_init_match(struct wpa_global *global)
+{
+	/*
+	 * The assumption is that the first driver is the primary driver and
+	 * will handle the arrival / departure of interfaces.
+	 */
+	if (wpa_drivers[0]->global_init && !global->drv_priv[0]) {
+		global->drv_priv[0] = wpa_drivers[0]->global_init(global);
+		if (!global->drv_priv[0]) {
+			wpa_printf(MSG_ERROR,
+				   "Failed to initialize driver '%s'",
+				   wpa_drivers[0]->name);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+#endif /* CONFIG_MATCH_IFACE */
+
+
 int main(int argc, char *argv[])
 {
 	int c, i;
@@ -176,7 +201,7 @@
 
 	for (;;) {
 		c = getopt(argc, argv,
-			   "b:Bc:C:D:de:f:g:G:hi:I:KLm:No:O:p:P:qsTtuvW");
+			   "b:Bc:C:D:de:f:g:G:hi:I:KLMm:No:O:p:P:qsTtuvW");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -282,6 +307,20 @@
 		case 'W':
 			params.wait_for_monitor++;
 			break;
+#ifdef CONFIG_MATCH_IFACE
+		case 'M':
+			params.match_iface_count++;
+			iface = os_realloc_array(params.match_ifaces,
+						 params.match_iface_count,
+						 sizeof(struct wpa_interface));
+			if (!iface)
+				goto out;
+			params.match_ifaces = iface;
+			iface = &params.match_ifaces[params.match_iface_count -
+						     1];
+			os_memset(iface, 0, sizeof(*iface));
+			break;
+#endif /* CONFIG_MATCH_IFACE */
 		case 'N':
 			iface_count++;
 			iface = os_realloc_array(ifaces, iface_count,
@@ -328,6 +367,9 @@
 		     ifaces[i].ctrl_interface == NULL) ||
 		    ifaces[i].ifname == NULL) {
 			if (iface_count == 1 && (params.ctrl_interface ||
+#ifdef CONFIG_MATCH_IFACE
+						 params.match_iface_count ||
+#endif /* CONFIG_MATCH_IFACE */
 						 params.dbus_ctrl_interface))
 				break;
 			usage();
@@ -341,6 +383,11 @@
 		}
 	}
 
+#ifdef CONFIG_MATCH_IFACE
+	if (exitcode == 0)
+		exitcode = wpa_supplicant_init_match(global);
+#endif /* CONFIG_MATCH_IFACE */
+
 	if (exitcode == 0)
 		exitcode = wpa_supplicant_run(global);
 
@@ -351,6 +398,9 @@
 out:
 	wpa_supplicant_fd_workaround(0);
 	os_free(ifaces);
+#ifdef CONFIG_MATCH_IFACE
+	os_free(params.match_ifaces);
+#endif /* CONFIG_MATCH_IFACE */
 	os_free(params.pid_file);
 
 	os_program_deinit();
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index 7925aa9..89b033b 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -597,3 +597,16 @@
 	mesh_wpa_s->mesh_if_created = 1;
 	return 0;
 }
+
+
+int wpas_mesh_peer_remove(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+	return mesh_mpm_close_peer(wpa_s, addr);
+}
+
+
+int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, const u8 *addr,
+		       int duration)
+{
+	return mesh_mpm_connect_peer(wpa_s, addr, duration);
+}
diff --git a/wpa_supplicant/mesh.h b/wpa_supplicant/mesh.h
index 3cb7f1b..7317083 100644
--- a/wpa_supplicant/mesh.h
+++ b/wpa_supplicant/mesh.h
@@ -18,6 +18,9 @@
 			       char *end);
 int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname,
 			    size_t len);
+int wpas_mesh_peer_remove(struct wpa_supplicant *wpa_s, const u8 *addr);
+int wpas_mesh_peer_add(struct wpa_supplicant *wpa_s, const u8 *addr,
+		       int duration);
 
 #ifdef CONFIG_MESH
 
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index 27be46c..c014eaf 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -14,6 +14,7 @@
 #include "ap/hostapd.h"
 #include "ap/sta_info.h"
 #include "ap/ieee802_11.h"
+#include "ap/wpa_auth.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "mesh_mpm.h"
@@ -419,6 +420,7 @@
 	struct sta_info *sta = user_data;
 	u16 reason = 0;
 	struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
 
 	switch (sta->plink_state) {
 	case PLINK_OPEN_RCVD:
@@ -448,6 +450,13 @@
 		break;
 	case PLINK_HOLDING:
 		/* holding timer */
+
+		if (sta->mesh_sae_pmksa_caching) {
+			wpa_printf(MSG_DEBUG, "MPM: Peer " MACSTR
+				   " looks like it does not support mesh SAE PMKSA caching, so remove the cached entry for it",
+				   MAC2STR(sta->addr));
+			wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+		}
 		mesh_mpm_fsm_restart(wpa_s, sta);
 		break;
 	default:
@@ -472,8 +481,8 @@
 }
 
 
-int mesh_mpm_plink_close(struct hostapd_data *hapd,
-			 struct sta_info *sta, void *ctx)
+static int mesh_mpm_plink_close(struct hostapd_data *hapd, struct sta_info *sta,
+				void *ctx)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	int reason = WLAN_REASON_MESH_PEERING_CANCELLED;
@@ -491,6 +500,85 @@
 }
 
 
+int mesh_mpm_close_peer(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+	struct hostapd_data *hapd;
+	struct sta_info *sta;
+
+	if (!wpa_s->ifmsh) {
+		wpa_msg(wpa_s, MSG_INFO, "Mesh is not prepared yet");
+		return -1;
+	}
+
+	hapd = wpa_s->ifmsh->bss[0];
+	sta = ap_get_sta(hapd, addr);
+	if (!sta) {
+		wpa_msg(wpa_s, MSG_INFO, "No such mesh peer");
+		return -1;
+	}
+
+	return mesh_mpm_plink_close(hapd, sta, wpa_s) == 0 ? 0 : -1;
+}
+
+
+static void peer_add_timer(void *eloop_ctx, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+
+	os_memset(hapd->mesh_required_peer, 0, ETH_ALEN);
+}
+
+
+int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+			  int duration)
+{
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	struct hostapd_data *hapd;
+	struct sta_info *sta;
+	struct mesh_conf *conf;
+
+	if (!wpa_s->ifmsh) {
+		wpa_msg(wpa_s, MSG_INFO, "Mesh is not prepared yet");
+		return -1;
+	}
+
+	if (!ssid || !ssid->no_auto_peer) {
+		wpa_msg(wpa_s, MSG_INFO,
+			"This command is available only with no_auto_peer mesh network");
+		return -1;
+	}
+
+	hapd = wpa_s->ifmsh->bss[0];
+	conf = wpa_s->ifmsh->mconf;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta) {
+		wpa_msg(wpa_s, MSG_INFO, "No such mesh peer");
+		return -1;
+	}
+
+	if ((PLINK_OPEN_SENT <= sta->plink_state &&
+	    sta->plink_state <= PLINK_ESTAB) ||
+	    (sta->sae && sta->sae->state > SAE_NOTHING)) {
+		wpa_msg(wpa_s, MSG_INFO,
+			"Specified peer is connecting/connected");
+		return -1;
+	}
+
+	if (conf->security == MESH_CONF_SEC_NONE) {
+		mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT);
+	} else {
+		mesh_rsn_auth_sae_sta(wpa_s, sta);
+		os_memcpy(hapd->mesh_required_peer, addr, ETH_ALEN);
+		eloop_register_timeout(duration == -1 ? 10 : duration, 0,
+				       peer_add_timer, wpa_s, NULL);
+	}
+
+	return 0;
+}
+
+
 void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh)
 {
 	struct hostapd_data *hapd = ifmsh->bss[0];
@@ -500,6 +588,7 @@
 
 	hapd->num_plinks = 0;
 	hostapd_free_stas(hapd);
+	eloop_cancel_timeout(peer_add_timer, wpa_s, NULL);
 }
 
 
@@ -640,7 +729,9 @@
 	if (!sta)
 		return;
 
-	if (ssid && ssid->no_auto_peer) {
+	if (ssid && ssid->no_auto_peer &&
+	    (is_zero_ether_addr(data->mesh_required_peer) ||
+	     os_memcmp(data->mesh_required_peer, addr, ETH_ALEN) != 0)) {
 		wpa_msg(wpa_s, MSG_INFO, "will not initiate new peer link with "
 			MACSTR " because of no_auto_peer", MAC2STR(addr));
 		if (data->mesh_pending_auth) {
@@ -720,7 +811,10 @@
 	hapd->num_plinks++;
 
 	sta->flags |= WLAN_STA_ASSOC;
+	sta->mesh_sae_pmksa_caching = 0;
 
+	eloop_cancel_timeout(peer_add_timer, wpa_s, NULL);
+	peer_add_timer(wpa_s, NULL);
 	eloop_cancel_timeout(plink_timer, wpa_s, sta);
 
 	/* Send ctrl event */
@@ -1000,7 +1094,8 @@
 	 * open mesh, then go ahead and add the peer before proceeding.
 	 */
 	if (!sta && action_field == PLINK_OPEN &&
-	    !(mconf->security & MESH_CONF_SEC_AMPE))
+	    (!(mconf->security & MESH_CONF_SEC_AMPE) ||
+	     wpa_auth_pmksa_get(hapd->wpa_auth, mgmt->sa)))
 		sta = mesh_mpm_add_peer(wpa_s, mgmt->sa, &elems);
 
 	if (!sta) {
diff --git a/wpa_supplicant/mesh_mpm.h b/wpa_supplicant/mesh_mpm.h
index 9af7563..5fc1e61 100644
--- a/wpa_supplicant/mesh_mpm.h
+++ b/wpa_supplicant/mesh_mpm.h
@@ -18,6 +18,9 @@
 void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s,
 			      struct sta_info *sta,
 			      enum mesh_plink_state state);
+int mesh_mpm_close_peer(struct wpa_supplicant *wpa_s, const u8 *addr);
+int mesh_mpm_connect_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+			  int duration);
 
 #ifdef CONFIG_MESH
 
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index 5d88274..1994f3f 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -27,12 +27,12 @@
 
 #define MESH_AUTH_TIMEOUT 10
 #define MESH_AUTH_RETRY 3
-#define MESH_AUTH_BLOCK_DURATION 3600
 
 void mesh_auth_timer(void *eloop_ctx, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
 	struct sta_info *sta = user_data;
+	struct hostapd_data *hapd;
 
 	if (sta->sae->state != SAE_ACCEPTED) {
 		wpa_printf(MSG_DEBUG, "AUTH: Re-authenticate with " MACSTR
@@ -43,23 +43,20 @@
 		if (sta->sae_auth_retry < MESH_AUTH_RETRY) {
 			mesh_rsn_auth_sae_sta(wpa_s, sta);
 		} else {
+			hapd = wpa_s->ifmsh->bss[0];
+
 			if (sta->sae_auth_retry > MESH_AUTH_RETRY) {
-				ap_free_sta(wpa_s->ifmsh->bss[0], sta);
+				ap_free_sta(hapd, sta);
 				return;
 			}
 
 			/* block the STA if exceeded the number of attempts */
 			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_BLOCKED);
 			sta->sae->state = SAE_NOTHING;
-			if (wpa_s->mesh_auth_block_duration <
-			    MESH_AUTH_BLOCK_DURATION)
-				wpa_s->mesh_auth_block_duration += 60;
-			eloop_register_timeout(wpa_s->mesh_auth_block_duration,
-					       0, mesh_auth_timer, wpa_s, sta);
 			wpa_msg(wpa_s, MSG_INFO, MESH_SAE_AUTH_BLOCKED "addr="
 				MACSTR " duration=%d",
 				MAC2STR(sta->addr),
-				wpa_s->mesh_auth_block_duration);
+				hapd->conf->ap_max_inactivity);
 		}
 		sta->sae_auth_retry++;
 	}
@@ -291,6 +288,7 @@
 {
 	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	struct rsn_pmksa_cache_entry *pmksa;
 	unsigned int rnd;
 	int ret;
 
@@ -306,6 +304,29 @@
 			return -1;
 	}
 
+	pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr);
+	if (pmksa) {
+		if (!sta->wpa_sm)
+			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+							sta->addr, NULL);
+		if (!sta->wpa_sm) {
+			wpa_printf(MSG_ERROR,
+				   "mesh: Failed to initialize RSN state machine");
+			return -1;
+		}
+
+		wpa_printf(MSG_DEBUG,
+			   "AUTH: Mesh PMKSA cache entry found for " MACSTR
+			   " - try to use PMKSA caching instead of new SAE authentication",
+			   MAC2STR(sta->addr));
+		wpa_auth_pmksa_set_to_sm(pmksa, sta->wpa_sm, hapd->wpa_auth,
+					 sta->sae->pmkid, sta->sae->pmk);
+		sae_accept_sta(hapd, sta);
+		sta->mesh_sae_pmksa_caching = 1;
+		return 0;
+	}
+	sta->mesh_sae_pmksa_caching = 0;
+
 	if (mesh_rsn_build_sae_commit(wpa_s, ssid, sta))
 		return -1;
 
@@ -514,6 +535,17 @@
 	const size_t aad_len[] = { ETH_ALEN, ETH_ALEN,
 				   (elems->mic - 2) - cat };
 
+	if (!sta->sae) {
+		struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+
+		if (!wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr)) {
+			wpa_printf(MSG_INFO,
+				   "Mesh RSN: SAE is not prepared yet");
+			return -1;
+		}
+		mesh_rsn_auth_sae_sta(wpa_s, sta);
+	}
+
 	if (chosen_pmk && os_memcmp(chosen_pmk, sta->sae->pmkid, PMKID_LEN)) {
 		wpa_msg(wpa_s, MSG_DEBUG,
 			"Mesh RSN: Invalid PMKID (Chosen PMK did not match calculated PMKID)");
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 325883d..e739363 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -13,6 +13,7 @@
 #include "config.h"
 #include "wpa_supplicant_i.h"
 #include "wps_supplicant.h"
+#include "binder/binder.h"
 #include "dbus/dbus_common.h"
 #include "dbus/dbus_old.h"
 #include "dbus/dbus_new.h"
@@ -34,6 +35,12 @@
 	}
 #endif /* CONFIG_DBUS */
 
+#ifdef CONFIG_BINDER
+	global->binder = wpas_binder_init(global);
+	if (!global->binder)
+		return -1;
+#endif /* CONFIG_BINDER */
+
 	return 0;
 }
 
@@ -44,6 +51,11 @@
 	if (global->dbus)
 		wpas_dbus_deinit(global->dbus);
 #endif /* CONFIG_DBUS */
+
+#ifdef CONFIG_BINDER
+	if (global->binder)
+		wpas_binder_deinit(global->binder);
+#endif /* CONFIG_BINDER */
 }
 
 
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 5ff758f..b310885 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -1134,7 +1134,8 @@
 	s->auth_alg = WPA_AUTH_ALG_OPEN;
 	s->key_mgmt = WPA_KEY_MGMT_PSK;
 	s->proto = WPA_PROTO_RSN;
-	s->pairwise_cipher = WPA_CIPHER_CCMP;
+	s->pbss = ssid->pbss;
+	s->pairwise_cipher = ssid->pbss ? WPA_CIPHER_GCMP : WPA_CIPHER_CCMP;
 	s->export_keys = 1;
 	if (ssid->passphrase) {
 		os_free(s->passphrase);
@@ -6107,8 +6108,10 @@
 	wpa_config_set_network_defaults(ssid);
 	ssid->temporary = 1;
 	ssid->proto = WPA_PROTO_RSN;
-	ssid->pairwise_cipher = WPA_CIPHER_CCMP;
-	ssid->group_cipher = WPA_CIPHER_CCMP;
+	ssid->pbss = params->pbss;
+	ssid->pairwise_cipher = params->pbss ? WPA_CIPHER_GCMP :
+		WPA_CIPHER_CCMP;
+	ssid->group_cipher = params->pbss ? WPA_CIPHER_GCMP : WPA_CIPHER_CCMP;
 	ssid->key_mgmt = WPA_KEY_MGMT_PSK;
 	ssid->ssid = os_malloc(params->ssid_len);
 	if (ssid->ssid == NULL) {
@@ -6328,6 +6331,8 @@
 	cfg->cb_ctx = wpa_s;
 	cfg->ie_update = wpas_p2p_ie_update;
 	cfg->idle_update = wpas_p2p_idle_update;
+	cfg->ip_addr_alloc = WPA_GET_BE32(wpa_s->p2pdev->conf->ip_addr_start)
+		!= 0;
 
 	group = p2p_group_init(wpa_s->global->p2p, cfg);
 	if (group == NULL)
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 3463dd9..7a52826 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -1895,7 +1895,7 @@
 
 /*
  * Noise floor values to use when we have signal strength
- * measurements, but no noise floor measurments. These values were
+ * measurements, but no noise floor measurements. These values were
  * measured in an office environment with many APs.
  */
 #define DEFAULT_NOISE_FLOOR_2GHZ (-89)
@@ -2334,10 +2334,18 @@
 	struct wpa_ssid *ssid;
 	struct wpa_driver_scan_params params;
 	struct sched_scan_plan scan_plan;
+	unsigned int max_sched_scan_ssids;
 
 	if (!wpa_s->sched_scan_supported)
 		return -1;
 
+	if (wpa_s->max_sched_scan_ssids > WPAS_MAX_SCAN_SSIDS)
+		max_sched_scan_ssids = WPAS_MAX_SCAN_SSIDS;
+	else
+		max_sched_scan_ssids = wpa_s->max_sched_scan_ssids;
+	if (max_sched_scan_ssids < 1)
+		return -1;
+
 	if (wpa_s->pno || wpa_s->pno_sched_pending)
 		return 0;
 
@@ -2381,10 +2389,10 @@
 		num_ssid++;
 	}
 
-	if (num_ssid > WPAS_MAX_SCAN_SSIDS) {
+	if (num_ssid > max_sched_scan_ssids) {
 		wpa_printf(MSG_DEBUG, "PNO: Use only the first %u SSIDs from "
-			   "%u", WPAS_MAX_SCAN_SSIDS, (unsigned int) num_ssid);
-		num_ssid = WPAS_MAX_SCAN_SSIDS;
+			   "%u", max_sched_scan_ssids, (unsigned int) num_ssid);
+		num_ssid = max_sched_scan_ssids;
 	}
 
 	if (num_match_ssid > wpa_s->max_match_sets) {
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 7d2a92f..f77d51a 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -546,6 +546,17 @@
 			continue;
 		}
 
+		if (wpa_s->current_ssid &&
+		    !wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid,
+					1)) {
+			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
+				   " (pref %d) does not match the current network profile",
+				   MAC2STR(nei->bssid),
+				   nei->preference_present ? nei->preference :
+				   -1);
+			continue;
+		}
+
 		if (wpa_is_bss_tmp_disallowed(wpa_s, target->bssid)) {
 			wpa_printf(MSG_DEBUG,
 				   "MBO: Candidate BSS " MACSTR
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index eecb1dc..36a7a4e 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -2042,6 +2042,20 @@
 	return wpa_cli_cmd(ctrl, "MESH_GROUP_REMOVE", 1, argc, argv);
 }
 
+
+static int wpa_cli_cmd_mesh_peer_remove(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "MESH_PEER_REMOVE", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_mesh_peer_add(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "MESH_PEER_ADD", 1, argc, argv);
+}
+
 #endif /* CONFIG_MESH */
 
 
@@ -3210,6 +3224,12 @@
 	{ "mesh_group_remove", wpa_cli_cmd_mesh_group_remove, NULL,
 	  cli_cmd_flag_none,
 	  "<ifname> = Remove mesh group interface" },
+	{ "mesh_peer_remove", wpa_cli_cmd_mesh_peer_remove, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> = Remove a mesh peer" },
+	{ "mesh_peer_add", wpa_cli_cmd_mesh_peer_add, NULL,
+	  cli_cmd_flag_none,
+	  "<addr> [duration=<seconds>] = Add a mesh peer" },
 #endif /* CONFIG_MESH */
 #ifdef CONFIG_P2P
 	{ "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_p2p_find,
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index 3f91cc1..511df4f 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -29,6 +29,8 @@
 	char *sock_name;
 	int fd;
 
+	void *ctx;
+
 	const struct wpa_driver_ops *driver;
 	void *drv_priv;
 	void *drv_global_priv;
@@ -40,6 +42,10 @@
 	struct sockaddr_un l2_addr;
 };
 
+struct wpa_priv_global {
+	struct wpa_priv_interface *interfaces;
+};
+
 
 static void wpa_priv_cmd_register(struct wpa_priv_interface *iface,
 				  struct sockaddr_un *from)
@@ -65,7 +71,8 @@
 
 	if (iface->driver->init2) {
 		if (iface->driver->global_init) {
-			iface->drv_global_priv = iface->driver->global_init();
+			iface->drv_global_priv =
+				iface->driver->global_init(iface->ctx);
 			if (!iface->drv_global_priv) {
 				wpa_printf(MSG_INFO,
 					   "Failed to initialize driver global context");
@@ -638,7 +645,7 @@
 
 
 static struct wpa_priv_interface *
-wpa_priv_interface_init(const char *dir, const char *params)
+wpa_priv_interface_init(void *ctx, const char *dir, const char *params)
 {
 	struct wpa_priv_interface *iface;
 	char *pos;
@@ -654,6 +661,7 @@
 	if (iface == NULL)
 		return NULL;
 	iface->fd = -1;
+	iface->ctx = ctx;
 
 	len = pos - params;
 	iface->driver_name = dup_binstr(params, len);
@@ -1002,6 +1010,37 @@
 }
 
 
+void wpa_supplicant_event_global(void *ctx, enum wpa_event_type event,
+				 union wpa_event_data *data)
+{
+	struct wpa_priv_global *global = ctx;
+	struct wpa_priv_interface *iface;
+
+	if (event != EVENT_INTERFACE_STATUS)
+		return;
+
+	for (iface = global->interfaces; iface; iface = iface->next) {
+		if (os_strcmp(iface->ifname, data->interface_status.ifname) ==
+		    0)
+			break;
+	}
+	if (iface && iface->driver->get_ifindex) {
+		unsigned int ifindex;
+
+		ifindex = iface->driver->get_ifindex(iface->drv_priv);
+		if (ifindex != data->interface_status.ifindex) {
+			wpa_printf(MSG_DEBUG,
+				   "%s: interface status ifindex %d mismatch (%d)",
+				   iface->ifname, ifindex,
+				   data->interface_status.ifindex);
+			return;
+		}
+	}
+	if (iface)
+		wpa_supplicant_event(iface, event, data);
+}
+
+
 void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
 			     const u8 *buf, size_t len)
 {
@@ -1077,13 +1116,17 @@
 	char *pid_file = NULL;
 	int daemonize = 0;
 	char *ctrl_dir = "/var/run/wpa_priv";
-	struct wpa_priv_interface *interfaces = NULL, *iface;
+	struct wpa_priv_global global;
+	struct wpa_priv_interface *iface;
 
 	if (os_program_init())
 		return -1;
 
 	wpa_priv_fd_workaround();
 
+	os_memset(&global, 0, sizeof(global));
+	global.interfaces = NULL;
+
 	for (;;) {
 		c = getopt(argc, argv, "Bc:dP:");
 		if (c < 0)
@@ -1121,11 +1164,11 @@
 
 	for (i = optind; i < argc; i++) {
 		wpa_printf(MSG_DEBUG, "Adding driver:interface %s", argv[i]);
-		iface = wpa_priv_interface_init(ctrl_dir, argv[i]);
+		iface = wpa_priv_interface_init(&global, ctrl_dir, argv[i]);
 		if (iface == NULL)
 			goto out;
-		iface->next = interfaces;
-		interfaces = iface;
+		iface->next = global.interfaces;
+		global.interfaces = iface;
 	}
 
 	if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue())
@@ -1137,7 +1180,7 @@
 	ret = 0;
 
 out:
-	iface = interfaces;
+	iface = global.interfaces;
 	while (iface) {
 		struct wpa_priv_interface *prev = iface;
 		iface = iface->next;
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 136cb58..51bb245 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -11,6 +11,10 @@
  */
 
 #include "includes.h"
+#ifdef CONFIG_MATCH_IFACE
+#include <net/if.h>
+#include <fnmatch.h>
+#endif /* CONFIG_MATCH_IFACE */
 
 #include "common.h"
 #include "crypto/random.h"
@@ -1642,9 +1646,11 @@
 
 	wmm_ac_clear_saved_tspecs(wpa_s);
 	wpa_s->reassoc_same_bss = 0;
+	wpa_s->reassoc_same_ess = 0;
 
 	if (wpa_s->last_ssid == ssid) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS");
+		wpa_s->reassoc_same_ess = 1;
 		if (wpa_s->current_bss && wpa_s->current_bss == bss) {
 			wmm_ac_save_tspecs(wpa_s);
 			wpa_s->reassoc_same_bss = 1;
@@ -2059,6 +2065,7 @@
 	int wep_keys_set = 0;
 	int assoc_failed = 0;
 	struct wpa_ssid *old_ssid;
+	u8 prev_bssid[ETH_ALEN];
 #ifdef CONFIG_HT_OVERRIDES
 	struct ieee80211_ht_capabilities htcaps;
 	struct ieee80211_ht_capabilities htcaps_mask;
@@ -2092,6 +2099,7 @@
 		return;
 	}
 
+	os_memcpy(prev_bssid, wpa_s->bssid, ETH_ALEN);
 	os_memset(&params, 0, sizeof(params));
 	wpa_s->reassociate = 0;
 	wpa_s->eap_expected_failure = 0;
@@ -2532,6 +2540,10 @@
 	}
 #endif /* CONFIG_P2P */
 
+	if (wpa_s->reassoc_same_ess && !is_zero_ether_addr(prev_bssid) &&
+	    wpa_s->current_ssid)
+		params.prev_bssid = prev_bssid;
+
 	ret = wpa_drv_associate(wpa_s, &params);
 	if (ret < 0) {
 		wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
@@ -3143,7 +3155,7 @@
 	struct wpa_global *global = wpa_s->global;
 
 	if (wpa_drivers[i]->global_init && global->drv_priv[i] == NULL) {
-		global->drv_priv[i] = wpa_drivers[i]->global_init();
+		global->drv_priv[i] = wpa_drivers[i]->global_init(global);
 		if (global->drv_priv[i] == NULL) {
 			wpa_printf(MSG_ERROR, "Failed to initialize driver "
 				   "'%s'", wpa_drivers[i]->name);
@@ -4911,6 +4923,74 @@
 }
 
 
+#ifdef CONFIG_MATCH_IFACE
+
+/**
+ * wpa_supplicant_match_iface - Match an interface description to a name
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @ifname: Name of the interface to match
+ * Returns: Pointer to the created interface description or %NULL on failure
+ */
+struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global,
+						  const char *ifname)
+{
+	int i;
+	struct wpa_interface *iface, *miface;
+
+	for (i = 0; i < global->params.match_iface_count; i++) {
+		miface = &global->params.match_ifaces[i];
+		if (!miface->ifname ||
+		    fnmatch(miface->ifname, ifname, 0) == 0) {
+			iface = os_zalloc(sizeof(*iface));
+			if (!iface)
+				return NULL;
+			*iface = *miface;
+			iface->ifname = ifname;
+			return iface;
+		}
+	}
+
+	return NULL;
+}
+
+
+/**
+ * wpa_supplicant_match_existing - Match existing interfaces
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * Returns: 0 on success, -1 on failure
+ */
+static int wpa_supplicant_match_existing(struct wpa_global *global)
+{
+	struct if_nameindex *ifi, *ifp;
+	struct wpa_supplicant *wpa_s;
+	struct wpa_interface *iface;
+
+	ifp = if_nameindex();
+	if (!ifp) {
+		wpa_printf(MSG_ERROR, "if_nameindex: %s", strerror(errno));
+		return -1;
+	}
+
+	for (ifi = ifp; ifi->if_name; ifi++) {
+		wpa_s = wpa_supplicant_get_iface(global, ifi->if_name);
+		if (wpa_s)
+			continue;
+		iface = wpa_supplicant_match_iface(global, ifi->if_name);
+		if (iface) {
+			wpa_s = wpa_supplicant_add_iface(global, iface, NULL);
+			os_free(iface);
+			if (wpa_s)
+				wpa_s->matched = 1;
+		}
+	}
+
+	if_freenameindex(ifp);
+	return 0;
+}
+
+#endif /* CONFIG_MATCH_IFACE */
+
+
 /**
  * wpa_supplicant_add_iface - Add a new network interface
  * @global: Pointer to global data from wpa_supplicant_init()
@@ -5212,6 +5292,18 @@
 	if (params->override_ctrl_interface)
 		global->params.override_ctrl_interface =
 			os_strdup(params->override_ctrl_interface);
+#ifdef CONFIG_MATCH_IFACE
+	global->params.match_iface_count = params->match_iface_count;
+	if (params->match_iface_count) {
+		global->params.match_ifaces =
+			os_calloc(params->match_iface_count,
+				  sizeof(struct wpa_interface));
+		os_memcpy(global->params.match_ifaces,
+			  params->match_ifaces,
+			  params->match_iface_count *
+			  sizeof(struct wpa_interface));
+	}
+#endif /* CONFIG_MATCH_IFACE */
 #ifdef CONFIG_P2P
 	if (params->conf_p2p_dev)
 		global->params.conf_p2p_dev =
@@ -5291,6 +5383,11 @@
 	     eloop_sock_requeue()))
 		return -1;
 
+#ifdef CONFIG_MATCH_IFACE
+	if (wpa_supplicant_match_existing(global))
+		return -1;
+#endif
+
 	if (global->params.wait_for_monitor) {
 		for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
 			if (wpa_s->ctrl_iface && !wpa_s->p2p_mgmt)
@@ -5359,6 +5456,9 @@
 	os_free(global->params.ctrl_interface_group);
 	os_free(global->params.override_driver);
 	os_free(global->params.override_ctrl_interface);
+#ifdef CONFIG_MATCH_IFACE
+	os_free(global->params.match_ifaces);
+#endif /* CONFIG_MATCH_IFACE */
 #ifdef CONFIG_P2P
 	os_free(global->params.conf_p2p_dev);
 #endif /* CONFIG_P2P */
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 269bac0..c485891 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -44,6 +44,7 @@
 struct ctrl_iface_priv;
 struct ctrl_iface_global_priv;
 struct wpas_dbus_priv;
+struct wpas_binder_priv;
 
 /**
  * struct wpa_interface - Parameters for wpa_supplicant_add_iface()
@@ -228,6 +229,17 @@
 	char *conf_p2p_dev;
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_MATCH_IFACE
+	/**
+	 * match_ifaces - Interface descriptions to match
+	 */
+	struct wpa_interface *match_ifaces;
+
+	/**
+	 * match_iface_count - Number of defined matching interfaces
+	 */
+	int match_iface_count;
+#endif /* CONFIG_MATCH_IFACE */
 };
 
 struct p2p_srv_bonjour {
@@ -253,6 +265,7 @@
 	struct wpa_params params;
 	struct ctrl_iface_global_priv *ctrl_iface;
 	struct wpas_dbus_priv *dbus;
+	struct wpas_binder_priv *binder;
 	void **drv_priv;
 	size_t drv_count;
 	struct os_time suspend_time;
@@ -458,6 +471,9 @@
 	unsigned char own_addr[ETH_ALEN];
 	unsigned char perm_addr[ETH_ALEN];
 	char ifname[100];
+#ifdef CONFIG_MATCH_IFACE
+	int matched;
+#endif /* CONFIG_MATCH_IFACE */
 #ifdef CONFIG_CTRL_IFACE_DBUS
 	char *dbus_path;
 #endif /* CONFIG_CTRL_IFACE_DBUS */
@@ -468,6 +484,9 @@
 	char *preq_notify_peer;
 #endif /* CONFIG_AP */
 #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
+#ifdef CONFIG_CTRL_IFACE_BINDER
+	const void *binder_object_key;
+#endif /* CONFIG_CTRL_IFACE_BINDER */
 	char bridge_ifname[16];
 
 	char *confname;
@@ -480,7 +499,8 @@
 	u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this
 				     * field contains the target BSSID. */
 	int reassociate; /* reassociation requested */
-	int reassoc_same_bss; /* reassociating to the same bss */
+	unsigned int reassoc_same_bss:1; /* reassociating to the same BSS */
+	unsigned int reassoc_same_ess:1; /* reassociating to the same ESS */
 	int disconnected; /* all connections disabled; i.e., do no reassociate
 			   * before this has been cleared */
 	struct wpa_ssid *current_ssid;
@@ -754,7 +774,6 @@
 	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 */
 
 	unsigned int off_channel_freq;
@@ -1109,6 +1128,8 @@
 
 void wpa_show_license(void);
 
+struct wpa_interface * wpa_supplicant_match_iface(struct wpa_global *global,
+						  const char *ifname);
 struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
 						 struct wpa_interface *iface,
 						 struct wpa_supplicant *parent);