[PATCH v5 19/22] Fix STA dynamic vlan tracking

Michael Braun michael-dev
Tue Nov 19 11:48:15 PST 2013


When an STA is authenticated, it gets moved to appropiate (dynamic)
vlan using ap_sta_bind_vlan. Though, it is not removed when the
station disconnects - even if the struct sta_info is freed.

This results in dynamic interfaces not being removed.

Solution: The struct sta_info tracks the dynamic vlans the station
          has been assigned. This information can be used to release
          the VLAN the STA has been added to it before.

Signed-hostap: Michael Braun <michael-dev at fami-braun.de>
---
 src/ap/ieee802_1x.c |    2 +-
 src/ap/sta_info.c   |   33 ++++++++++++++++++++++++++++++---
 src/ap/sta_info.h   |    4 ++++
 3 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index f619898..1e5168e 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -1360,7 +1360,6 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
 
 		if (ap_sta_bind_vlan(hapd, sta, old_vlanid) < 0)
 			break;
-		vlan_free(&old_vlanid);
 
 		/* RFC 3580, Ch. 3.17 */
 		if (session_timeout_set && termination_action ==
@@ -1415,6 +1414,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
 		sm->eap_if->aaaEapReq = FALSE;
 
 	eapol_auth_step(sm);
+	vlan_free(&old_vlanid);
 
 	return RADIUS_RX_QUEUED;
 }
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 0d93abd..2d7cb83 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -143,6 +143,22 @@ static void ap_sta_hash_del(struct hostapd_data *hapd, struct sta_info *sta)
 }
 
 
+/**
+ * This method marks the STA as not being assigned to any VLAN currently.
+ * It may only be called if the STA is not assigned to its VLAN interface
+ * in kernel!
+ * It is used before freeing the sta data structure to release
+ * tehe vlan_id_bound VLAN pointer.
+ */
+void ap_unbind_sta_vlan(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	if (vlan_notempty(&sta->vlan_id_bound) &&
+	    vlan_untagged(&sta->vlan_id_bound) >= 0)
+		vlan_remove_dynamic(hapd, sta->vlan_id_bound);
+	vlan_free(&sta->vlan_id_bound);
+}
+
+
 void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 {
 	int set_beacon = 0;
@@ -272,6 +288,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 #endif /* CONFIG_SAE */
 
 	vlan_free(&sta->vlan_id);
+	ap_unbind_sta_vlan(hapd, sta);
 
 	os_free(sta);
 }
@@ -699,12 +716,18 @@ int ap_sta_wps_cancel(struct hostapd_data *hapd,
 #endif /* CONFIG_WPS */
 
 
+/**
+ * Moves the STA to its VLAN.
+ * @param old_vlanid Used to check if anything has changed. Might differ from
+ *                   sta->bound_vlanid as tracking in hostapd is not exact.
+ */
 int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
 		     struct vlan_description old_vlanid)
 {
 #ifndef CONFIG_NO_VLAN
 	const char *iface;
 	struct hostapd_vlan *vlan = NULL;
+	int boundVLANchanged;
 	int ret;
 
 	/*
@@ -718,8 +741,9 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
 	 * During 1x reauth, if the vlan id changes, then remove the old id and
 	 * proceed furthur to add the new one.
 	 */
-	if (vlan_notempty(&old_vlanid) && vlan_untagged(&old_vlanid) >= 0)
-		vlan_remove_dynamic(hapd, old_vlanid);
+	boundVLANchanged = !vlan_cmp(&sta->vlan_id, &sta->vlan_id_bound);
+	if (boundVLANchanged)
+		ap_unbind_sta_vlan(hapd, sta);
 
 	iface = hapd->conf->iface;
 	if (sta->ssid->vlan[0])
@@ -779,7 +803,8 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
 			       "interface '%s'", iface);
 	} else if (vlan && vlan_cmp(&vlan->vlan_id, &sta->vlan_id)) {
 		if (vlan_notempty(&sta->vlan_id) &&
-		    vlan_untagged(&sta->vlan_id) >= 0) {
+		    vlan_untagged(&sta->vlan_id) >= 0 &&
+		    boundVLANchanged) {
 			vlan->dynamic_vlan++;
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_IEEE80211,
@@ -807,6 +832,8 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
 		       HOSTAPD_LEVEL_DEBUG, "binding station to interface "
 		       "'%s'", iface);
 
+	if (boundVLANchanged)
+		vlan_alloc_copy(&sta->vlan_id_bound, &sta->vlan_id);
 	if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0)
 		wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA");
 
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 0bc50c4..56b49a9 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -98,7 +98,10 @@ struct sta_info {
 	struct hostapd_ssid *ssid; /* SSID selection based on (Re)AssocReq */
 	struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */
 
+	/* VLAN-ID given by auth or acl */
 	struct vlan_description vlan_id;
+	/* VLAN ID currently in use (as counted with vlan->dynamic_vlan) */
+	struct vlan_description vlan_id_bound;
 	 /* PSKs from RADIUS authentication server */
 	struct hostapd_sta_wpa_psk_short *psk;
 
@@ -181,6 +184,7 @@ int ap_sta_wps_cancel(struct hostapd_data *hapd,
 #endif /* CONFIG_WPS */
 int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
 		     struct vlan_description old_vlanid);
+void ap_unbind_sta_vlan(struct hostapd_data *hapd, struct sta_info *sta);
 void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
 void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
 int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);




More information about the Hostap mailing list