diff mbox

[2/3] wpa: fix init of group state machine for static vlans

Message ID 1444053446-13210-3-git-send-email-michael-dev@fami-braun.de
State Superseded
Headers show

Commit Message

michael-dev Oct. 5, 2015, 1:57 p.m. UTC
This ensures that group key is set as long as the interface
exists. This fixes hwsim test ap_vlan_without_station.

Additionally, ifconfig_up is needed as wpa_group will enter
FATAL_FAILURE if the interface is still down. Also
vlan_remove_dynamic is moved after wpa_auth_sta_deinit so
vlan_remove_dynamic can check it was the last user of
the wpa_group.

Signed-off-by: Michael Braun <michael-dev@fami-braun.de>
---
 src/ap/sta_info.c      | 31 ++++++++++--------
 src/ap/vlan_init.c     | 84 ++++++++++++++++++++++++++++++++++++-----------
 src/ap/wpa_auth.c      | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/ap/wpa_auth.h      |  2 ++
 src/ap/wpa_auth_glue.c |  9 +++++
 src/ap/wpa_auth_glue.h |  2 ++
 src/ap/wpa_auth_i.h    |  1 +
 7 files changed, 186 insertions(+), 32 deletions(-)
diff mbox

Patch

diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 20847d5..ace60d6 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -171,19 +171,6 @@  void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 	    !(sta->flags & WLAN_STA_PREAUTH))
 		hostapd_drv_sta_remove(hapd, sta->addr);
 
-#ifndef CONFIG_NO_VLAN
-	if (sta->vlan_id_bound) {
-		/*
-		 * Need to remove the STA entry before potentially removing the
-		 * VLAN.
-		 */
-		if (hapd->iface->driver_ap_teardown &&
-		    !(sta->flags & WLAN_STA_PREAUTH))
-			hostapd_drv_sta_remove(hapd, sta->addr);
-		vlan_remove_dynamic(hapd, sta->vlan_id_bound);
-	}
-#endif /* CONFIG_NO_VLAN */
-
 	ap_sta_hash_del(hapd, sta);
 	ap_sta_list_del(hapd, sta);
 
@@ -273,6 +260,24 @@  void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 		radius_client_flush_auth(hapd->radius, sta->addr);
 #endif /* CONFIG_NO_RADIUS */
 
+#ifndef CONFIG_NO_VLAN
+	/* sta->wpa_sm->group needs to be released before
+	 * so that vlan_remove_dynamic can check that no
+	 * stations are left on the AP_VLAN netdev.
+	 */
+	if (sta->vlan_id_bound) {
+		/*
+		 * Need to remove the STA entry before potentially removing the
+		 * VLAN.
+		 */
+		if (hapd->iface->driver_ap_teardown &&
+		    !(sta->flags & WLAN_STA_PREAUTH))
+			hostapd_drv_sta_remove(hapd, sta->addr);
+		vlan_remove_dynamic(hapd, sta->vlan_id_bound);
+	}
+#endif /* CONFIG_NO_VLAN */
+
+
 	os_free(sta->challenge);
 
 #ifdef CONFIG_IEEE80211W
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index b89a1f4..a68f03f 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -23,6 +23,7 @@ 
 #include "ap_drv_ops.h"
 #include "vlan_init.h"
 #include "vlan_util.h"
+#include "wpa_auth_glue.h"
 
 
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
@@ -34,6 +35,7 @@ 
 struct full_dynamic_vlan {
 	int s; /* socket on which to listen for new/removed interfaces. */
 };
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
 
 
 static int ifconfig_helper(const char *if_name, int up)
@@ -90,6 +92,7 @@  static int ifconfig_down(const char *if_name)
 }
 
 
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
 /*
  * These are only available in recent linux headers (without the leading
  * underscore).
@@ -475,6 +478,54 @@  static int vlan_set_name_type(unsigned int name_type)
 #endif /* CONFIG_VLAN_NETLINK */
 
 
