Cumulative patch from commit 077dcfb8c48d2509a6e116c0de3ad57d2fbfe4fe

077dcfb AP: Debug print management frame TX result
ca911d6 MBO: Parse non-preferred channel list on the AP
3f48274 WNM: Fix a memory leak on AP error path
f5ca176 VLAN: Fix vlan_compare() for tagged VLANs
1260564 hostapd_cli: Add support for RAW command
940491c MBO: Mandate use of PMF for WPA2+MBO association (STA)
4c57228 MBO: Mandate use of PMF for WPA2+MBO association (AP)
8dd49f0 MBO: Update STA cellular data capability based on WNM Notification
6332aaf MBO: Track STA cellular data capability from association request
f3cb7a6 WNM: Minimal processing for WNM Notification Request frames on AP
e578343 MBO: Indicate WNM-Notification support on AP when MBO is enabled
990b7b6 Simplify hostapd_build_ap_extra_ies() with helper functions
d010048 MBO: Expire non-matching bss_tmp_disallowed entries as part of check
f4c74e1 MBO: Parse MBO IE in ieee802_11_parse_elems()
016082e MBO: Send WNM-Notification when cellular capabilities change
c0e2a17 hostapd: Add MBO IE to BSS Transition Management Request frame
fb9a1c3 hostapd: Add MBO IE to Beacon, Probe Response, Association Response
c484b19 Move Hotspot 2.0 element in (Re)Association Request frames
a0c38e5 Re-order elements in (Re)Association Request frames
9a493fa WNM: Add candidate list to BSS transition query
84d1c0f WNM: Add candidate list to BSS transition response
cf11ab7 utils: Derive phy type by frequency and bandwidth
c8082d2 MBO: Add MBO IE to BSS Transition Management Response frame
dd59990 MBO: Parse MBO IE in BSS Transition Management Request frames
5e57ba2 MBO: Add Supported Operating Classes element to Association Request
7d46f58 MBO: Add global operating class definitions
cb06cf3 MBO: Prevent association to APs that explicitly disallow this
c5d193d MBO: Add cellular capability to MBO IE
2d5b861 MBO: Send MBO WNM-Notification Request frames to notify changes
92c6e2e MBO: Implement MBO non-preferred channel report in Association Request
facf2c7 MBO: Add non-preferred channel configuration in wpa_supplicant
425dd78 MBO: Add Multi Band Operation definitions
a159958 ndis: Use the new get_ie() helper to avoid duplicated code
231b04b utils: Share a single helper function to get IE by ID
ea69d97 wpa_supplicant: Share a single get_mode() implementation
75cc211 VLAN: Check vlan_desc validity in a failure debug print
43022ab Use 64-bit TX/RX byte counters for statistics
3f81ac0 AP: Set STA assoc flag in the driver before sending Assoc Resp frame
bb598c3 AP: Add support for full station state
dc55b6b nl80211: Add support for full station state operations
5558b99 EAP-FAST peer: Remove fixed return value from eap_fast_parse_phase1()
4b16c15 EAP-pwd server: Use os_get_random() for unpredictable token
239952b DFS: Remove the os_random() fallback
98a516e WPS: Use only os_get_random() for PIN generation
f441e5a Use os_get_random() for Shared Key authentication challenge
8c676b5 Add RADIUS Service-Type attribute with a value of Framed
09d96de mesh: Drop Authentication frames from BLOCKED STA
70c9396 SAE: Fix PMKID calculation for PMKSA cache
1492fbb Print Acct-Session-Id and Acct-Multi-Session-Id 64-bit values
e21ceca kqueue: Use 0 instead of NULL for udata
640b0b9 ctype functions require an unsigned char
a5a3efc Fix compile on NetBSD for vlan
a084c24 wired: Fix compile on NetBSD for wired driver
634e2e2 Add CONFIG_ELOOP_KQUEUE to defconfig
99a94f5 nl80211: Avoid wpa_printf %s call with NULL pointer in set_param()
ba91e92 wpa_supplicant: Parse ifname argument from DATA_TEST_CONFIG
8be640b VLAN: Add per-STA vif option
d0bdc96 VLAN: Actually add tagged VLANs to AP_VLAN
f9c0018 VLAN: Factor out per-vid code in newlink/dellink
8e44c19 radius: Add tagged VLAN parsing
1889af2 VLAN: Separate station grouping and uplink configuration
3a583e0 OpenSSL: Fix PKCS#12 parsing of extra certificates with OpenSSL 1.0.1
ddd0032 wpa_cli: Clean up logical operation
24c382a TDLS: Clean up os_memcmp use
6136d43 trace: Free symbols on program exit
8bcf8de OpenSSL: Fix memory leak in PKCS12 additional certificate parsing
03e3ddf OpenSSL: Fix memory leak in HMAC_CTX compatibility wrapper function
d9a0f69 OpenSSL: Fix memory leak in OCSP parsing
29bc76e OpenSSL: Do not use library init/deinit functions with 1.1.0
0f09637 OpenSSL: Fix memory leak in subjectAltName parsing
e60913b curl: Fix memory leak in subjectAltName parsing
6014890 OpenSSL: Fix memory leak with EVP_CIPHER_CTX_new()
99a1735 rfkill: Fix a memory leak
1f1e599 OpenSSL: Fix memory leak on error path
b907491 wpa_supplicant: Basic support for PBSS/PCP
86b5c40 nl80211: Basic support for PBSS/PCP
afa453a Sync with mac80211-next.git include/uapi/linux/nl80211.h
d1d8a2b EAP peer: Simplify buildNotify return
1314bc1 Clean up EAP peer PCSC identity functions

