Fix init of group state machine for static VLANs

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>
This commit is contained in:
Michael Braun 2015-10-05 16:14:26 +02:00 committed by Jouni Malinen
parent a359c7bb23
commit 7cebc8e210
5 changed files with 184 additions and 33 deletions

View File

@ -172,19 +172,6 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
!(sta->flags & WLAN_STA_PREAUTH)) !(sta->flags & WLAN_STA_PREAUTH))
hostapd_drv_sta_remove(hapd, sta->addr); 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_hash_del(hapd, sta);
ap_sta_list_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); radius_client_flush_auth(hapd->radius, sta->addr);
#endif /* CONFIG_NO_RADIUS */ #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); os_free(sta->challenge);
#ifdef CONFIG_IEEE80211W #ifdef CONFIG_IEEE80211W

View File

@ -9,9 +9,9 @@
*/ */
#include "utils/includes.h" #include "utils/includes.h"
#ifdef CONFIG_FULL_DYNAMIC_VLAN
#include <net/if.h> #include <net/if.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#ifdef CONFIG_FULL_DYNAMIC_VLAN
#include <linux/sockios.h> #include <linux/sockios.h>
#include <linux/if_vlan.h> #include <linux/if_vlan.h>
#include <linux/if_bridge.h> #include <linux/if_bridge.h>
@ -21,6 +21,7 @@
#include "hostapd.h" #include "hostapd.h"
#include "ap_config.h" #include "ap_config.h"
#include "ap_drv_ops.h" #include "ap_drv_ops.h"
#include "wpa_auth.h"
#include "vlan_init.h" #include "vlan_init.h"
#include "vlan_util.h" #include "vlan_util.h"
@ -119,6 +120,8 @@ static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname)
return clean; return clean;
} }
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
static int ifconfig_helper(const char *if_name, int up) 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) static int ifconfig_down(const char *if_name)
{ {
wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", 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) { while (vlan) {
if (vlan->vlan_id != VLAN_ID_WILDCARD) { if (vlan->vlan_id != VLAN_ID_WILDCARD) {
if (hostapd_vlan_if_add(hapd, vlan->ifname)) { if (vlan_if_add(hapd, vlan, 1)) {
if (errno != EEXIST) { wpa_printf(MSG_ERROR,
wpa_printf(MSG_ERROR, "VLAN: Could " "VLAN: Could not add VLAN %s: %s",
"not add VLAN %s: %s", vlan->ifname, strerror(errno));
vlan->ifname, return -1;
strerror(errno));
return -1;
}
} }
#ifdef CONFIG_FULL_DYNAMIC_VLAN #ifdef CONFIG_FULL_DYNAMIC_VLAN
ifconfig_up(vlan->ifname); vlan_newlink(vlan->ifname, hapd);
#endif /* CONFIG_FULL_DYNAMIC_VLAN */ #endif /* CONFIG_FULL_DYNAMIC_VLAN */
} }
@ -943,7 +995,7 @@ static void vlan_dynamic_remove(struct hostapd_data *hapd,
next = vlan->next; next = vlan->next;
if (vlan->vlan_id != VLAN_ID_WILDCARD && 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 " wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
"iface: %s: %s", "iface: %s: %s",
vlan->ifname, strerror(errno)); 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, os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
pos); 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); os_free(n);
n = NULL; n = NULL;
goto free_ifname; 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: free_ifname:
os_free(ifname); os_free(ifname);
return n; return n;
@ -1073,7 +1123,7 @@ int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
return 1; return 1;
if (vlan->dynamic_vlan == 0) { if (vlan->dynamic_vlan == 0) {
hostapd_vlan_if_remove(hapd, vlan->ifname); vlan_if_remove(hapd, vlan);
#ifdef CONFIG_FULL_DYNAMIC_VLAN #ifdef CONFIG_FULL_DYNAMIC_VLAN
vlan_dellink(vlan->ifname, hapd); vlan_dellink(vlan->ifname, hapd);
#endif /* CONFIG_FULL_DYNAMIC_VLAN */ #endif /* CONFIG_FULL_DYNAMIC_VLAN */

View 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) int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
{ {
struct wpa_group *group; struct wpa_group *group;

View File

@ -325,4 +325,7 @@ int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
struct radius_das_attrs *attr); struct radius_das_attrs *attr);
void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth); 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 */ #endif /* WPA_AUTH_H */

View File

@ -171,6 +171,7 @@ struct wpa_group {
#endif /* CONFIG_IEEE80211W */ #endif /* CONFIG_IEEE80211W */
/* Number of references except those in struct wpa_group->next */ /* Number of references except those in struct wpa_group->next */
unsigned int references; unsigned int references;
unsigned int num_setup_iface;
}; };