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"
|
2015-04-26 12:16:16 +02:00
|
|
|
#include <net/if.h>
|
|
|
|
#include <sys/ioctl.h>
|
2015-10-05 16:14:26 +02:00
|
|
|
#ifdef CONFIG_FULL_DYNAMIC_VLAN
|
2015-04-26 12:16:16 +02:00
|
|
|
#include <linux/sockios.h>
|
|
|
|
#include <linux/if_vlan.h>
|
|
|
|
#include <linux/if_bridge.h>
|
|
|
|
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
|
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
|
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_FULL_DYNAMIC_VLAN
|
|
|
|
|
2009-04-09 12:40:12 +02:00
|
|
|
#include "drivers/priv_netlink.h"
|
2009-12-25 23:05:40 +01:00
|
|
|
#include "utils/eloop.h"
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
|
|
|
|
struct full_dynamic_vlan {
|
|
|
|
int s; /* socket on which to listen for new/removed interfaces. */
|
|
|
|
};
|
|
|
|
|
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
|
|
|
#define DVLAN_CLEAN_BR 0x1
|
|
|
|
#define DVLAN_CLEAN_VLAN 0x2
|
|
|
|
#define DVLAN_CLEAN_VLAN_PORT 0x4
|
|
|
|
|
|
|
|
struct dynamic_iface {
|
|
|
|
char ifname[IFNAMSIZ + 1];
|
|
|
|
int usage;
|
|
|
|
int clean;
|
|
|
|
struct dynamic_iface *next;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* Increment ref counter for ifname and add clean flag.
|
|
|
|
* If not in list, add it only if some flags are given.
|
|
|
|
*/
|
|
|
|
static void dyn_iface_get(struct hostapd_data *hapd, const char *ifname,
|
|
|
|
int clean)
|
|
|
|
{
|
|
|
|
struct dynamic_iface *next, **dynamic_ifaces;
|
|
|
|
struct hapd_interfaces *interfaces;
|
|
|
|
|
|
|
|
interfaces = hapd->iface->interfaces;
|
|
|
|
dynamic_ifaces = &interfaces->vlan_priv;
|
|
|
|
|
|
|
|
for (next = *dynamic_ifaces; next; next = next->next) {
|
|
|
|
if (os_strcmp(ifname, next->ifname) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (next) {
|
|
|
|
next->usage++;
|
|
|
|
next->clean |= clean;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!clean)
|
|
|
|
return;
|
|
|
|
|
|
|
|
next = os_zalloc(sizeof(*next));
|
|
|
|
if (!next)
|
|
|
|
return;
|
|
|
|
os_strlcpy(next->ifname, ifname, sizeof(next->ifname));
|
|
|
|
next->usage = 1;
|
|
|
|
next->clean = clean;
|
|
|
|
next->next = *dynamic_ifaces;
|
|
|
|
*dynamic_ifaces = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Decrement reference counter for given ifname.
|
|
|
|
* Return clean flag iff reference counter was decreased to zero, else zero
|
|
|
|
*/
|
|
|
|
static int dyn_iface_put(struct hostapd_data *hapd, const char *ifname)
|
|
|
|
{
|
|
|
|
struct dynamic_iface *next, *prev = NULL, **dynamic_ifaces;
|
|
|
|
struct hapd_interfaces *interfaces;
|
|
|
|
int clean;
|
|
|
|
|
|
|
|
interfaces = hapd->iface->interfaces;
|
|
|
|
dynamic_ifaces = &interfaces->vlan_priv;
|
|
|
|
|
|
|
|
for (next = *dynamic_ifaces; next; next = next->next) {
|
|
|
|
if (os_strcmp(ifname, next->ifname) == 0)
|
|
|
|
break;
|
|
|
|
prev = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!next)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
next->usage--;
|
|
|
|
if (next->usage)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (prev)
|
|
|
|
prev->next = next->next;
|
|
|
|
else
|
|
|
|
*dynamic_ifaces = next->next;
|
|
|
|
clean = next->clean;
|
|
|
|
os_free(next);
|
|
|
|
|
|
|
|
return clean;
|
|
|
|
}
|
|
|
|
|
2015-10-05 16:14:26 +02:00
|
|
|
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
static int ifconfig_helper(const char *if_name, int up)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
struct ifreq ifr;
|
|
|
|
|
|
|
|
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
|
|
|
"failed: %s", __func__, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
os_memset(&ifr, 0, sizeof(ifr));
|
|
|
|
os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
|
|
|
|
|
|
|
|
if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed "
|
|
|
|
"for interface %s: %s",
|
|
|
|
__func__, if_name, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (up)
|
|
|
|
ifr.ifr_flags |= IFF_UP;
|
|
|
|
else
|
|
|
|
ifr.ifr_flags &= ~IFF_UP;
|
|
|
|
|
|
|
|
if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed "
|
|
|
|
"for interface %s (up=%d): %s",
|
|
|
|
__func__, if_name, up, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int ifconfig_up(const char *if_name)
|
|
|
|
{
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name);
|
2008-02-28 02:34:43 +01:00
|
|
|
return ifconfig_helper(if_name, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-05 16:14:26 +02:00
|
|
|
static int vlan_if_add(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
|
|
|
|
int existsok)
|
|
|
|
{
|
2015-10-05 16:14:27 +02:00
|
|
|
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;
|
|
|
|
}
|
2015-10-05 16:14:26 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
static int ifconfig_down(const char *if_name)
|
|
|
|
{
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name);
|
2008-02-28 02:34:43 +01:00
|
|
|
return ifconfig_helper(if_name, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* These are only available in recent linux headers (without the leading
|
|
|
|
* underscore).
|
|
|
|
*/
|
|
|
|
#define _GET_VLAN_REALDEV_NAME_CMD 8
|
|
|
|
#define _GET_VLAN_VID_CMD 9
|
|
|
|
|
|
|
|
/* This value should be 256 ONLY. If it is something else, then hostapd
|
|
|
|
* might crash!, as this value has been hard-coded in 2.4.x kernel
|
|
|
|
* bridging code.
|
|
|
|
*/
|
|
|
|
#define MAX_BR_PORTS 256
|
|
|
|
|
|
|
|
static int br_delif(const char *br_name, const char *if_name)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
struct ifreq ifr;
|
|
|
|
unsigned long args[2];
|
|
|
|
int if_index;
|
|
|
|
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name);
|
2008-02-28 02:34:43 +01:00
|
|
|
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
|
|
|
"failed: %s", __func__, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if_index = if_nametoindex(if_name);
|
|
|
|
|
|
|
|
if (if_index == 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
|
|
|
|
"interface index for '%s'",
|
|
|
|
__func__, if_name);
|
2008-02-28 02:34:43 +01:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
args[0] = BRCTL_DEL_IF;
|
|
|
|
args[1] = if_index;
|
|
|
|
|
|
|
|
os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
|
|
|
|
ifr.ifr_data = (__caddr_t) args;
|
|
|
|
|
|
|
|
if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) {
|
|
|
|
/* No error if interface already removed. */
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
|
|
|
|
"BRCTL_DEL_IF] failed for br_name=%s if_name=%s: "
|
|
|
|
"%s", __func__, br_name, if_name, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Add interface 'if_name' to the bridge 'br_name'
|
|
|
|
|
|
|
|
returns -1 on error
|
|
|
|
returns 1 if the interface is already part of the bridge
|
|
|
|
returns 0 otherwise
|
|
|
|
*/
|
|
|
|
static int br_addif(const char *br_name, const char *if_name)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
struct ifreq ifr;
|
|
|
|
unsigned long args[2];
|
|
|
|
int if_index;
|
|
|
|
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name);
|
2008-02-28 02:34:43 +01:00
|
|
|
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
|
|
|
"failed: %s", __func__, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if_index = if_nametoindex(if_name);
|
|
|
|
|
|
|
|
if (if_index == 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
|
|
|
|
"interface index for '%s'",
|
|
|
|
__func__, if_name);
|
2008-02-28 02:34:43 +01:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
args[0] = BRCTL_ADD_IF;
|
|
|
|
args[1] = if_index;
|
|
|
|
|
|
|
|
os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
|
|
|
|
ifr.ifr_data = (__caddr_t) args;
|
|
|
|
|
|
|
|
if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
|
|
|
|
if (errno == EBUSY) {
|
|
|
|
/* The interface is already added. */
|
|
|
|
close(fd);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
|
|
|
|
"BRCTL_ADD_IF] failed for br_name=%s if_name=%s: "
|
|
|
|
"%s", __func__, br_name, if_name, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int br_delbr(const char *br_name)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
unsigned long arg[2];
|
|
|
|
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name);
|
2008-02-28 02:34:43 +01:00
|
|
|
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
|
|
|
"failed: %s", __func__, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
arg[0] = BRCTL_DEL_BRIDGE;
|
|
|
|
arg[1] = (unsigned long) br_name;
|
|
|
|
|
|
|
|
if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) {
|
|
|
|
/* No error if bridge already removed. */
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for "
|
|
|
|
"%s: %s", __func__, br_name, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Add a bridge with the name 'br_name'.
|
|
|
|
|
|
|
|
returns -1 on error
|
|
|
|
returns 1 if the bridge already exists
|
|
|
|
returns 0 otherwise
|
|
|
|
*/
|
|
|
|
static int br_addbr(const char *br_name)
|
|
|
|
{
|
|
|
|
int fd;
|
2010-04-15 23:48:32 +02:00
|
|
|
unsigned long arg[4];
|
|
|
|
struct ifreq ifr;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name);
|
2008-02-28 02:34:43 +01:00
|
|
|
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
|
|
|
"failed: %s", __func__, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
arg[0] = BRCTL_ADD_BRIDGE;
|
|
|
|
arg[1] = (unsigned long) br_name;
|
|
|
|
|
|
|
|
if (ioctl(fd, SIOCGIFBR, arg) < 0) {
|
|
|
|
if (errno == EEXIST) {
|
|
|
|
/* The bridge is already added. */
|
|
|
|
close(fd);
|
|
|
|
return 1;
|
|
|
|
} else {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE "
|
|
|
|
"failed for %s: %s",
|
|
|
|
__func__, br_name, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-15 23:48:32 +02:00
|
|
|
/* Decrease forwarding delay to avoid EAPOL timeouts. */
|
|
|
|
os_memset(&ifr, 0, sizeof(ifr));
|
|
|
|
os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ);
|
|
|
|
arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
|
|
|
|
arg[1] = 1;
|
|
|
|
arg[2] = 0;
|
|
|
|
arg[3] = 0;
|
|
|
|
ifr.ifr_data = (char *) &arg;
|
|
|
|
if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
|
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: "
|
|
|
|
"BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for "
|
|
|
|
"%s: %s", __func__, br_name, strerror(errno));
|
|
|
|
/* Continue anyway */
|
|
|
|
}
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
close(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int br_getnumports(const char *br_name)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
int i;
|
|
|
|
int port_cnt = 0;
|
|
|
|
unsigned long arg[4];
|
|
|
|
int ifindices[MAX_BR_PORTS];
|
|
|
|
struct ifreq ifr;
|
|
|
|
|
|
|
|
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
|
|
|
"failed: %s", __func__, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
arg[0] = BRCTL_GET_PORT_LIST;
|
|
|
|
arg[1] = (unsigned long) ifindices;
|
|
|
|
arg[2] = MAX_BR_PORTS;
|
|
|
|
arg[3] = 0;
|
|
|
|
|
|
|
|
os_memset(ifindices, 0, sizeof(ifindices));
|
|
|
|
os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
|
|
|
|
ifr.ifr_data = (__caddr_t) arg;
|
|
|
|
|
|
|
|
if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST "
|
|
|
|
"failed for %s: %s",
|
|
|
|
__func__, br_name, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 1; i < MAX_BR_PORTS; i++) {
|
|
|
|
if (ifindices[i] > 0) {
|
|
|
|
port_cnt++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
return port_cnt;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-10 11:55:33 +02:00
|
|
|
#ifndef CONFIG_VLAN_NETLINK
|
|
|
|
|
|
|
|
int vlan_rem(const char *if_name)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
struct vlan_ioctl_args if_request;
|
|
|
|
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name);
|
2008-02-28 02:34:43 +01:00
|
|
|
if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
|
|
|
|
if_name);
|
2008-02-28 02:34:43 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
|
|
|
"failed: %s", __func__, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
os_memset(&if_request, 0, sizeof(if_request));
|
|
|
|
|
|
|
|
os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
|
|
|
|
if_request.cmd = DEL_VLAN_CMD;
|
|
|
|
|
|
|
|
if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: "
|
|
|
|
"%s", __func__, if_name, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Add a vlan interface with VLAN ID 'vid' and tagged interface
|
|
|
|
'if_name'.
|
|
|
|
|
|
|
|
returns -1 on error
|
|
|
|
returns 1 if the interface already exists
|
|
|
|
returns 0 otherwise
|
|
|
|
*/
|
2012-08-10 11:55:33 +02:00
|
|
|
int vlan_add(const char *if_name, int vid, const char *vlan_if_name)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
struct vlan_ioctl_args if_request;
|
|
|
|
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)",
|
|
|
|
if_name, vid);
|
2008-02-28 02:34:43 +01:00
|
|
|
ifconfig_up(if_name);
|
|
|
|
|
|
|
|
if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
|
|
|
|
if_name);
|
2008-02-28 02:34:43 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
|
|
|
"failed: %s", __func__, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
os_memset(&if_request, 0, sizeof(if_request));
|
|
|
|
|
|
|
|
/* Determine if a suitable vlan device already exists. */
|
|
|
|
|
|
|
|
os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d",
|
|
|
|
vid);
|
|
|
|
|
|
|
|
if_request.cmd = _GET_VLAN_VID_CMD;
|
|
|
|
|
|
|
|
if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0) {
|
|
|
|
|
|
|
|
if (if_request.u.VID == vid) {
|
|
|
|
if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD;
|
|
|
|
|
|
|
|
if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 &&
|
|
|
|
os_strncmp(if_request.u.device2, if_name,
|
|
|
|
sizeof(if_request.u.device2)) == 0) {
|
|
|
|
close(fd);
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "VLAN: vlan_add: "
|
|
|
|
"if_name %s exists already",
|
|
|
|
if_request.device1);
|
2008-02-28 02:34:43 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A suitable vlan device does not already exist, add one. */
|
|
|
|
|
|
|
|
os_memset(&if_request, 0, sizeof(if_request));
|
|
|
|
os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1));
|
|
|
|
if_request.u.VID = vid;
|
|
|
|
if_request.cmd = ADD_VLAN_CMD;
|
|
|
|
|
|
|
|
if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: ADD_VLAN_CMD failed for %s: "
|
|
|
|
"%s",
|
|
|
|
__func__, if_request.device1, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int vlan_set_name_type(unsigned int name_type)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
struct vlan_ioctl_args if_request;
|
|
|
|
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)",
|
|
|
|
name_type);
|
2008-02-28 02:34:43 +01:00
|
|
|
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
|
|
|
|
"failed: %s", __func__, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
os_memset(&if_request, 0, sizeof(if_request));
|
|
|
|
|
|
|
|
if_request.u.name_type = name_type;
|
|
|
|
if_request.cmd = SET_VLAN_NAME_TYPE_CMD;
|
|
|
|
if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: SET_VLAN_NAME_TYPE_CMD "
|
|
|
|
"name_type=%u failed: %s",
|
|
|
|
__func__, name_type, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
close(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-10 11:55:33 +02:00
|
|
|
#endif /* CONFIG_VLAN_NETLINK */
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2016-01-21 14:51:58 +01:00
|
|
|
static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface,
|
|
|
|
const char *br_name, int vid,
|
|
|
|
struct hostapd_data *hapd)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
|
|
|
char vlan_ifname[IFNAMSIZ];
|
2016-01-21 14:51:58 +01:00
|
|
|
int clean;
|
|
|
|
|
|
|
|
if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
|
|
|
|
os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
|
|
|
|
tagged_interface, vid);
|
|
|
|
else
|
|
|
|
os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid);
|
|
|
|
|
|
|
|
clean = 0;
|
|
|
|
ifconfig_up(tagged_interface);
|
|
|
|
if (!vlan_add(tagged_interface, vid, vlan_ifname))
|
|
|
|
clean |= DVLAN_CLEAN_VLAN;
|
|
|
|
|
|
|
|
if (!br_addif(br_name, vlan_ifname))
|
|
|
|
clean |= DVLAN_CLEAN_VLAN_PORT;
|
|
|
|
|
|
|
|
dyn_iface_get(hapd, vlan_ifname, clean);
|
|
|
|
|
|
|
|
ifconfig_up(vlan_ifname);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd, int vid)
|
|
|
|
{
|
|
|
|
char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
|
|
|
|
|
|
|
|
if (hapd->conf->vlan_bridge[0]) {
|
|
|
|
os_snprintf(br_name, IFNAMSIZ, "%s%d",
|
|
|
|
hapd->conf->vlan_bridge, vid);
|
|
|
|
} else if (tagged_interface) {
|
|
|
|
os_snprintf(br_name, IFNAMSIZ, "br%s.%d",
|
|
|
|
tagged_interface, vid);
|
|
|
|
} else {
|
|
|
|
os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void vlan_get_bridge(const char *br_name, struct hostapd_data *hapd,
|
|
|
|
int vid)
|
|
|
|
{
|
2008-02-28 02:34:43 +01:00
|
|
|
char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
|
2012-08-10 11:38:17 +02:00
|
|
|
int vlan_naming = hapd->conf->ssid.vlan_naming;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2016-01-21 14:51:58 +01:00
|
|
|
dyn_iface_get(hapd, br_name, br_addbr(br_name) ? 0 : DVLAN_CLEAN_BR);
|
2010-04-15 19:35:51 +02:00
|
|
|
|
2016-01-21 14:51:58 +01:00
|
|
|
ifconfig_up(br_name);
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2016-01-21 14:51:58 +01:00
|
|
|
if (tagged_interface)
|
|
|
|
vlan_newlink_tagged(vlan_naming, tagged_interface, br_name,
|
|
|
|
vid, hapd);
|
|
|
|
}
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
|
2016-01-21 14:51:58 +01:00
|
|
|
static void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
|
|
|
|
{
|
|
|
|
char br_name[IFNAMSIZ];
|
|
|
|
struct hostapd_vlan *vlan;
|
2016-01-21 14:52:00 +01:00
|
|
|
int untagged, *tagged, i, notempty;
|
2012-08-10 11:38:17 +02:00
|
|
|
|
2016-01-21 14:51:58 +01:00
|
|
|
wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2016-01-21 14:51:58 +01:00
|
|
|
for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
|
|
|
|
if (vlan->configured ||
|
|
|
|
os_strcmp(ifname, vlan->ifname) != 0)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!vlan)
|
|
|
|
return;
|
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
|
|
|
|
2016-01-21 14:51:58 +01:00
|
|
|
vlan->configured = 1;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2016-01-21 14:52:00 +01:00
|
|
|
notempty = vlan->vlan_desc.notempty;
|
2016-01-21 14:51:58 +01:00
|
|
|
untagged = vlan->vlan_desc.untagged;
|
2016-01-21 14:51:59 +01:00
|
|
|
tagged = vlan->vlan_desc.tagged;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2016-01-21 14:52:00 +01:00
|
|
|
if (!notempty) {
|
|
|
|
/* Non-VLAN STA */
|
|
|
|
if (hapd->conf->bridge[0] &&
|
|
|
|
!br_addif(hapd->conf->bridge, ifname))
|
|
|
|
vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
|
|
|
|
} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
|
2016-01-21 14:51:58 +01:00
|
|
|
vlan_bridge_name(br_name, hapd, untagged);
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2016-01-21 14:51:58 +01:00
|
|
|
vlan_get_bridge(br_name, hapd, untagged);
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2016-01-21 14:51:58 +01:00
|
|
|
if (!br_addif(br_name, ifname))
|
|
|
|
vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
2016-01-21 14:51:58 +01:00
|
|
|
|
2016-01-21 14:51:59 +01:00
|
|
|
for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
|
|
|
|
if (tagged[i] == untagged ||
|
|
|
|
tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
|
|
|
|
(i > 0 && tagged[i] == tagged[i - 1]))
|
|
|
|
continue;
|
|
|
|
vlan_bridge_name(br_name, hapd, tagged[i]);
|
|
|
|
vlan_get_bridge(br_name, hapd, tagged[i]);
|
|
|
|
vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
|
|
|
|
ifname, br_name, tagged[i], hapd);
|
|
|
|
}
|
|
|
|
|
2016-01-21 14:51:58 +01:00
|
|
|
ifconfig_up(ifname);
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-21 14:51:58 +01:00
|
|
|
static void vlan_dellink_tagged(int vlan_naming, const char *tagged_interface,
|
|
|
|
const char *br_name, int vid,
|
|
|
|
struct hostapd_data *hapd)
|
2008-02-28 02:34:43 +01:00
|
|
|
{
|
|
|
|
char vlan_ifname[IFNAMSIZ];
|
2016-01-21 14:51:58 +01:00
|
|
|
int clean;
|
|
|
|
|
|
|
|
if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
|
|
|
|
os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
|
|
|
|
tagged_interface, vid);
|
|
|
|
else
|
|
|
|
os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid);
|
|
|
|
|
|
|
|
clean = dyn_iface_put(hapd, vlan_ifname);
|
|
|
|
|
|
|
|
if (clean & DVLAN_CLEAN_VLAN_PORT)
|
|
|
|
br_delif(br_name, vlan_ifname);
|
|
|
|
|
|
|
|
if (clean & DVLAN_CLEAN_VLAN) {
|
|
|
|
ifconfig_down(vlan_ifname);
|
|
|
|
vlan_rem(vlan_ifname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void vlan_put_bridge(const char *br_name, struct hostapd_data *hapd,
|
|
|
|
int vid)
|
|
|
|
{
|
|
|
|
int clean;
|
2008-02-28 02:34:43 +01:00
|
|
|
char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
|
2012-08-10 11:38:17 +02:00
|
|
|
int vlan_naming = hapd->conf->ssid.vlan_naming;
|
2016-01-21 14:51:58 +01:00
|
|
|
|
|
|
|
if (tagged_interface)
|
|
|
|
vlan_dellink_tagged(vlan_naming, tagged_interface, br_name,
|
|
|
|
vid, hapd);
|
|
|
|
|
|
|
|
clean = dyn_iface_put(hapd, br_name);
|
|
|
|
if ((clean & DVLAN_CLEAN_BR) && br_getnumports(br_name) == 0) {
|
|
|
|
ifconfig_down(br_name);
|
|
|
|
br_delbr(br_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void vlan_dellink(const char *ifname, struct hostapd_data *hapd)
|
|
|
|
{
|
|
|
|
struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
|
|
|
|
|
2008-02-28 02:34:43 +01:00
|
|
|
first = prev = vlan;
|
|
|
|
|
|
|
|
while (vlan) {
|
2016-01-21 14:51:58 +01:00
|
|
|
if (os_strcmp(ifname, vlan->ifname) != 0) {
|
|
|
|
prev = vlan;
|
|
|
|
vlan = vlan->next;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!vlan)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (vlan->configured) {
|
2016-01-21 14:52:00 +01:00
|
|
|
int notempty = vlan->vlan_desc.notempty;
|
2016-01-21 14:51:58 +01:00
|
|
|
int untagged = vlan->vlan_desc.untagged;
|
2016-01-21 14:51:59 +01:00
|
|
|
int *tagged = vlan->vlan_desc.tagged;
|
2016-01-21 14:51:58 +01:00
|
|
|
char br_name[IFNAMSIZ];
|
2016-01-21 14:51:59 +01:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_NUM_TAGGED_VLAN && tagged[i]; i++) {
|
|
|
|
if (tagged[i] == untagged ||
|
|
|
|
tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
|
|
|
|
(i > 0 && tagged[i] == tagged[i - 1]))
|
|
|
|
continue;
|
|
|
|
vlan_bridge_name(br_name, hapd, tagged[i]);
|
|
|
|
vlan_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
|
|
|
|
ifname, br_name, tagged[i], hapd);
|
|
|
|
vlan_put_bridge(br_name, hapd, tagged[i]);
|
|
|
|
}
|
2016-01-21 14:51:58 +01:00
|
|
|
|
2016-01-21 14:52:00 +01:00
|
|
|
if (!notempty) {
|
|
|
|
/* Non-VLAN STA */
|
|
|
|
if (hapd->conf->bridge[0] &&
|
|
|
|
(vlan->clean & DVLAN_CLEAN_WLAN_PORT))
|
|
|
|
br_delif(hapd->conf->bridge, ifname);
|
|
|
|
} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
|
2016-01-21 14:51:58 +01:00
|
|
|
vlan_bridge_name(br_name, hapd, untagged);
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2014-03-08 09:54:26 +01:00
|
|
|
if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
|
2010-04-16 00:13:46 +02:00
|
|
|
br_delif(br_name, vlan->ifname);
|
|
|
|
|
2016-01-21 14:51:58 +01:00
|
|
|
vlan_put_bridge(br_name, hapd, untagged);
|
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
|
|
|
}
|
2016-01-21 14:51:58 +01:00
|
|
|
}
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2016-01-21 14:51:59 +01:00
|
|
|
/*
|
|
|
|
* Ensure this VLAN interface is actually removed even if
|
|
|
|
* NEWLINK message is only received later.
|
|
|
|
*/
|
|
|
|
if (if_nametoindex(vlan->ifname) && vlan_if_remove(hapd, vlan))
|
|
|
|
wpa_printf(MSG_ERROR,
|
|
|
|
"VLAN: Could not remove VLAN iface: %s: %s",
|
|
|
|
vlan->ifname, strerror(errno));
|
|
|
|
|
2016-01-21 14:51:58 +01:00
|
|
|
if (vlan == first)
|
|
|
|
hapd->conf->vlan = vlan->next;
|
|
|
|
else
|
|
|
|
prev->next = vlan->next;
|
2008-02-28 02:34:43 +01:00
|
|
|
|
2016-01-21 14:51:58 +01:00
|
|
|
os_free(vlan);
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
|
|
|
|
struct hostapd_data *hapd)
|
|
|
|
{
|
|
|
|
struct ifinfomsg *ifi;
|
|
|
|
int attrlen, nlmsg_len, rta_len;
|
|
|
|
struct rtattr *attr;
|
2015-02-11 09:29:52 +01:00
|
|
|
char ifname[IFNAMSIZ + 1];
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
if (len < sizeof(*ifi))
|
|
|
|
return;
|
|
|
|
|
|
|
|
ifi = NLMSG_DATA(h);
|
|
|
|
|
|
|
|
nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
|
|
|
|
|
|
|
|
attrlen = h->nlmsg_len - nlmsg_len;
|
|
|
|
if (attrlen < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
|
|
|
|
|
2015-02-11 09:29:52 +01:00
|
|
|
os_memset(ifname, 0, sizeof(ifname));
|
2008-02-28 02:34:43 +01:00
|
|
|
rta_len = RTA_ALIGN(sizeof(struct rtattr));
|
|
|
|
while (RTA_OK(attr, attrlen)) {
|
|
|
|
if (attr->rta_type == IFLA_IFNAME) {
|
|
|
|
int n = attr->rta_len - rta_len;
|
|
|
|
if (n < 0)
|
|
|
|
break;
|
|
|
|
|
2015-02-11 09:29:52 +01:00
|
|
|
if ((size_t) n >= sizeof(ifname))
|
|
|
|
n = sizeof(ifname) - 1;
|
2008-02-28 02:34:43 +01:00
|
|
|
os_memcpy(ifname, ((char *) attr) + rta_len, n);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
attr = RTA_NEXT(attr, attrlen);
|
|
|
|
}
|
2015-02-11 09:29:52 +01:00
|
|
|
|
|
|
|
if (!ifname[0])
|
|
|
|
return;
|
2015-04-10 14:49:48 +02:00
|
|
|
if (del && if_nametoindex(ifname)) {
|
|
|
|
/* interface still exists, race condition ->
|
|
|
|
* iface has just been recreated */
|
|
|
|
return;
|
|
|
|
}
|
2015-02-11 09:29:52 +01:00
|
|
|
|
|
|
|
wpa_printf(MSG_DEBUG,
|
|
|
|
"VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
|
|
|
|
del ? "DEL" : "NEW",
|
|
|
|
ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags,
|
|
|
|
(ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
|
|
|
|
(ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
|
|
|
|
(ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
|
|
|
|
(ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
|
|
|
|
|
|
|
|
if (del)
|
|
|
|
vlan_dellink(ifname, hapd);
|
|
|
|
else
|
|
|
|
vlan_newlink(ifname, hapd);
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
|
|
|
|
{
|
|
|
|
char buf[8192];
|
|
|
|
int left;
|
|
|
|
struct sockaddr_nl from;
|
|
|
|
socklen_t fromlen;
|
|
|
|
struct nlmsghdr *h;
|
|
|
|
struct hostapd_data *hapd = eloop_ctx;
|
|
|
|
|
|
|
|
fromlen = sizeof(from);
|
|
|
|
left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
|
|
|
|
(struct sockaddr *) &from, &fromlen);
|
|
|
|
if (left < 0) {
|
|
|
|
if (errno != EINTR && errno != EAGAIN)
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s",
|
|
|
|
__func__, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
h = (struct nlmsghdr *) buf;
|
2015-02-11 09:29:52 +01:00
|
|
|
while (NLMSG_OK(h, left)) {
|
2008-02-28 02:34:43 +01:00
|
|
|
int len, plen;
|
|
|
|
|
|
|
|
len = h->nlmsg_len;
|
|
|
|
plen = len - sizeof(*h);
|
|
|
|
if (len > left || plen < 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink "
|
|
|
|
"message: len=%d left=%d plen=%d",
|
|
|
|
len, left, plen);
|
2008-02-28 02:34:43 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (h->nlmsg_type) {
|
|
|
|
case RTM_NEWLINK:
|
|
|
|
vlan_read_ifnames(h, plen, 0, hapd);
|
|
|
|
break;
|
|
|
|
case RTM_DELLINK:
|
|
|
|
vlan_read_ifnames(h, plen, 1, hapd);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-02-11 09:29:52 +01:00
|
|
|
h = NLMSG_NEXT(h, left);
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (left > 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of "
|
|
|
|
"netlink message", __func__, left);
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct full_dynamic_vlan *
|
|
|
|
full_dynamic_vlan_init(struct hostapd_data *hapd)
|
|
|
|
{
|
|
|
|
struct sockaddr_nl local;
|
|
|
|
struct full_dynamic_vlan *priv;
|
|
|
|
|
|
|
|
priv = os_zalloc(sizeof(*priv));
|
|
|
|
if (priv == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2012-08-10 11:55:33 +02:00
|
|
|
#ifndef CONFIG_VLAN_NETLINK
|
2012-08-10 11:38:17 +02:00
|
|
|
vlan_set_name_type(hapd->conf->ssid.vlan_naming ==
|
|
|
|
DYNAMIC_VLAN_NAMING_WITH_DEVICE ?
|
|
|
|
VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD :
|
|
|
|
VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
|
2012-08-10 11:55:33 +02:00
|
|
|
#endif /* CONFIG_VLAN_NETLINK */
|
2008-02-28 02:34:43 +01:00
|
|
|
|
|
|
|
priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
|
|
|
if (priv->s < 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW,"
|
|
|
|
"NETLINK_ROUTE) failed: %s",
|
|
|
|
__func__, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
os_free(priv);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
os_memset(&local, 0, sizeof(local));
|
|
|
|
local.nl_family = AF_NETLINK;
|
|
|
|
local.nl_groups = RTMGRP_LINK;
|
|
|
|
if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) {
|
2010-04-15 19:35:51 +02:00
|
|
|
wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s",
|
|
|
|
__func__, strerror(errno));
|
2008-02-28 02:34:43 +01:00
|
|
|
close(priv->s);
|
|
|
|
os_free(priv);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL))
|
|
|
|
{
|
|
|
|
close(priv->s);
|
|
|
|
os_free(priv);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return priv;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv)
|
|
|
|
{
|
|
|
|
if (priv == NULL)
|
|
|
|
return;
|
|
|
|
eloop_unregister_read_sock(priv->s);
|
|
|
|
close(priv->s);
|
|
|
|
os_free(priv);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_FULL_DYNAMIC_VLAN */
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#",
|
|
|
|
hapd->conf->iface);
|
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
|
|
|
{
|
2014-03-17 14:16:24 +01:00
|
|
|
struct hostapd_vlan *n = NULL;
|
2008-02-28 02:34:43 +01:00
|
|
|
char *ifname, *pos;
|
|
|
|
|
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);
|
2008-02-28 02:34:43 +01:00
|
|
|
ifname = os_strdup(vlan->ifname);
|
|
|
|
if (ifname == NULL)
|
|
|
|
return NULL;
|
|
|
|
pos = os_strchr(ifname, '#');
|
2014-03-17 14:16:24 +01:00
|
|
|
if (pos == NULL)
|
|
|
|
goto free_ifname;
|
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)
|
|
|
|
goto free_ifname;
|
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;
|
|
|
|
|
|
|
|
os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
|
|
|
|
pos);
|
|
|
|
|
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;
|
|
|
|
goto free_ifname;
|
2008-02-28 02:34:43 +01:00
|
|
|
}
|
|
|
|
|
2014-03-17 14:16:24 +01:00
|
|
|
free_ifname:
|
|
|
|
os_free(ifname);
|
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;
|
|
|
|
}
|