Change-Id: I9db475a2a4ebc88d2ee024319ed59a850636bb16
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index e3df164..8eab6cb 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -9,13 +9,6 @@
  */
 
 #include "utils/includes.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 */
 
 #include "utils/common.h"
 #include "hostapd.h"
@@ -25,6 +18,14 @@
 #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
 
@@ -623,153 +624,228 @@
 #endif /* CONFIG_VLAN_NETLINK */
 
 
-static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
+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];
-	char br_name[IFNAMSIZ];
-	struct hostapd_vlan *vlan = hapd->conf->vlan;
-	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
-	int vlan_naming = hapd->conf->ssid.vlan_naming;
 	int clean;
 
-	wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
+	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);
 
-	while (vlan) {
-		if (os_strcmp(ifname, vlan->ifname) == 0 && !vlan->configured) {
-			vlan->configured = 1;
+	clean = 0;
+	ifconfig_up(tagged_interface);
+	if (!vlan_add(tagged_interface, vid, vlan_ifname))
+		clean |= DVLAN_CLEAN_VLAN;
 
-			if (hapd->conf->vlan_bridge[0]) {
-				os_snprintf(br_name, sizeof(br_name), "%s%d",
-					    hapd->conf->vlan_bridge,
-					    vlan->vlan_id);
-			} else if (tagged_interface) {
-				os_snprintf(br_name, sizeof(br_name),
-				            "br%s.%d", tagged_interface,
-					    vlan->vlan_id);
-			} else {
-				os_snprintf(br_name, sizeof(br_name),
-				            "brvlan%d", vlan->vlan_id);
-			}
+	if (!br_addif(br_name, vlan_ifname))
+		clean |= DVLAN_CLEAN_VLAN_PORT;
 
-			dyn_iface_get(hapd, br_name,
-				      br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR);
+	dyn_iface_get(hapd, vlan_ifname, clean);
 
-			ifconfig_up(br_name);
+	ifconfig_up(vlan_ifname);
+}
 
-			if (tagged_interface) {
-				if (vlan_naming ==
-				    DYNAMIC_VLAN_NAMING_WITH_DEVICE)
-					os_snprintf(vlan_ifname,
-						    sizeof(vlan_ifname),
-						    "%s.%d", tagged_interface,
-						    vlan->vlan_id);
-				else
-					os_snprintf(vlan_ifname,
-						    sizeof(vlan_ifname),
-						    "vlan%d", vlan->vlan_id);
 
-				clean = 0;
-				ifconfig_up(tagged_interface);
-				if (!vlan_add(tagged_interface, vlan->vlan_id,
-					      vlan_ifname))
-					clean |= DVLAN_CLEAN_VLAN;
+static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd, int vid)
+{
+	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
 
-				if (!br_addif(br_name, vlan_ifname))
-					clean |= DVLAN_CLEAN_VLAN_PORT;
-
-				dyn_iface_get(hapd, vlan_ifname, clean);
-
-				ifconfig_up(vlan_ifname);
-			}
-
-			if (!br_addif(br_name, ifname))
-				vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
-
-			ifconfig_up(ifname);
-
-			break;
-		}
-		vlan = vlan->next;
+	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_dellink(char *ifname, struct hostapd_data *hapd)
+static void vlan_get_bridge(const char *br_name, struct hostapd_data *hapd,
+			    int vid)
 {
-	char vlan_ifname[IFNAMSIZ];
-	char br_name[IFNAMSIZ];
-	struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
 	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
 	int vlan_naming = hapd->conf->ssid.vlan_naming;
+
+	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 &&
-		    vlan->configured) {
-			if (hapd->conf->vlan_bridge[0]) {
-				os_snprintf(br_name, sizeof(br_name), "%s%d",
-					    hapd->conf->vlan_bridge,
-					    vlan->vlan_id);
-			} else if (tagged_interface) {
-				os_snprintf(br_name, sizeof(br_name),
-				            "br%s.%d", tagged_interface,
-					    vlan->vlan_id);
-			} else {
-				os_snprintf(br_name, sizeof(br_name),
-				            "brvlan%d", vlan->vlan_id);
-			}
+		if (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);
 
-			if (tagged_interface) {
-				if (vlan_naming ==
-				    DYNAMIC_VLAN_NAMING_WITH_DEVICE)
-					os_snprintf(vlan_ifname,
-						    sizeof(vlan_ifname),
-						    "%s.%d", tagged_interface,
-						    vlan->vlan_id);
-				else
-					os_snprintf(vlan_ifname,
-						    sizeof(vlan_ifname),
-						    "vlan%d", vlan->vlan_id);
-
-				clean = dyn_iface_put(hapd, vlan_ifname);
-
-				if (clean & DVLAN_CLEAN_VLAN_PORT)
-					br_delif(br_name, vlan_ifname);
-
-				if (clean & DVLAN_CLEAN_VLAN) {
-					ifconfig_down(vlan_ifname);
-					vlan_rem(vlan_ifname);
-				}
-			}
-
-			clean = dyn_iface_put(hapd, br_name);
-			if ((clean & DVLAN_CLEAN_BR) &&
-			    br_getnumports(br_name) == 0) {
-				ifconfig_down(br_name);
-				br_delbr(br_name);
-			}
+			vlan_put_bridge(br_name, hapd, untagged);
 		}
