Patchwork [RFC,4/5] driver_nl80211: Allow bridging of AP_VLAN interfaces

login
register
mail settings
Submitter Helmut Schaa
Date March 17, 2014, 1:16 p.m.
Message ID <1395062188-13222-5-git-send-email-helmut.schaa@googlemail.com>
Download mbox | patch
Permalink /patch/331039/
State New
Headers show

Comments

Helmut Schaa - March 17, 2014, 1:16 p.m.
Signed-off-by: Helmut Schaa <helmut.schaa@googlemail.com>
---
 src/drivers/driver_nl80211.c | 109 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 92 insertions(+), 17 deletions(-)

Patch

diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 3cfaadc..276c750 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -224,6 +224,15 @@  struct i802_bridge {
 	unsigned int added_bridge:1;
 };
 
+struct i802_ap_vlan {
+	struct i802_ap_vlan *next;
+
+	int ifindex;
+	char ifname[IFNAMSIZ + 1];
+	struct i802_bridge bridge;
+	unsigned int added_if:1;
+};
+
 struct i802_bss {
 	struct wpa_driver_nl80211_data *drv;
 	struct i802_bss *next;
@@ -235,6 +244,7 @@  struct i802_bss {
 	unsigned int wdev_id_set:1;
 	unsigned int added_if:1;
 	struct i802_bridge bridge;
+	struct i802_ap_vlan *vlans;
 
 	u8 addr[ETH_ALEN];
 
@@ -9743,6 +9753,35 @@  static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
 	}
 #endif /* CONFIG_P2P */
 
+	if (type == WPA_IF_AP_VLAN) {
+		struct i802_ap_vlan *ap_vlan = os_zalloc(sizeof(*ap_vlan));
+		if (bridge &&
+		    i802_check_bridge(drv, &ap_vlan->bridge, bridge, ifname) < 0) {
+			wpa_printf(MSG_ERROR, "nl80211: Failed to add the new "
+				   "interface %s to a bridge %s",
+				   ifname, bridge);
+			if (added)
+				nl80211_remove_iface(drv, ifidx);
+			os_free(ap_vlan);
+			return -1;
+		}
+
+		if (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1)) {
+			if (added)
+				nl80211_remove_iface(drv, ifidx);
+			os_free(ap_vlan);
+			return -1;
+		}
+
+		ap_vlan->added_if = added;
+		ap_vlan->ifindex = ifidx;
+		os_strlcpy(ap_vlan->ifname, ifname, IFNAMSIZ);
+
+		/* Remeber this AP VLAN */
+		ap_vlan->next = bss->vlans;
+		bss->vlans = ap_vlan;
+	}
+
 	if (type == WPA_IF_AP_BSS) {
 		struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss));
 		if (new_bss == NULL) {
@@ -9799,29 +9838,65 @@  static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	int ifindex = if_nametoindex(ifname);
-
+	struct i802_bridge *bridge = NULL;
+	struct i802_ap_vlan *ap_vlan = NULL, *ap_vlan_prev = NULL;
+ 
 	wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d added_if=%d",
 		   __func__, type, ifname, ifindex, bss->added_if);
-	if (ifindex > 0 && (bss->added_if || bss->ifindex != ifindex))
-		nl80211_remove_iface(drv, ifindex);
-
-	if (type != WPA_IF_AP_BSS)
-		return 0;
 
-	if (bss->bridge.added_if_into_bridge) {
-		if (linux_br_del_if(drv->global->ioctl_sock, bss->bridge.brname,
-				    bss->ifname) < 0)
-			wpa_printf(MSG_INFO, "nl80211: Failed to remove "
-				   "interface %s from bridge %s: %s",
-				   bss->ifname, bss->bridge.brname, strerror(errno));
+	switch (type) {
+		case WPA_IF_AP_BSS:
+			bridge = &bss->bridge;
+			break;
+		case WPA_IF_AP_VLAN:
+			ap_vlan = bss->vlans;
+			while (ap_vlan) {
+				if (ap_vlan->ifindex != ifindex) {
+					ap_vlan_prev = ap_vlan;
+					ap_vlan = ap_vlan->next;
+					continue;
+				}
+				bridge = &ap_vlan->bridge;
+				break;
+			}
+			break;
+		default:
+			if (ifindex > 0)
+				nl80211_remove_iface(drv, ifindex);
+			return 0;
 	}
-	if (bss->bridge.added_bridge) {
-		if (linux_br_del(drv->global->ioctl_sock, bss->bridge.brname) < 0)
-			wpa_printf(MSG_INFO, "nl80211: Failed to remove "
-				   "bridge %s: %s",
-				   bss->bridge.brname, strerror(errno));
+ 
+	if (bridge) {
+		if (bridge->added_if_into_bridge) {
+			if (linux_br_del_if(drv->global->ioctl_sock, bridge->brname,
+					    ifname) < 0)
+				wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+					   "interface %s from bridge %s: %s",
+					   ifname, bridge->brname, strerror(errno));
+		}
+		if (bridge->added_bridge) {
+			if (linux_br_del(drv->global->ioctl_sock, bridge->brname) < 0)
+				wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+					   "bridge %s: %s",
+					   bridge->brname, strerror(errno));
+		}
 	}
 
+	if (ap_vlan) {
+		if (ap_vlan->added_if && ap_vlan->ifindex == ifindex)
+			nl80211_remove_iface(drv, ifindex);
+		if (ap_vlan_prev)
+			ap_vlan_prev->next = ap_vlan->next;
+		else
+			bss->vlans = ap_vlan->next;
+
+		os_free(ap_vlan);
+		return 0;
+	}
+ 
+	if (bss->added_if)
+		nl80211_remove_iface(drv, ifindex);
+
 	if (bss != drv->first_bss) {
 		struct i802_bss *tbss;