+static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+		       int existsok)
+{
+	int ret;
+
+	if (!if_nametoindex(vlan->ifname))
+		ret = hostapd_vlan_if_add(hapd, vlan->ifname);
+	else if (!existsok)
+		return -1;
+	else
+		ret = 0;
+
+	if (ret)
+		return ret;
+
+	ifconfig_up(vlan->ifname); /* else wpa group will fail fatal */
+
+	if (hapd->wpa_auth)
+		ret = hostapd_setup_wpa_vlan(hapd, vlan->vlan_id);
+
+	if (ret == 0)
+		return ret;
+
+	wpa_printf(MSG_ERROR, "WPA initialization for vlan %d failed (%d)",
+		   vlan->vlan_id, ret);
+	if (hostapd_desetup_wpa_vlan(hapd, vlan->vlan_id))
+		wpa_printf(MSG_ERROR, "WPA deinit of %s failed", vlan->ifname);
+
+	/* group state machine setup failed */
+	if (hostapd_vlan_if_remove(hapd, vlan->ifname))
+		wpa_printf(MSG_ERROR, "Removal of %s failed", vlan->ifname);
+
+	return ret;
+}
+
+
+static int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+	int ret;
+	ret = hostapd_desetup_wpa_vlan(hapd, vlan->vlan_id);
+	if (ret)
+		wpa_printf(MSG_ERROR, "WPA deinitialization for vlan %d failed"
+			   " (%d)", vlan->vlan_id, ret);
+
+	return hostapd_vlan_if_remove(hapd, vlan->ifname);
+}
+
+
 static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
 {
 	char vlan_ifname[IFNAMSIZ];
@@ -816,17 +867,14 @@  static int vlan_dynamic_add(struct hostapd_data *hapd,
 {
 	while (vlan) {
 		if (vlan->vlan_id != VLAN_ID_WILDCARD) {
-			if (hostapd_vlan_if_add(hapd, vlan->ifname)) {
-				if (errno != EEXIST) {
-					wpa_printf(MSG_ERROR, "VLAN: Could "
-						   "not add VLAN %s: %s",
-						   vlan->ifname,
-						   strerror(errno));
-					return -1;
-				}
+			if (vlan_if_add(hapd, vlan, 1)) {
+				wpa_printf(MSG_ERROR, "VLAN: Could not add "
+					   "VLAN %s: %s", vlan->ifname,
+					   strerror(errno));
+				return -1;
 			}
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
-			ifconfig_up(vlan->ifname);
+			vlan_newlink(vlan->ifname, hapd);
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
 		}
 
@@ -846,7 +894,7 @@  static void vlan_dynamic_remove(struct hostapd_data *hapd,
 		next = vlan->next;
 
 		if (vlan->vlan_id != VLAN_ID_WILDCARD &&
-		    hostapd_vlan_if_remove(hapd, vlan->ifname)) {
+		    vlan_if_remove(hapd, vlan)) {
 			wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
 				   "iface: %s: %s",
 				   vlan->ifname, strerror(errno));
@@ -934,19 +982,17 @@  struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
 	os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
 		    pos);
 
-	if (hostapd_vlan_if_add(hapd, n->ifname)) {
+	n->next = hapd->conf->vlan;
+	hapd->conf->vlan = n;
+
+	/* hapd->conf->vlan needs this new vlan here for wpa setup */
+	if (vlan_if_add(hapd, n, 0)) {
+		hapd->conf->vlan = n->next;
 		os_free(n);
 		n = NULL;
 		goto free_ifname;
 	}
 
-	n->next = hapd->conf->vlan;
-	hapd->conf->vlan = n;
-
-#ifdef CONFIG_FULL_DYNAMIC_VLAN
-	ifconfig_up(n->ifname);
-#endif /* CONFIG_FULL_DYNAMIC_VLAN */
-
 free_ifname:
 	os_free(ifname);
 	return n;
@@ -976,7 +1022,7 @@  int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
 		return 1;
 
 	if (vlan->dynamic_vlan == 0)
-		hostapd_vlan_if_remove(hapd, vlan->ifname);
+		vlan_if_remove(hapd, vlan);
 
 	return 0;
 }
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index f23a57a..f73d6a8 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -3374,6 +3374,95 @@  wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id)
 }
 
 
+/* enforce group state machine for vlan running, increase ref counter as iface
+ * is up. References might have been increased even if a negative value is
+ * returned.
+ * returns: -1 on error (group missing, group already failed)
+ *           0 else
+ */
+int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id)
+{
+	struct wpa_group *group;
+
+	if (wpa_auth == NULL)
+		return 0;
+
+	group = wpa_auth->group;
+	while (group) {
+		if (group->vlan_id == vlan_id)
+			break;
+		group = group->next;
+	}
+
+	if (group == NULL) {
+		group = wpa_auth_add_group(wpa_auth, vlan_id);
+		if (group == NULL)
+			return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "WPA: Ensure group state machine running for "
+		   "VLAN ID %d", vlan_id);
+
+	wpa_group_get(wpa_auth, group);
+	group->numSetupIface++;
+
+	if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+		return -1;
+
+	return 0;
+}
+
+
+/* decrease ref counter, expected to be zero afterwards
+ * returns: -1 on error (group not found, group in fail state)
+ *          -2 if wpa_group is still referenced
+ *           0 else
+ */
+int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id)
+{
+	struct wpa_group *group;
+	int ret = 0;
+
+	if (wpa_auth == NULL)
+		return 0;
+
+	group = wpa_auth->group;
+	while (group) {
+		if (group->vlan_id == vlan_id)
+			break;
+		group = group->next;
+	}
+
+	if (group == NULL)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "WPA: Try stopping group state machine for "
+		   "VLAN ID %d", vlan_id);
+
+	if (group->numSetupIface <= 0) {
+		wpa_printf(MSG_ERROR, "WPA: wpa_auth_release_group called more "
+			   "often than wpa_auth_ensure_group  for VLAN ID %d, "
+			   "skipping.", vlan_id);
+		return -1;
+	}
+	group->numSetupIface--;
+
+	if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+		ret = -1;
+
+	if (group->references > 1) {
+		wpa_printf(MSG_DEBUG, "WPA: Cannot stop group state machine for"
+				      " VLAN ID %d as references are still hold",
+				      vlan_id);
+		ret = -2;
+	}
+
+	wpa_group_put(wpa_auth, group);
+
+	return ret;
+}
+
+
 int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
 {
 	struct wpa_group *group;
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index e747806..2ca0277 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -321,4 +321,6 @@  int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
 					 struct radius_das_attrs *attr);
 void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth);
 