-
-		if (os_strcmp(ifname, vlan->ifname) == 0) {
-			if (vlan == first) {
-				hapd->conf->vlan = vlan->next;
-			} else {
-				prev->next = vlan->next;
-			}
-			os_free(vlan);
-
-			break;
-		}
-		prev = vlan;
-		vlan = vlan->next;
 	}
+
+	/*
+	 * 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);
 }
 
 
@@ -977,15 +1053,17 @@
 	while (vlan) {
 		next = vlan->next;
 
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+		/* vlan_dellink() takes care of cleanup and interface removal */
+		if (vlan->vlan_id != VLAN_ID_WILDCARD)
+			vlan_dellink(vlan->ifname, hapd);
+#else /* CONFIG_FULL_DYNAMIC_VLAN */
 		if (vlan->vlan_id != VLAN_ID_WILDCARD &&
 		    vlan_if_remove(hapd, vlan)) {
 			wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
 				   "iface: %s: %s",
 				   vlan->ifname, strerror(errno));
 		}
-#ifdef CONFIG_FULL_DYNAMIC_VLAN
-		if (vlan->clean)
-			vlan_dellink(vlan->ifname, hapd);
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
 
 		vlan = next;
@@ -999,7 +1077,8 @@
 	hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd);
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
 
-	if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED &&
+	if ((hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED ||
+	     hapd->conf->ssid.per_sta_vif) &&
 	    !hapd->conf->vlan) {
 		/* dynamic vlans enabled but no (or empty) vlan_file given */
 		struct hostapd_vlan *vlan;
@@ -1037,13 +1116,13 @@
 
 struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
 				       struct hostapd_vlan *vlan,
-				       int vlan_id)
+				       int vlan_id,
+				       struct vlan_description *vlan_desc)
 {
 	struct hostapd_vlan *n = NULL;
 	char *ifname, *pos;
 
-	if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID ||
-	    vlan->vlan_id != VLAN_ID_WILDCARD)
+	if (vlan == NULL || vlan->vlan_id != VLAN_ID_WILDCARD)
 		return NULL;
 
 	wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)",
@@ -1061,6 +1140,8 @@
 		goto free_ifname;
 
 	n->vlan_id = vlan_id;
+	if (vlan_desc)
+		n->vlan_desc = *vlan_desc;
 	n->dynamic_vlan = 1;
 
 	os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
@@ -1087,7 +1168,7 @@
 {
 	struct hostapd_vlan *vlan;
 
-	if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID)
+	if (vlan_id <= 0)
 		return 1;
 
 	wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)",