diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index d64307ccf..d8ef858da 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 fd1c8ddac..7228ceedb 100644 --- a/src/ap/vlan_init.c +++ b/src/ap/vlan_init.c @@ -9,9 +9,9 @@ */ #include "utils/includes.h" -#ifdef CONFIG_FULL_DYNAMIC_VLAN #include #include +#ifdef CONFIG_FULL_DYNAMIC_VLAN #include #include #include @@ -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 */ diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 2760a3f3a..9c136efec 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -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; diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index fd04f1694..7d432efc1 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -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 */ diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index 57b098f2e..409efb9be 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 num_setup_iface; };