@@ -2575,6 +2575,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
#ifndef CONFIG_NO_VLAN
} else if (os_strcmp(buf, "dynamic_vlan") == 0) {
bss->ssid.dynamic_vlan = atoi(pos);
+ } else if (os_strcmp(buf, "dynamic_vlan_release_timeout") == 0) {
+ bss->ssid.dynamic_vlan_release_timeout = atoi(pos);
} else if (os_strcmp(buf, "vlan_file") == 0) {
if (hostapd_config_read_vlan_file(bss, pos)) {
wpa_printf(MSG_ERROR, "Line %d: failed to "
@@ -852,6 +852,13 @@ own_ip_addr=127.0.0.1
# 1 = option; use default interface if RADIUS server does not include VLAN ID
# 2 = required; reject authentication if RADIUS server does not include VLAN ID
#dynamic_vlan=0
+#
+# Removing dynamic interfaces seems to trigger kernel bugs, so it is disabled.
+# This gives a timeout for removing the interface after it has been disabled.
+# -1 = disabled
+# 0 = remove immediately
+# >0 deferred removal
+#dynamic_vlan_release_timeout=30
# VLAN interface list for dynamic VLAN mode is read from a separate text file.
# This list is used to map VLAN ID from the RADIUS server to a network
@@ -91,6 +91,8 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
bss->radius_das_time_window = 300;
bss->sae_anti_clogging_threshold = 5;
+
+ bss->ssid.dynamic_vlan_release_timeout = 30;
}
@@ -67,6 +67,7 @@ struct hostapd_ssid {
#define DYNAMIC_VLAN_OPTIONAL 1
#define DYNAMIC_VLAN_REQUIRED 2
int dynamic_vlan;
+ int dynamic_vlan_release_timeout;
#define DYNAMIC_VLAN_NAMING_WITHOUT_DEVICE 0
#define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
#define DYNAMIC_VLAN_NAMING_END 2
@@ -40,6 +40,10 @@ struct full_dynamic_vlan {
int s; /* socket on which to listen for new/removed interfaces. */
};
+struct vlan_removal_info {
+ int ifidx;
+ char ifname[IFNAMSIZ+1];
+};
static int ifconfig_helper(const char *if_name, int up)
{
@@ -1109,6 +1113,9 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
{
struct hostapd_vlan *n, *i, *prev;
char *ifname, *pos;
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ int ifidx, created = 0;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
if (vlan == NULL
|| vlan_untagged(&vlan_id) <= 0
@@ -1168,14 +1175,21 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
artifical_id, pos);
os_free(ifname);
- if (hostapd_vlan_if_add(hapd, n->ifname)) {
- os_free(n);
- return NULL;
- }
#ifdef CONFIG_FULL_DYNAMIC_VLAN
- // if id_add returned the ifidx, it could be reused here
+ ifidx = if_nametoindex(n->ifname);
+ if (!ifidx) {
+ /* interface does not yet exist */
+ if (hostapd_vlan_if_add(hapd, n->ifname)) {
+ /* interface could not be created */
+ os_free(n);
+ return NULL;
+ }
+ created = 1;
+ }
+ hapd_get_dynamic_iface(NULL, n->ifname, created, hapd);
+ // if if_add returned the ifidx, it could be reused here
n->ifidx = if_nametoindex(n->ifname);
-#endif
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
/* insert into list */
if (!prev) {
@@ -1194,12 +1208,50 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
}
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+/**
+ * ap_handle_timer - Per STA timer handler
+ * @eloop_ctx: struct hostapd_data *
+ * @timeout_ctx: struct sta_info *
+ *
+ * This function is called to check station activity and to remove inactive
+ * stations.
+ */
+void vlan_remove_timer(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct vlan_removal_info *ctx = timeout_ctx;
+
+ wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s, ifidx=%d)", __func__, ctx->ifname, ctx->ifidx);
+
+ if (hapd_put_dynamic_iface(NULL, ctx->ifname, hapd) != 1)
+ goto out;
+
+ /* if somebody else removed that interface and thus a new one
+ * has been created, we don't need to remove the new one.
+ */
+ if (ctx->ifidx != 0 && if_nametoindex(ctx->ifname) != ctx->ifidx)
+ goto out;
+
+ hostapd_vlan_if_remove(hapd, ctx->ifname);
+
+out:
+ os_free(ctx);
+}
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+
int vlan_remove_dynamic(struct hostapd_data *hapd, vlan_t vlan_id)
{
struct hostapd_vlan *vlan;
- char buf[IFNAMSIZ+1];
+ char ifname[IFNAMSIZ+1];
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ int ifidx;
+ struct vlan_removal_info *ctx = NULL;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
- if (vlan_untagged(&vlan->vlan_id) == VLAN_ID_WILDCARD)
+ if (vlan_untagged(&vlan->vlan_id) == VLAN_ID_WILDCARD
+ || !vlan_notempty(&vlan_id))
return 1;
wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%s)", __func__,
@@ -1218,14 +1270,37 @@ int vlan_remove_dynamic(struct hostapd_data *hapd, vlan_t vlan_id)
if (vlan == NULL)
return 1;
- if (vlan->dynamic_vlan == 0) {
- os_strncpy(buf, vlan->ifname, sizeof(buf));
-#ifdef CONFIG_FULL_DYNAMIC_VLAN
- vlan_dellink(buf, vlan->ifidx, hapd);
-#endif
- vlan = NULL; /* freed by vlan_dellink */
- hostapd_vlan_if_remove(hapd, buf);
+ if (vlan->dynamic_vlan != 0)
+ return 0;
+
+ if (hapd->conf->ssid.dynamic_vlan_release_timeout < 0)
+ return 0;
+
+ os_strncpy(ifname, vlan->ifname, sizeof(ifname));
+#ifndef CONFIG_FULL_DYNAMIC_VLAN
+ hostapd_vlan_if_remove(hapd, ifname);
+#else
+ ifidx = vlan->ifidx;
+
+ vlan_dellink(ifname, ifidx, hapd);
+ vlan = NULL; /* freed by vlan_dellink */
+
+ if (hapd->conf->ssid.dynamic_vlan_release_timeout > 0)
+ ctx = os_zalloc(sizeof(*ctx));
+
+ if (hapd->conf->ssid.dynamic_vlan_release_timeout == 0 || !ctx) {
+ /* timeout = 0 */
+ if (hapd_put_dynamic_iface(NULL, ifname, hapd) == 1)
+ hostapd_vlan_if_remove(hapd, ifname);
+ return 0;
}
+ ctx->ifidx = ifidx;
+ os_strncpy(ctx->ifname, ifname, sizeof(ctx->ifname));
+
+ eloop_register_timeout(hapd->conf->ssid.dynamic_vlan_release_timeout, 0,
+ vlan_remove_timer, hapd, ctx);
+
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
return 0;
}