Patchwork [PATCHv2,20/21] VLAN: do not remove VLAN interfaces due to kernel bug

login
register
mail settings
Submitter michael-dev@fami-braun.de
Date June 1, 2013, 3:54 p.m.
Message ID <c926836da08bf9b0cda25393eb69fa8c9d75d0b6.1370512966.git.michael-dev@fami-braun.de>
Download mbox | patch
Permalink /patch/249377/
State Superseded
Headers show

Comments

michael-dev@fami-braun.de - June 1, 2013, 3:54 p.m.
Since running this hostapd version, I continously
see kernel warnings. I suspect them to be due to
interfaces removal now occuring on STA freeing,
which is happen frequently on my test node.

This patch makes interface freeing optional
and deferred by default.

Signed-hostap: Michael Braun <michael-dev@fami-braun.de>

WARNING: at /opt/openwrt/stage2/trunk/build_dir/target-powerpc_uClibc-0.9.33.2/linux-mpc85xx_p1020wlan-fem/compat-wireless-2013-04-16/net/mac80211/wpa.c:68
[  876.001783] [eef81e10] [f57eef2c] rate_control_get_rate+0xb4/0x100 [mac80211] (unreliable)
[  876.010055] [eef81e30] [f57fbd24] ieee80211_proberesp_get+0xbcc/0x1320 [mac80211]
[  876.017540] [eef81eb0] [f57fc598] ieee80211_get_buffered_bc+0x120/0x140 [mac80211]
[  876.025111] [eef81f10] [f5bf8a1c] ath9k_beacon_tasklet+0x434/0x684 [ath9k]
[  876.031988] [eef81f70] [c002c514] tasklet_action+0xe0/0x174
[  876.037558] [eef81fa0] [c002ca6c] __do_softirq+0xe0/0x18c
[  876.042952] [eef81ff0] [c000bef8] call_do_softirq+0x14/0x24
[  876.048522] [c03d5e70] [c000459c] do_softirq+0x80/0xc0
[  876.053657] [c03d5e90] [c002cc54] irq_exit+0x60/0x8c
[  876.058616] [c03d5ea0] [c0004374] do_IRQ+0x12c/0x150
[  876.063580] [c03d5ed0] [c000dee8] ret_from_except+0x0/0x18
[  876.069066] --- Exception: 501 at cpu_idle+0x84/0xd0
[  876.069066]     LR = cpu_idle+0x84/0xd0
[  876.077846] [c03d5f90] [c00078ec] cpu_idle+0x44/0xd0 (unreliable)
[  876.083938] [c03d5fb0] [c0002724] rest_init+0x68/0x78
[  876.088989] [c03d5fc0] [c037a7bc] start_kernel+0x2e0/0x2f4
[  876.094469] [c03d5ff0] [c00003f8] skpinv+0x2e4/0x320

