hostap/src/ap/vlan_init.c

265 lines
5.9 KiB
C
Raw Normal View History

/*
* hostapd / VLAN initialization
* Copyright 2003, Instant802 Networks, Inc.
* Copyright 2005-2006, Devicescape Software, Inc.
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.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"
static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
int existsok)
{
int ret, i;
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;
}
if (!iface_exists(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;
}
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);
}
static int vlan_dynamic_add(struct hostapd_data *hapd,
struct hostapd_vlan *vlan)
{
while (vlan) {
if (vlan->vlan_id != VLAN_ID_WILDCARD) {
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
vlan_newlink(vlan->ifname, hapd);
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
}
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;
#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 */
if (vlan->vlan_id != VLAN_ID_WILDCARD &&
vlan_if_remove(hapd, vlan)) {
wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
"iface: %s: %s",
vlan->ifname, strerror(errno));
}
#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 */
if ((hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED ||
hapd->conf->ssid.per_sta_vif) &&
!hapd->conf->vlan) {
/* dynamic vlans enabled but no (or empty) vlan_file given */
struct hostapd_vlan *vlan;
int ret;
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;
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;
}
vlan->next = hapd->conf->vlan;
hapd->conf->vlan = vlan;
}
if (vlan_dynamic_add(hapd, hapd->conf->vlan))
return -1;
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);
hapd->full_dynamic_vlan = NULL;
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
}
struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
struct hostapd_vlan *vlan,
VLAN: Separate station grouping and uplink configuration Separate uplink configuration (IEEE 802.1q VID) and grouping of stations into AP_VLAN interfaces. The int vlan_id will continue to identify the AP_VLAN interface the station should be assigned to. Each AP_VLAN interface corresponds to an instance of struct hostapd_vlan that is uniquely identified by int vlan_id within an BSS. New: Each station and struct hostapd_vlan holds a struct vlan_description vlan_desc member that describes the uplink configuration requested. Currently this is just an int untagged IEEE 802.1q VID, but can be extended to tagged VLANs and other settings easily. When the station was about to be assigned its vlan_id, vlan_desc and vlan_id will now be set simultaneously by ap_sta_set_vlan(). So sta->vlan_id can still be tested for whether the station needs to be moved to an AP_VLAN interface. To ease addition of tagged VLAN support, a member notempty is added to struct vlan_description. Is is set to 1 if an untagged or tagged VLAN assignment is requested and needs to be validated. The inverted form allows os_zalloc() to initialize an empty description. Though not depended on by the code, vlan_id assignment ensures: * vlan_id = 0 will continue to mean no AP_VLAN interface * vlan_id < 4096 will continue to mean vlan_id = untagged vlan id with no per_sta_vif and no extra tagged vlan. * vlan_id > 4096 will be used for per_sta_vif and/or tagged vlans. This way struct wpa_group and drivers API do not need to be changed in order to implement tagged VLANs or per_sta_vif support. DYNAMIC_VLAN_* will refer to (struct vlan_description).notempty only, thus grouping of the stations for per_sta_vif can be used with DYNAMIC_VLAN_DISABLED, but not with CONFIG_NO_VLAN, as struct hostapd_vlan is still used to manage AP_VLAN interfaces. MAX_VLAN_ID will be checked in hostapd_vlan_valid and during setup of VLAN interfaces and refer to IEEE 802.1q VID. VLAN_ID_WILDCARD will continue to refer to int vlan_id. Renaming vlan_id to vlan_desc when type changed from int to struct vlan_description was avoided when vlan_id was also used in a way that did not depend on its type (for example, when passed to another function). Output of "VLAN ID %d" continues to refer to int vlan_id, while "VLAN %d" will refer to untagged IEEE 802.1q VID. Signed-off-by: Michael Braun <michael-dev@fami-braun.de>
2016-01-21 14:51:56 +01:00
int vlan_id,
struct vlan_description *vlan_desc)
{
struct hostapd_vlan *n;
char ifname[IFNAMSIZ + 1], *pos;
int ret;
VLAN: Separate station grouping and uplink configuration Separate uplink configuration (IEEE 802.1q VID) and grouping of stations into AP_VLAN interfaces. The int vlan_id will continue to identify the AP_VLAN interface the station should be assigned to. Each AP_VLAN interface corresponds to an instance of struct hostapd_vlan that is uniquely identified by int vlan_id within an BSS. New: Each station and struct hostapd_vlan holds a struct vlan_description vlan_desc member that describes the uplink configuration requested. Currently this is just an int untagged IEEE 802.1q VID, but can be extended to tagged VLANs and other settings easily. When the station was about to be assigned its vlan_id, vlan_desc and vlan_id will now be set simultaneously by ap_sta_set_vlan(). So sta->vlan_id can still be tested for whether the station needs to be moved to an AP_VLAN interface. To ease addition of tagged VLAN support, a member notempty is added to struct vlan_description. Is is set to 1 if an untagged or tagged VLAN assignment is requested and needs to be validated. The inverted form allows os_zalloc() to initialize an empty description. Though not depended on by the code, vlan_id assignment ensures: * vlan_id = 0 will continue to mean no AP_VLAN interface * vlan_id < 4096 will continue to mean vlan_id = untagged vlan id with no per_sta_vif and no extra tagged vlan. * vlan_id > 4096 will be used for per_sta_vif and/or tagged vlans. This way struct wpa_group and drivers API do not need to be changed in order to implement tagged VLANs or per_sta_vif support. DYNAMIC_VLAN_* will refer to (struct vlan_description).notempty only, thus grouping of the stations for per_sta_vif can be used with DYNAMIC_VLAN_DISABLED, but not with CONFIG_NO_VLAN, as struct hostapd_vlan is still used to manage AP_VLAN interfaces. MAX_VLAN_ID will be checked in hostapd_vlan_valid and during setup of VLAN interfaces and refer to IEEE 802.1q VID. VLAN_ID_WILDCARD will continue to refer to int vlan_id. Renaming vlan_id to vlan_desc when type changed from int to struct vlan_description was avoided when vlan_id was also used in a way that did not depend on its type (for example, when passed to another function). Output of "VLAN ID %d" continues to refer to int vlan_id, while "VLAN %d" will refer to untagged IEEE 802.1q VID. Signed-off-by: Michael Braun <michael-dev@fami-braun.de>
2016-01-21 14:51:56 +01:00
if (vlan == NULL || vlan->vlan_id != VLAN_ID_WILDCARD)
return NULL;
wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)",
__func__, vlan_id, vlan->ifname);
os_strlcpy(ifname, vlan->ifname, sizeof(ifname));
pos = os_strchr(ifname, '#');
if (pos == NULL)
return NULL;
*pos++ = '\0';
n = os_zalloc(sizeof(*n));
if (n == NULL)
return NULL;
n->vlan_id = vlan_id;
VLAN: Separate station grouping and uplink configuration Separate uplink configuration (IEEE 802.1q VID) and grouping of stations into AP_VLAN interfaces. The int vlan_id will continue to identify the AP_VLAN interface the station should be assigned to. Each AP_VLAN interface corresponds to an instance of struct hostapd_vlan that is uniquely identified by int vlan_id within an BSS. New: Each station and struct hostapd_vlan holds a struct vlan_description vlan_desc member that describes the uplink configuration requested. Currently this is just an int untagged IEEE 802.1q VID, but can be extended to tagged VLANs and other settings easily. When the station was about to be assigned its vlan_id, vlan_desc and vlan_id will now be set simultaneously by ap_sta_set_vlan(). So sta->vlan_id can still be tested for whether the station needs to be moved to an AP_VLAN interface. To ease addition of tagged VLAN support, a member notempty is added to struct vlan_description. Is is set to 1 if an untagged or tagged VLAN assignment is requested and needs to be validated. The inverted form allows os_zalloc() to initialize an empty description. Though not depended on by the code, vlan_id assignment ensures: * vlan_id = 0 will continue to mean no AP_VLAN interface * vlan_id < 4096 will continue to mean vlan_id = untagged vlan id with no per_sta_vif and no extra tagged vlan. * vlan_id > 4096 will be used for per_sta_vif and/or tagged vlans. This way struct wpa_group and drivers API do not need to be changed in order to implement tagged VLANs or per_sta_vif support. DYNAMIC_VLAN_* will refer to (struct vlan_description).notempty only, thus grouping of the stations for per_sta_vif can be used with DYNAMIC_VLAN_DISABLED, but not with CONFIG_NO_VLAN, as struct hostapd_vlan is still used to manage AP_VLAN interfaces. MAX_VLAN_ID will be checked in hostapd_vlan_valid and during setup of VLAN interfaces and refer to IEEE 802.1q VID. VLAN_ID_WILDCARD will continue to refer to int vlan_id. Renaming vlan_id to vlan_desc when type changed from int to struct vlan_description was avoided when vlan_id was also used in a way that did not depend on its type (for example, when passed to another function). Output of "VLAN ID %d" continues to refer to int vlan_id, while "VLAN %d" will refer to untagged IEEE 802.1q VID. Signed-off-by: Michael Braun <michael-dev@fami-braun.de>
2016-01-21 14:51:56 +01:00
if (vlan_desc)
n->vlan_desc = *vlan_desc;
n->dynamic_vlan = 1;
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;
}
os_strlcpy(n->bridge, vlan->bridge, sizeof(n->bridge));
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;
}
return n;
}
int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
{
struct hostapd_vlan *vlan;
VLAN: Separate station grouping and uplink configuration Separate uplink configuration (IEEE 802.1q VID) and grouping of stations into AP_VLAN interfaces. The int vlan_id will continue to identify the AP_VLAN interface the station should be assigned to. Each AP_VLAN interface corresponds to an instance of struct hostapd_vlan that is uniquely identified by int vlan_id within an BSS. New: Each station and struct hostapd_vlan holds a struct vlan_description vlan_desc member that describes the uplink configuration requested. Currently this is just an int untagged IEEE 802.1q VID, but can be extended to tagged VLANs and other settings easily. When the station was about to be assigned its vlan_id, vlan_desc and vlan_id will now be set simultaneously by ap_sta_set_vlan(). So sta->vlan_id can still be tested for whether the station needs to be moved to an AP_VLAN interface. To ease addition of tagged VLAN support, a member notempty is added to struct vlan_description. Is is set to 1 if an untagged or tagged VLAN assignment is requested and needs to be validated. The inverted form allows os_zalloc() to initialize an empty description. Though not depended on by the code, vlan_id assignment ensures: * vlan_id = 0 will continue to mean no AP_VLAN interface * vlan_id < 4096 will continue to mean vlan_id = untagged vlan id with no per_sta_vif and no extra tagged vlan. * vlan_id > 4096 will be used for per_sta_vif and/or tagged vlans. This way struct wpa_group and drivers API do not need to be changed in order to implement tagged VLANs or per_sta_vif support. DYNAMIC_VLAN_* will refer to (struct vlan_description).notempty only, thus grouping of the stations for per_sta_vif can be used with DYNAMIC_VLAN_DISABLED, but not with CONFIG_NO_VLAN, as struct hostapd_vlan is still used to manage AP_VLAN interfaces. MAX_VLAN_ID will be checked in hostapd_vlan_valid and during setup of VLAN interfaces and refer to IEEE 802.1q VID. VLAN_ID_WILDCARD will continue to refer to int vlan_id. Renaming vlan_id to vlan_desc when type changed from int to struct vlan_description was avoided when vlan_id was also used in a way that did not depend on its type (for example, when passed to another function). Output of "VLAN ID %d" continues to refer to int vlan_id, while "VLAN %d" will refer to untagged IEEE 802.1q VID. Signed-off-by: Michael Braun <michael-dev@fami-braun.de>
2016-01-21 14:51:56 +01:00
if (vlan_id <= 0)
return 1;
wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)",
__func__, hapd->conf->iface, vlan_id);
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) {
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 */
}
return 0;
}