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/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);