[ 2523.961923] ------------[ cut here ]------------
[ 2523.966547] WARNING: at /opt/openwrt/stage2/trunk/build_dir/target-powerpc_uClibc-0.9.33.2/linux-mpc85xx_p1020wlan-fem/compat-wireless-2013-04-16/net/mac80211/chan.c:218
[ 2524.209752] [ef8a9990] [f5803838] ieee80211_iter_chan_contexts_atomic+0x378/0x3f4 [mac80211] (unreliable)
[ 2524.219326] [ef8a99c0] [f5803cac] ieee80211_recalc_smps_chanctx+0x304/0x358 [mac80211]
[ 2524.227247] [ef8a99e0] [f5804218] ieee80211_vif_release_channel+0x48/0x68 [mac80211]
[ 2524.234993] [ef8a99f0] [f57f2fd0] ieee80211_aes_cmac_key_free+0x2c04/0x4648 [mac80211]
[ 2524.242922] [ef8a9a10] [f56fb96c] cfg80211_stop_ap+0x74/0x3e8 [cfg80211]
[ 2524.249623] [ef8a9a30] [f56de740] cfg80211_leave+0xdc/0x9a0 [cfg80211]
[ 2524.256149] [ef8a9a50] [f56de984] cfg80211_leave+0x320/0x9a0 [cfg80211]
[ 2524.262767] [ef8a9ab0] [c004a054] notifier_call_chain+0x4c/0xa4
[ 2524.268685] [ef8a9ad0] [c0243148] call_netdevice_notifiers+0x5c/0x70
[ 2524.275035] [ef8a9ae0] [c02431b0] __dev_close_many+0x54/0xf4
[ 2524.280690] [ef8a9b00] [c0243320] dev_close_many+0x84/0xfc
[ 2524.286172] [ef8a9b30] [c0243470] rollback_registered_many+0xd8/0x2d0
[ 2524.292607] [ef8a9b60] [c0243710] rollback_registered+0x2c/0x4c
[ 2524.298524] [ef8a9b80] [c024504c] unregister_netdevice_queue+0x80/0xb8
[ 2524.305056] [ef8a9b90] [f57edcf8] ieee80211_if_remove+0x8c/0xb4 [mac80211]
[ 2524.311934] [ef8a9ba0] [f57f37fc] ieee80211_aes_cmac_key_free+0x3430/0x4648 [mac80211]
[ 2524.319853] [ef8a9bb0] [f56e6968] cfg80211_wext_giwscan+0x1154/0x11c0 [cfg80211]
[ 2524.327252] [ef8a9bc0] [c0269838] genl_rcv_msg+0x1ec/0x228
[ 2524.332734] [ef8a9c30] [c0268c40] netlink_rcv_skb+0x64/0xd4
[ 2524.338303] [ef8a9c50] [c0269638] genl_rcv+0x28/0x3c
[ 2524.343265] [ef8a9c60] [c02685f0] netlink_unicast+0x168/0x224
[ 2524.349007] [ef8a9ca0] [c0268a28] netlink_sendmsg+0x2b0/0x32c
[ 2524.354758] [ef8a9cf0] [c022fb1c] sock_sendmsg+0x80/0xa4
[ 2524.360066] [ef8a9dd0] [c022fe60] __sys_sendmsg+0x1f8/0x2b4
[ 2524.365635] [ef8a9ef0] [c0231c68] sys_sendmsg+0x40/0x74
[ 2524.370859] [ef8a9f40] [c000d824] ret_from_syscall+0x0/0x3c

Patch

diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 90d1259..45c5916 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -2564,6 +2564,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 "
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index ff444a0..28af598 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -830,6 +830,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
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index aac8a50..68a791a 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -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;
 }
 
 
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index a648aab..666481d 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -73,6 +73,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
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index 5e0b536..1842aef 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -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)
 {
@@ -1103,6 +1107,7 @@  struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
 {
 	struct hostapd_vlan *n, *i, *prev;
 	char *ifname, *pos;
+	int ifidx, created = 0;
 
 	if (vlan == NULL
 	    || vlan_untagged(&vlan_id) <= 0
@@ -1162,11 +1167,18 @@  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;
+	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;
 	}
-	// if id_add returned the ifidx, it could be reused here
+	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);
 
 	/* insert into list */
@@ -1186,12 +1198,46 @@  struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
 }
 
 
+/**
+ * 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);
+}
+
+
 int vlan_remove_dynamic(struct hostapd_data *hapd, vlan_t vlan_id)
 {
 	struct hostapd_vlan *vlan;
-	char buf[IFNAMSIZ+1];
+	char ifname[IFNAMSIZ+1];
+	int ifidx;
+	struct vlan_removal_info *ctx = NULL;
 
-	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__,
@@ -1210,12 +1256,33 @@  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));
-		vlan_dellink(buf, vlan->ifidx, hapd);
-		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));
+	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);
+
 	return 0;
 }