2008-02-28 02:34:43 +01:00
|
|
|
/*
|
|
|
|
* hostapd / VLAN initialization
|
|
|
|
* Copyright 2003, Instant802 Networks, Inc.
|
|
|
|
* Copyright 2005-2006, Devicescape Software, Inc.
|
2009-12-24 23:17:07 +01:00
|
|
|
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
|
2008-02-28 02:34:43 +01:00
|
|
|
*
|
2013-12-24 21:59:52 +01:00
|
|
|
* This software may be distributed under the terms of the BSD license.
|
|
|
|
* See README for more details.
|
2008-02-28 02:34:43 +01:00
|
|
|
*/
|
|
|
|
|
2009-12-25 23:05:40 +01:00
|
|
|
#include "utils/includes.h"
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2009-12-25 23:05:40 +01:00
|
|
|
#include "utils/common.h"
|
2008-02-28 02:34:43 +01:00
|
|
|
#include "hostapd.h"
|
2009-12-25 23:05:40 +01:00
|
|
|
#include "ap_config.h"
|
2010-11-24 15:34:49 +01:00
|
|
|
#include "ap_drv_ops.h"
|
2015-10-05 16:14:26 +02:00
|
|
|
#include "wpa_auth.h"
|
2008-02-28 02:34:43 +01:00
|
|
|
#include "vlan_init.h"
|
2012-08-10 11:55:33 +02:00
|
|
|
#include "vlan_util.h"
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
|
2015-10-05 16:14:26 +02:00
|
|
|
static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
|
|
|
|
int existsok)
|
|
|
|
{
|
2020-02-29 15:52:39 +01:00
|
|
|
int ret;
|
|
|
|
#ifdef CONFIG_WEP
|
|
|
|
int i;
|
2015-10-05 16:14:27 +02:00
|
|
|
|
|
|
|
for (i = 0; i < NUM_WEP_KEYS; i++) {
|
|
|
|
if (!hapd->conf->ssid.wep.key[i])
|
|
|
|
continue;
|
|
|
|
wpa_printf(MSG_ERROR,
|
|
|
|
"VLAN: Refusing to set up VLAN iface %s with WEP",
|
|
|
|
vlan->ifname);
|
|
|
|
return -1;
|
|
|
|
}
|
2020-02-29 15:52:39 +01:00
|
|
|
#endif /* CONFIG_WEP */
|
2015-10-05 16:14:26 +02:00
|
|
|
|
2016-03-25 17:00:44 +01:00
|
|
|
if (!iface_exists(vlan->ifname))
|
2015-10-05 16:14:26 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-03-25 16:43:27 +01:00
|
|
|
int vlan_if_remove(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
|
2015-10-05 16:14:26 +02:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
static int vlan_dynamic_add(struct hostapd_data *hapd,
|
|
|
|
struct hostapd_vlan *vlan)
|
|
|
|
{
|
|
|
|
while (vlan) {
|
2010-04-17 08:45:18 +02:00
|
|
|
if (vlan->vlan_id != VLAN_ID_WILDCARD) {
|
2015-10-05 16:14:26 +02:00
|
|
|
if (vlan_if_add(hapd, vlan, 1)) {
|
|
|
|
wpa_printf(MSG_ERROR,
|
|
|
|
"VLAN: Could not add VLAN %s: %s",
|
|
|
|
vlan->ifname, strerror(errno));
|
|
|
|
return -1;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
2010-04-17 20:01:35 +02:00
|
|
|
#ifdef CONFIG_FULL_DYNAMIC_VLAN
|
2015-10-05 16:14:26 +02:00
|
|
|
vlan_newlink(vlan->ifname, hapd);
|
2010-04-17 20:01:35 +02:00
|
|
|
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
vlan = vlan->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void vlan_dynamic_remove(struct hostapd_data *hapd,
|
|
|
|
struct hostapd_vlan *vlan)
|
|
|
|
{
|
|
|
|
struct hostapd_vlan *next;
|
|
|
|
|
|
|
|
while (vlan) {
|
|
|
|
next = vlan->next;
|
|
|
|
|
2016-01-21 14:51:59 +01:00
|
|
|
#ifdef CONFIG_FULL_DYNAMIC_VLAN
|
|
|
|
/* vlan_dellink() takes care of cleanup and interface removal */
|
|
|
|
if (vlan->vlan_id != VLAN_ID_WILDCARD)
|
|
|
|
vlan_dellink(vlan->ifname, hapd);
|
|
|
|
#else /* CONFIG_FULL_DYNAMIC_VLAN */
|
2008-02-28 02:34:43 +01:00
|
|
|
if (vlan->vlan_id != VLAN_ID_WILDCARD &&
|
2015-10-05 16:14:26 +02:00
|
|
|
vlan_if_remove(hapd, vlan)) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
|
|
|
|
"iface: %s: %s",
|
|
|
|
vlan->ifname, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
|
|
|
|
|
|
|
|
vlan = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int vlan_init(struct hostapd_data *hapd)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_FULL_DYNAMIC_VLAN
|
|
|
|
hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd);
|
|
|
|
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
|
|
|
|
|
2016-01-21 14:52:00 +01:00
|
|
|
if ((hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED ||
|
|
|
|
hapd->conf->ssid.per_sta_vif) &&
|
2013-04-27 21:53:34 +02:00
|
|
|
!hapd->conf->vlan) {
|
|
|
|
/* dynamic vlans enabled but no (or empty) vlan_file given */
|
|
|
|
struct hostapd_vlan *vlan;
|
2018-08-22 19:47:32 +02:00
|
|
|
int ret;
|
|
|
|
|
2013-04-27 21:53:34 +02:00
|
|
|
vlan = os_zalloc(sizeof(*vlan));
|
|
|
|
if (vlan == NULL) {
|
|
|
|
wpa_printf(MSG_ERROR, "Out of memory while assigning "
|
|
|
|
"VLAN interfaces");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
vlan->vlan_id = VLAN_ID_WILDCARD;
|
2018-08-22 19:47:32 +02:00
|
|
|
ret = os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#",
|
|
|
|
hapd->conf->iface);
|
|
|
|
if (ret >= (int) sizeof(vlan->ifname)) {
|
|
|
|
wpa_printf(MSG_WARNING,
|
|
|
|
"VLAN: Interface name was truncated to %s",
|
|
|
|
vlan->ifname);
|
|
|
|
} else if (ret < 0) {
|
|
|
|
os_free(vlan);
|
|
|
|
return ret;
|
|
|
|
}
|
2013-08-04 20:45:50 +02:00
|
|
|
vlan->next = hapd->conf->vlan;
|
|
|
|
hapd->conf->vlan = vlan;
|
2013-04-27 21:53:34 +02:00
|
|
|
}
|
|
|
|
|
2010-04-17 08:48:27 +02:00
|
|
|
if (vlan_dynamic_add(hapd, hapd->conf->vlan))
|
|
|
|
return -1;
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void vlan_deinit(struct hostapd_data *hapd)
|
|
|
|
{
|
|
|
|
vlan_dynamic_remove(hapd, hapd->conf->vlan);
|
|
|
|
|
|
|
|
#ifdef CONFIG_FULL_DYNAMIC_VLAN
|
|
|
|
full_dynamic_vlan_deinit(hapd->full_dynamic_vlan);
|
2013-09-25 17:14:13 +02:00
|
|
|
hapd->full_dynamic_vlan = NULL;
|
2008-02-28 02:34:43 +01:00
|
|
|
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
|
|
|
|
struct hostapd_vlan *vlan,
|
2016-01-21 14:51:56 +01:00
|
|
|
int vlan_id,
|
|
|
|
struct vlan_description *vlan_desc)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
2016-02-24 12:53:35 +01:00
|
|
|
struct hostapd_vlan *n;
|
|
|
|
char ifname[IFNAMSIZ + 1], *pos;
|
2018-12-23 23:44:36 +01:00
|
|
|
int ret;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2016-01-21 14:51:56 +01:00
|
|
|
if (vlan == NULL || vlan->vlan_id != VLAN_ID_WILDCARD)
|
2008-02-28 02:34:43 +01:00
|
|
|
return NULL;
|
|
|
|
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)",
|
|
|
|
__func__, vlan_id, vlan->ifname);
|
2016-02-24 12:53:35 +01:00
|
|
|
os_strlcpy(ifname, vlan->ifname, sizeof(ifname));
|
2008-02-28 02:34:43 +01:00
|
|
|
pos = os_strchr(ifname, '#');
|
2014-03-17 14:16:24 +01:00
|
|
|
if (pos == NULL)
|
2016-02-24 12:53:35 +01:00
|
|
|
return NULL;
|
2008-02-28 02:34:43 +01:00
|
|
|
*pos++ = '\0';
|
|
|
|
|
|
|
|
n = os_zalloc(sizeof(*n));
|
2014-03-17 14:16:24 +01:00
|
|
|
if (n == NULL)
|
2016-02-24 12:53:35 +01:00
|
|
|
return NULL;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
n->vlan_id = vlan_id;
|
2016-01-21 14:51:56 +01:00
|
|
|
if (vlan_desc)
|
|
|
|
n->vlan_desc = *vlan_desc;
|
2008-02-28 02:34:43 +01:00
|
|
|
n->dynamic_vlan = 1;
|
|
|
|
|
2018-12-23 23:44:36 +01:00
|
|
|
ret = os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s",
|
|
|
|
ifname, vlan_id, pos);
|
|
|
|
if (os_snprintf_error(sizeof(n->ifname), ret)) {
|
|
|
|
os_free(n);
|
|
|
|
return NULL;
|
|
|
|
}
|
2018-11-14 17:50:23 +01:00
|
|
|
os_strlcpy(n->bridge, vlan->bridge, sizeof(n->bridge));
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2015-10-05 16:14:26 +02:00
|
|
|
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;
|
2008-02-28 02:34:43 +01:00
|
|
|
os_free(n);
|
2014-03-17 14:16:24 +01:00
|
|
|
n = NULL;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
|
|
|
|
{
|
|
|
|
struct hostapd_vlan *vlan;
|
|
|
|
|
2016-01-21 14:51:56 +01:00
|
|
|
if (vlan_id <= 0)
|
2008-02-28 02:34:43 +01:00
|
|
|
return 1;
|
|
|
|
|
2015-04-13 16:06:12 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)",
|
|
|
|
__func__, hapd->conf->iface, vlan_id);
|
2010-04-15 19:35:51 +02:00
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
vlan = hapd->conf->vlan;
|
|
|
|
while (vlan) {
|
|
|
|
if (vlan->vlan_id == vlan_id && vlan->dynamic_vlan > 0) {
|
|
|
|
vlan->dynamic_vlan--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
vlan = vlan->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vlan == NULL)
|
|
|
|
return 1;
|
|
|
|
|
Fix removal of tagged interface and bridge when multiple BSS share them
Currently, if multiple bss share are bridge and tagged vlan interface,
only the first instance of struct hostapd_vlan for this vlanid will have
the DVLAN_CLEAN_VLAN flag added. Thus, when this instance is removed,
the tagged vlan interface will be removed from bridge, thought other bss
might still need it. Similarily, the bridge will be left over, as the
does not have zero ports when the first instance of a struct
hostapd_vlan is freed.
This patch fixes this by having a global (per process) reference counter
for dynamic tagged vlan and dynamically created bridge interfaces, so
they are only removed after all local users are freed. (struct
hapd_interfaces *)->vlan_priv is used to hold src/ap/vlan_init.c global
per-process data like drv_priv does; right now this is only used for the
interface reference counting, but could get extended when needed. Then
possibly some vlan_global_init / vlan_global_deinit should be added, but
this is not required right now.
Additionally, vlan->configured is checked to avoid reference counter
decreasing before vlan_newlink increased them.
In order to avoid race conditions, vlan_dellink is called explicitly
after hostapd_vlan_if_remove. Otherwise there would be a short timeframe
between hostapd_vlan_if_remove and vlan_dellink during which the struct
hostapd_vlan still exists, so ap_sta_bind_vlan would try to attach
stations to it.
Signed-off-by: Michael Braun <michael-dev@fami-braun.de>
2015-04-27 09:08:03 +02:00
|
|
|
if (vlan->dynamic_vlan == 0) {
|
2015-10-05 16:14:26 +02:00
|
|
|
vlan_if_remove(hapd, vlan);
|
Fix removal of tagged interface and bridge when multiple BSS share them
Currently, if multiple bss share are bridge and tagged vlan interface,
only the first instance of struct hostapd_vlan for this vlanid will have
the DVLAN_CLEAN_VLAN flag added. Thus, when this instance is removed,
the tagged vlan interface will be removed from bridge, thought other bss
might still need it. Similarily, the bridge will be left over, as the
does not have zero ports when the first instance of a struct
hostapd_vlan is freed.
This patch fixes this by having a global (per process) reference counter
for dynamic tagged vlan and dynamically created bridge interfaces, so
they are only removed after all local users are freed. (struct
hapd_interfaces *)->vlan_priv is used to hold src/ap/vlan_init.c global
per-process data like drv_priv does; right now this is only used for the
interface reference counting, but could get extended when needed. Then
possibly some vlan_global_init / vlan_global_deinit should be added, but
this is not required right now.
Additionally, vlan->configured is checked to avoid reference counter
decreasing before vlan_newlink increased them.
In order to avoid race conditions, vlan_dellink is called explicitly
after hostapd_vlan_if_remove. Otherwise there would be a short timeframe
between hostapd_vlan_if_remove and vlan_dellink during which the struct
hostapd_vlan still exists, so ap_sta_bind_vlan would try to attach
stations to it.
Signed-off-by: Michael Braun <michael-dev@fami-braun.de>
2015-04-27 09:08:03 +02:00
|
|
|
#ifdef CONFIG_FULL_DYNAMIC_VLAN
|
|
|
|
vlan_dellink(vlan->ifname, hapd);
|
|
|
|
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
|
|
|
|
}
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|