+int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id);
+int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id);
 #endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 7cd0b6c..6cffc74 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -649,6 +649,15 @@  int hostapd_setup_wpa(struct hostapd_data *hapd)
 
 }
 
+int hostapd_setup_wpa_vlan(struct hostapd_data *hapd, int vlan_id)
+{
+	return wpa_auth_ensure_group(hapd->wpa_auth, vlan_id);
+}
+
+int hostapd_desetup_wpa_vlan(struct hostapd_data *hapd, int vlan_id)
+{
+	return wpa_auth_release_group(hapd->wpa_auth, vlan_id);
+}
 
 void hostapd_reconfig_wpa(struct hostapd_data *hapd)
 {
diff --git a/src/ap/wpa_auth_glue.h b/src/ap/wpa_auth_glue.h
index 1b13ae7..ed8ea53 100644
--- a/src/ap/wpa_auth_glue.h
+++ b/src/ap/wpa_auth_glue.h
@@ -10,6 +10,8 @@ 
 #define WPA_AUTH_GLUE_H
 
 int hostapd_setup_wpa(struct hostapd_data *hapd);
+int hostapd_setup_wpa_vlan(struct hostapd_data *hapd, int vlan_id);
+int hostapd_desetup_wpa_vlan(struct hostapd_data *hapd, int vlan_id);
 void hostapd_reconfig_wpa(struct hostapd_data *hapd);
 void hostapd_deinit_wpa(struct hostapd_data *hapd);
 
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 57b098f..8ba5008 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -171,6 +171,7 @@  struct wpa_group {
 #endif /* CONFIG_IEEE80211W */
 	/* Number of references except those in struct wpa_group->next */
 	unsigned int references;
+	unsigned int numSetupIface;
 };