[PATCHv2 2/3] wpa: fix init of group state machine for static vlans
Michael Braun
michael-dev
Mon Oct 5 07:14:26 PDT 2015
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 at 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 --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index d64307c..b0bf21e 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -172,19 +172,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);
@@ -274,6 +261,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 fd1c8dd..7ee2f8c 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
@@ -118,6 +119,7 @@ static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname)
return clean;
}
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
static int ifconfig_helper(const char *if_name, int up)
@@ -174,6 +176,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).
@@ -559,6 +562,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];
@@ -913,17 +964,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 */
}
@@ -943,7 +991,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));
@@ -1031,19 +1079,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;
@@ -1073,7 +1119,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);
#ifdef CONFIG_FULL_DYNAMIC_VLAN
vlan_dellink(vlan->ifname, hapd);
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 2760a3f..c514e30 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -3388,6 +3388,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 fd04f16..42d5bad 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -325,4 +325,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 f98cc50..bba1605 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -657,6 +657,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;
};
--
2.1.4
More information about the Hostap
mailing list