!(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);
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
*/
#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>
#include "hostapd.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
+#include "wpa_auth.h"
#include "vlan_init.h"
#include "vlan_util.h"
return clean;
}
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
static int ifconfig_helper(const char *if_name, int up)
{
}
+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);
{
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 */
}
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));
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;
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 */
}
+/*
+ * 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;