]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
Fix init of group state machine for static VLANs
authorMichael Braun <michael-dev@fami-braun.de>
Mon, 5 Oct 2015 14:14:26 +0000 (16:14 +0200)
committerJouni Malinen <j@w1.fi>
Mon, 5 Oct 2015 22:25:36 +0000 (01:25 +0300)
This ensures that group key is set as long as the interface exists.

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
src/ap/vlan_init.c
src/ap/wpa_auth.c
src/ap/wpa_auth.h
src/ap/wpa_auth_i.h

index d64307ccfd0850aa09bd97ec3854f784630ab1d8..d8ef858dabcf1747ecb065e8bee03982f11ee02e 100644 (file)
@@ -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
index fd1c8ddacee62e89eb9c929d7dbeb1d87f2e8654..7228ceedbd6c10b9f90751052e1aa3469275badb 100644 (file)
@@ -9,9 +9,9 @@
  */
 
 #include "utils/includes.h"
-#ifdef CONFIG_FULL_DYNAMIC_VLAN
 #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>
@@ -21,6 +21,7 @@
 #include "hostapd.h"
 #include "ap_config.h"
 #include "ap_drv_ops.h"
+#include "wpa_auth.h"
 #include "vlan_init.h"
 #include "vlan_util.h"
 
@@ -119,6 +120,8 @@ 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)
 {
@@ -167,6 +170,58 @@ static int ifconfig_up(const char *if_name)
 }
 
 
+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 = wpa_auth_ensure_group(hapd->wpa_auth, vlan->vlan_id);
+
+       if (ret == 0)
+               return ret;
+
+       wpa_printf(MSG_ERROR, "WPA initialization for VLAN %d failed (%d)",
+                  vlan->vlan_id, ret);
+       if (wpa_auth_release_group(hapd->wpa_auth, 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 = wpa_auth_release_group(hapd->wpa_auth, 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);
+}
+
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+
 static int ifconfig_down(const char *if_name)
 {
        wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
@@ -913,17 +968,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 +995,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 +1083,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 +1123,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 */
index 2760a3f3a00e508ad1c9d4a5dcde53fead6d015a..9c136efec2dabe4f0100390ffa3dd7b98f9763a8 100644 (file)
@@ -3388,6 +3388,98 @@ wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id)
 }
 
 
+/*
+ * Enforce that the group state machine for the VLAN is running, increase
+ * reference counter as interface is up. References might have been increased
+ * even if a negative value is returned.
+ * Returns: -1 on error (group missing, group already failed); otherwise, 0
+ */
+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->num_setup_iface++;
+
+       if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+               return -1;
+
+       return 0;
+}
+
+
+/*
+ * Decrease reference 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->num_setup_iface <= 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->num_setup_iface--;
+
+       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;
index fd04f169433af4ed108f213f56c702802e94135e..7d432efc1662e09fd423df144e180ae53baef695 100644 (file)
@@ -325,4 +325,7 @@ 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 */
index 57b098f2ed72a5e5399964503f9def59ea8da811..409efb9be0021fa035a308e0f7ed21d1a7f63608 100644 (file)
@@ -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 num_setup_iface;
 };