[PATCHv2 01/12] Separate station grouping and uplink configuration

Michael Braun michael-dev at fami-braun.de
Sun Dec 6 12:47:32 PST 2015


Superate uplink configuration (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 an struct vlan_description
vlan_desc member that describes the uplink configuration requested. Currently
this is just an int untagged 802.1q VID, but can be extended to tagged vlans
and other settings easily.

When the station was about 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.

In order to easy adding of tagged vlan support, a member notempty is added
to struct vlan_description. Is is set to 1 if a untagged or tagged vlan
assignment is requested and needs to be validated.
The inverted form allows os_zalloc to initialize an empty description.

Thought 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 does 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 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 802.1q VID.

Signed-off-by: Michael Braun <michael-dev at fami-braun.de>
---
 hostapd/config_file.c     |   6 ++-
 hostapd/ctrl_iface.c      |  10 +++--
 src/ap/ap_config.c        |  13 ++++--
 src/ap/ap_config.h        |   9 ++--
 src/ap/ieee802_11.c       |  15 ++++---
 src/ap/ieee802_11_auth.c  |  26 ++++++-----
 src/ap/ieee802_11_auth.h  |   6 ++-
 src/ap/ieee802_1x.c       |  51 ++++++++++++++--------
 src/ap/pmksa_cache_auth.c |   7 ++-
 src/ap/pmksa_cache_auth.h |   2 +-
 src/ap/sta_info.c         | 109 ++++++++++++++++++++++++++++++++++------------
 src/ap/sta_info.h         |   8 +++-
 src/ap/vlan.h             |  17 ++++++++
 src/ap/vlan_init.c        |  37 +++++++++-------
 src/ap/vlan_init.h        |  11 +++--
 15 files changed, 227 insertions(+), 100 deletions(-)
 create mode 100644 src/ap/vlan.h

diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index f2163b8..2887a16 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -97,6 +97,8 @@ static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss,
 		}
 
 		vlan->vlan_id = vlan_id;
+		vlan->vlan_desc.untagged = vlan_id;
+		vlan->vlan_desc.notempty = !!vlan_id;
 		os_strlcpy(vlan->ifname, pos, sizeof(vlan->ifname));
 		vlan->next = bss->vlan;
 		bss->vlan = vlan;
@@ -197,7 +199,9 @@ static int hostapd_config_read_maclist(const char *fname,
 
 		*acl = newacl;
 		os_memcpy((*acl)[*num].addr, addr, ETH_ALEN);
-		(*acl)[*num].vlan_id = vlan_id;
+		memset(&(*acl)[*num].vlan_id, 0, sizeof((*acl)[*num].vlan_id));
+		(*acl)[*num].vlan_id.untagged = vlan_id;
+		(*acl)[*num].vlan_id.notempty = !!vlan_id;
 		(*num)++;
 	}
 
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index cb6fb17..2f21f93 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -1322,7 +1322,7 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
 #endif /* CONFIG_TESTING_OPTIONS */
 	} else {
 		struct sta_info *sta;
-		int vlan_id;
+		struct vlan_description vlan_id;
 
 		ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value);
 		if (ret)
@@ -1334,7 +1334,9 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
 					    hapd->conf->deny_mac,
 					    hapd->conf->num_deny_mac, sta->addr,
 					    &vlan_id) &&
-				    (!vlan_id || vlan_id == sta->vlan_id))
+				    (!vlan_id.notempty ||
+				     !os_memcmp(&vlan_id, &sta->vlan_desc,
+						sizeof(vlan_id))))
 					ap_sta_disconnect(
 						hapd, sta, sta->addr,
 						WLAN_REASON_UNSPECIFIED);
@@ -1346,7 +1348,9 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
 					    hapd->conf->accept_mac,
 					    hapd->conf->num_accept_mac,
 					    sta->addr, &vlan_id) ||
-				    (vlan_id && vlan_id != sta->vlan_id))
+				    (vlan_id.notempty &&
+				     os_memcmp(&vlan_id, &sta->vlan_desc,
+						sizeof(vlan_id))))
 					ap_sta_disconnect(
 						hapd, sta, sta->addr,
 						WLAN_REASON_UNSPECIFIED);
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index cf9b2ce..6df9d47 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -628,7 +628,7 @@ void hostapd_config_free(struct hostapd_config *conf)
  * Perform a binary search for given MAC address from a pre-sorted list.
  */
 int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
-			  const u8 *addr, int *vlan_id)
+			  const u8 *addr, struct vlan_description *vlan_id)
 {
 	int start, end, middle, res;
 
@@ -668,11 +668,18 @@ int hostapd_rate_found(int *list, int rate)
 }
 
 
-int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id)
+int hostapd_vlan_id_valid(struct hostapd_vlan *vlan,
+			  struct vlan_description vlan_id)
 {
 	struct hostapd_vlan *v = vlan;
+
+	if (!vlan_id.notempty || vlan_id.untagged <= 0 ||
+	    vlan_id.untagged > MAX_VLAN_ID)
+		return 0;
+
 	while (v) {
-		if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD)
+		if (!os_memcmp(&v->vlan_desc, &vlan_id, sizeof(vlan_id))
+		    || v->vlan_id == VLAN_ID_WILDCARD)
 			return 1;
 		v = v->next;
 	}
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index ff9dcb0..ec6a026 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -17,6 +17,7 @@
 #include "common/ieee802_11_common.h"
 #include "wps/wps.h"
 #include "fst/fst.h"
+#include "vlan.h"
 
 /**
  * mesh_conf - local MBSS state and settings
@@ -53,7 +54,7 @@ typedef u8 macaddr[ETH_ALEN];
 
 struct mac_acl_entry {
 	macaddr addr;
-	int vlan_id;
+	struct vlan_description vlan_id;
 };
 
 struct hostapd_radius_servers;
@@ -114,6 +115,7 @@ struct hostapd_ssid {
 struct hostapd_vlan {
 	struct hostapd_vlan *next;
 	int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */
+	struct vlan_description vlan_desc;
 	char ifname[IFNAMSIZ + 1];
 	int configured;
 	int dynamic_vlan;
@@ -687,13 +689,14 @@ void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p);
 void hostapd_config_free_bss(struct hostapd_bss_config *conf);
 void hostapd_config_free(struct hostapd_config *conf);
 int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
-			  const u8 *addr, int *vlan_id);
+			  const u8 *addr, struct vlan_description *vlan_id);
 int hostapd_rate_found(int *list, int rate);
 const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
 			   const u8 *addr, const u8 *p2p_dev_addr,
 			   const u8 *prev_psk);
 int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf);
-int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id);
+int hostapd_vlan_id_valid(struct hostapd_vlan *vlan,
+			  struct vlan_description vlan_id);
 const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan,
 					int vlan_id);
 struct hostapd_radius_attr *
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 01b514e..11db12a 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -886,7 +886,7 @@ static void handle_auth(struct hostapd_data *hapd,
 	u16 fc;
 	const u8 *challenge = NULL;
 	u32 session_timeout, acct_interim_interval;
-	int vlan_id = 0;
+	struct vlan_description vlan_id;
 	struct hostapd_sta_wpa_psk_short *psk = NULL;
 	u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN];
 	size_t resp_ies_len = 0;
@@ -894,6 +894,8 @@ static void handle_auth(struct hostapd_data *hapd,
 	char *radius_cui = NULL;
 	u16 seq_ctrl;
 
+	os_memset(&vlan_id, 0, sizeof(vlan_id));
+
 	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
 		wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
 			   (unsigned long) len);
@@ -1095,16 +1097,19 @@ static void handle_auth(struct hostapd_data *hapd,
 	sta->last_seq_ctrl = seq_ctrl;
 	sta->last_subtype = WLAN_FC_STYPE_AUTH;
 
-	if (vlan_id > 0) {
+	if (vlan_id.notempty) {
 		if (!hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) {
 			hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
-				       HOSTAPD_LEVEL_INFO, "Invalid VLAN ID "
+				       HOSTAPD_LEVEL_INFO, "Invalid VLAN "
 				       "%d received from RADIUS server",
-				       vlan_id);
+				       vlan_id.untagged);
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		if (ap_sta_set_vlan(hapd, sta, vlan_id) < 0) {
 			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 			goto fail;
 		}
-		sta->vlan_id = vlan_id;
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
 			       HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
 	}
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index b7e7ce3..fcd0451 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -35,7 +35,7 @@ struct hostapd_cached_radius_acl {
 	struct hostapd_cached_radius_acl *next;
 	u32 session_timeout;
 	u32 acct_interim_interval;
-	int vlan_id;
+	struct vlan_description vlan_id;
 	struct hostapd_sta_wpa_psk_short *psk;
 	char *identity;
 	char *radius_cui;
@@ -99,7 +99,8 @@ static void copy_psk_list(struct hostapd_sta_wpa_psk_short **psk,
 
 static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
 				 u32 *session_timeout,
-				 u32 *acct_interim_interval, int *vlan_id,
+				 u32 *acct_interim_interval,
+				 struct vlan_description *vlan_id,
 				 struct hostapd_sta_wpa_psk_short **psk,
 				 char **identity, char **radius_cui)
 {
@@ -219,7 +220,8 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
  * @vlan_id: Buffer for returning VLAN ID
  * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
  */
- int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr, int *vlan_id)
+int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr,
+		      struct vlan_description *vlan_id)
 {
 	if (hostapd_maclist_found(hapd->conf->accept_mac,
 				  hapd->conf->num_accept_mac, addr, vlan_id))
@@ -257,7 +259,8 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
  */
 int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
 			    const u8 *msg, size_t len, u32 *session_timeout,
-			    u32 *acct_interim_interval, int *vlan_id,
+			    u32 *acct_interim_interval,
+			    struct vlan_description *vlan_id,
 			    struct hostapd_sta_wpa_psk_short **psk,
 			    char **identity, char **radius_cui)
 {
@@ -268,7 +271,7 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
 	if (acct_interim_interval)
 		*acct_interim_interval = 0;
 	if (vlan_id)
-		*vlan_id = 0;
+		os_memset(vlan_id, 0, sizeof(*vlan_id));
 	if (psk)
 		*psk = NULL;
 	if (identity)
@@ -553,7 +556,8 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
 			cache->acct_interim_interval = 0;
 		}
 
-		cache->vlan_id = radius_msg_get_vlanid(msg);
+		cache->vlan_id.untagged = radius_msg_get_vlanid(msg);
+		cache->vlan_id.notempty = !!cache->vlan_id.untagged;
 
 		decode_tunnel_passwords(hapd, shared_secret, shared_secret_len,
 					msg, req, cache);
@@ -576,17 +580,17 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
 		    !cache->psk)
 			cache->accepted = HOSTAPD_ACL_REJECT;
 
-		if (cache->vlan_id &&
+		if (cache->vlan_id.notempty &&
 		    !hostapd_vlan_id_valid(hapd->conf->vlan, cache->vlan_id)) {
 			hostapd_logger(hapd, query->addr,
 				       HOSTAPD_MODULE_RADIUS,
 				       HOSTAPD_LEVEL_INFO,
-				       "Invalid VLAN ID %d received from RADIUS server",
-				       cache->vlan_id);
-			cache->vlan_id = 0;
+				       "Invalid VLAN %d received from RADIUS server",
+				       cache->vlan_id.untagged);
+			os_memset(&cache->vlan_id, 0, sizeof(cache->vlan_id));
 		}
 		if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
-		    !cache->vlan_id)
+		    !cache->vlan_id.notempty)
 			cache->accepted = HOSTAPD_ACL_REJECT;
 	} else
 		cache->accepted = HOSTAPD_ACL_REJECT;
diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h
index da81c14..71f53b9 100644
--- a/src/ap/ieee802_11_auth.h
+++ b/src/ap/ieee802_11_auth.h
@@ -16,10 +16,12 @@ enum {
 	HOSTAPD_ACL_ACCEPT_TIMEOUT = 3
 };
 
-int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr, int *vlan_id);
+int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr,
+		      struct vlan_description *vlan_id);
 int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
 			    const u8 *msg, size_t len, u32 *session_timeout,
-			    u32 *acct_interim_interval, int *vlan_id,
+			    u32 *acct_interim_interval,
+			    struct vlan_description *vlan_id,
 			    struct hostapd_sta_wpa_psk_short **psk,
 			    char **identity, char **radius_cui);
 int hostapd_acl_init(struct hostapd_data *hapd);
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index f566603..fa87ce2 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -219,7 +219,7 @@ static void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
 		   MAC2STR(sta->addr));
 
 #ifndef CONFIG_NO_VLAN
-	if (sta->vlan_id > 0 && sta->vlan_id <= MAX_VLAN_ID) {
+	if (sta->vlan_id > 0) {
 		wpa_printf(MSG_ERROR, "Using WEP with vlans is not supported.");
 		return;
 	}
@@ -1593,10 +1593,13 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
 	struct hostapd_data *hapd = data;
 	struct sta_info *sta;
 	u32 session_timeout = 0, termination_action, acct_interim_interval;
-	int session_timeout_set, vlan_id = 0;
+	int session_timeout_set;
 	struct eapol_state_machine *sm;
 	int override_eapReq = 0;
 	struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+	struct vlan_description vlan_desc;
+
+	os_memset(&vlan_desc, 0, sizeof(vlan_desc));
 
 	sm = ieee802_1x_search_radius_identifier(hapd, hdr->identifier);
 	if (sm == NULL) {
@@ -1660,27 +1663,27 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
 
 	switch (hdr->code) {
 	case RADIUS_CODE_ACCESS_ACCEPT:
-		if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
-			vlan_id = 0;
 #ifndef CONFIG_NO_VLAN
-		else
-			vlan_id = radius_msg_get_vlanid(msg);
-		if (vlan_id > 0 &&
-		    hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) {
-			hostapd_logger(hapd, sta->addr,
-				       HOSTAPD_MODULE_RADIUS,
-				       HOSTAPD_LEVEL_INFO,
-				       "VLAN ID %d", vlan_id);
-		} else if (vlan_id > 0) {
+		if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED) {
+			vlan_desc.untagged = radius_msg_get_vlanid(msg);
+			vlan_desc.notempty = !!vlan_desc.untagged;
+		}
+
+		if (vlan_desc.notempty &&
+		    !hostapd_vlan_id_valid(hapd->conf->vlan, vlan_desc)) {
 			sta->eapol_sm->authFail = TRUE;
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_RADIUS,
 				       HOSTAPD_LEVEL_INFO,
-				       "Invalid VLAN ID %d received from RADIUS server",
-				       vlan_id);
+				       "Invalid VLAN %d received from RADIUS server",
+				       vlan_desc.untagged);
+			os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+			ap_sta_set_vlan(hapd, sta, vlan_desc);
 			break;
-		} else if (hapd->conf->ssid.dynamic_vlan ==
-			   DYNAMIC_VLAN_REQUIRED) {
+		}
+
+		if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
+		    !vlan_desc.notempty) {
 			sta->eapol_sm->authFail = TRUE;
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_IEEE8021X,
@@ -1691,7 +1694,19 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
 		}
 #endif /* CONFIG_NO_VLAN */
 
-		sta->vlan_id = vlan_id;
+		if (ap_sta_set_vlan(hapd, sta, vlan_desc) < 0)
+			break;
+
+#ifndef CONFIG_NO_VLAN
+		if (sta->vlan_id > 0) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_RADIUS,
+				       HOSTAPD_LEVEL_INFO,
+				       "VLAN ID %d", sta->vlan_id);
+		}
+#endif /* CONFIG_NO_VLAN */
+
+
 		if ((sta->flags & WLAN_STA_ASSOC) &&
 		    ap_sta_bind_vlan(hapd, sta) < 0)
 			break;
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index 83e4bda..b8947b5 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -146,7 +146,7 @@ static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
 #endif /* CONFIG_NO_RADIUS */
 
 	entry->eap_type_authsrv = eapol->eap_type_authsrv;
-	entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id;
+	entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_desc;
 
 	entry->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi;
 	entry->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo;
@@ -156,6 +156,8 @@ static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
 void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
 			       struct eapol_state_machine *eapol)
 {
+	struct sta_info *sta;
+
 	if (entry == NULL || eapol == NULL)
 		return;
 
@@ -186,7 +188,8 @@ void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
 	}
 
 	eapol->eap_type_authsrv = entry->eap_type_authsrv;
-	((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id;
+	sta = (struct sta_info *) eapol->sta;
+	ap_sta_set_vlan(sta->bss, sta, entry->vlan_id);
 
 	eapol->acct_multi_session_id_hi = entry->acct_multi_session_id_hi;
 	eapol->acct_multi_session_id_lo = entry->acct_multi_session_id_lo;
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index b2da379..f650eb3 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -28,7 +28,7 @@ struct rsn_pmksa_cache_entry {
 	struct wpabuf *cui;
 	struct radius_class_data radius_class;
 	u8 eap_type_authsrv;
-	int vlan_id;
+	struct vlan_description vlan_id;
 	int opportunistic;
 
 	u32 acct_multi_session_id_hi;
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 500beff..0bfd13d 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -34,6 +34,7 @@
 #include "wnm_ap.h"
 #include "ndisc_snoop.h"
 #include "sta_info.h"
+#include "vlan.h"
 
 static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
 				       struct sta_info *sta);
@@ -267,6 +268,8 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 	 * vlan_remove_dynamic() can check that no stations are left on the
 	 * AP_VLAN netdev.
 	 */
+	if (sta->vlan_id)
+		vlan_remove_dynamic(hapd, sta->vlan_id);
 	if (sta->vlan_id_bound) {
 		/*
 		 * Need to remove the STA entry before potentially removing the
@@ -623,6 +626,7 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
 		wpa_printf(MSG_ERROR, "malloc failed");
 		return NULL;
 	}
+	sta->bss = hapd;
 	sta->acct_interim_interval = hapd->conf->acct_interim_interval;
 	accounting_sta_get_id(hapd, sta);
 
@@ -789,6 +793,78 @@ int ap_sta_wps_cancel(struct hostapd_data *hapd,
 #endif /* CONFIG_WPS */
 
 
+int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
+		    struct vlan_description vlan_desc)
+{
+	struct hostapd_vlan *vlan = NULL, *wildcard_vlan = NULL;
+	int old_vlan_id, vlan_id = 0, ret = 0;
+
+	if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+		os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+	else if (vlan_desc.notempty) {
+		if (!os_memcmp(&vlan_desc, &sta->vlan_desc, sizeof(vlan_desc)))
+			return 0; /* nothing to change */
+
+		vlan = hapd->conf->vlan;
+		while (vlan) {
+			if (!os_memcmp(&vlan->vlan_desc, &vlan_desc,
+				       sizeof(vlan_desc)))
+				break;
+			if (vlan->vlan_id == VLAN_ID_WILDCARD)
+				wildcard_vlan = vlan;
+			vlan = vlan->next;
+		}
+		if (vlan)
+			vlan_id = vlan->vlan_id;
+		else if (wildcard_vlan) {
+			vlan = wildcard_vlan;
+			vlan_id = vlan_desc.untagged;
+		} else {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG, "missing vlan and "
+				       "wildcard for vlan=%d",
+				       vlan_desc.untagged);
+			vlan_id = 0;
+			ret = -1;
+			goto done;
+		}
+	}
+
+	if (vlan && vlan->vlan_id == VLAN_ID_WILDCARD) {
+		vlan = vlan_add_dynamic(hapd, vlan, vlan_id, vlan_desc);
+		if (vlan == NULL) {
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG, "could not add "
+				       "dynamic VLAN interface for vlan=%d",
+				       vlan_desc.untagged);
+			vlan_id = 0;
+			ret = -1;
+			goto done;
+		}
+
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN "
+			       "interface '%s'", vlan->ifname);
+	} else if (vlan && vlan->dynamic_vlan > 0) {
+		vlan->dynamic_vlan++;
+		hostapd_logger(hapd, sta->addr,
+			       HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG, "updated existing "
+			       "dynamic VLAN interface '%s'", vlan->ifname);
+	}
+done:
+	old_vlan_id = sta->vlan_id;
+	sta->vlan_id = vlan_id;
+	sta->vlan_desc = vlan_desc;
+
+	if (vlan_id != old_vlan_id && old_vlan_id)
+		vlan_remove_dynamic(hapd, old_vlan_id);
+
+	return ret;
+}
+
 int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta)
 {
 #ifndef CONFIG_NO_VLAN
@@ -801,20 +877,13 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta)
 	if (hapd->conf->ssid.vlan[0])
 		iface = hapd->conf->ssid.vlan;
 
-	if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
-		sta->vlan_id = 0;
-	else if (sta->vlan_id > 0) {
-		struct hostapd_vlan *wildcard_vlan = NULL;
+	if (sta->vlan_id > 0) {
 		vlan = hapd->conf->vlan;
 		while (vlan) {
 			if (vlan->vlan_id == sta->vlan_id)
 				break;
-			if (vlan->vlan_id == VLAN_ID_WILDCARD)
-				wildcard_vlan = vlan;
 			vlan = vlan->next;
 		}
-		if (!vlan)
-			vlan = wildcard_vlan;
 		if (vlan)
 			iface = vlan->ifname;
 	}
@@ -834,30 +903,12 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta)
 			       sta->vlan_id);
 		ret = -1;
 		goto done;
-	} else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) {
-		vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id);
-		if (vlan == NULL) {
-			hostapd_logger(hapd, sta->addr,
-				       HOSTAPD_MODULE_IEEE80211,
-				       HOSTAPD_LEVEL_DEBUG, "could not add "
-				       "dynamic VLAN interface for vlan_id=%d",
-				       sta->vlan_id);
-			ret = -1;
-			goto done;
-		}
-
-		iface = vlan->ifname;
-		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
-			       HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN "
-			       "interface '%s'", iface);
-	} else if (vlan && vlan->vlan_id == sta->vlan_id &&
-		   vlan->dynamic_vlan > 0) {
+	} else if (vlan && vlan->dynamic_vlan > 0) {
 		vlan->dynamic_vlan++;
 		hostapd_logger(hapd, sta->addr,
 			       HOSTAPD_MODULE_IEEE80211,
-			       HOSTAPD_LEVEL_DEBUG,
-			       "updated existing dynamic VLAN interface '%s'",
-			       iface);
+			       HOSTAPD_LEVEL_DEBUG, "updated existing "
+			       "dynamic VLAN interface '%s'", iface);
 	}
 
 	/* ref counters have been increased, so mark the station */
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 09deac6..89c9b3a 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -15,6 +15,7 @@
 #endif /* CONFIG_MESH */
 
 #include "list.h"
+#include "vlan.h"
 
 /* STA flags */
 #define WLAN_STA_AUTH BIT(0)
@@ -44,8 +45,10 @@
  * Supported Rates IEs). */
 #define WLAN_SUPP_RATES_MAX 32
 
+struct hostapd_data;
 
 struct sta_info {
+	struct hostapd_data *bss; /* required for set vlan in preauth */
 	struct sta_info *next; /* next entry in sta list */
 	struct sta_info *hnext; /* next entry in hash table list */
 	u8 addr[6];
@@ -119,6 +122,7 @@ struct sta_info {
 	struct rsn_preauth_interface *preauth_iface;
 
 	int vlan_id; /* 0: none, >0: VID */
+	struct vlan_description vlan_desc;
 	int vlan_id_bound; /* updated by ap_sta_bind_vlan() */
 	 /* PSKs from RADIUS authentication server */
 	struct hostapd_sta_wpa_psk_short *psk;
@@ -190,8 +194,6 @@ struct sta_info {
 #define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5)
 
 
-struct hostapd_data;
-
 int ap_for_each_sta(struct hostapd_data *hapd,
 		    int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
 			      void *ctx),
@@ -221,6 +223,8 @@ int ap_sta_wps_cancel(struct hostapd_data *hapd,
 		      struct sta_info *sta, void *ctx);
 #endif /* CONFIG_WPS */
 int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta);
+int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
+		    struct vlan_description vlan_id);
 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);
diff --git a/src/ap/vlan.h b/src/ap/vlan.h
new file mode 100644
index 0000000..aa681fd
--- /dev/null
+++ b/src/ap/vlan.h
@@ -0,0 +1,17 @@
+/*
+ * hostapd / VLAN definition
+ * Copyright (c) 2015, Jouni Malinen <j at w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef VLAN_H
+#define VLAN_H
+
+struct vlan_description {
+	int notempty; /* 0 : no vlan information present, 1: else */
+	int untagged; /* >0 802.1q vid */
+};
+
+#endif
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index e3df164..7beca3f 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -630,25 +630,26 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
 	struct hostapd_vlan *vlan = hapd->conf->vlan;
 	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
 	int vlan_naming = hapd->conf->ssid.vlan_naming;
-	int clean;
+	int clean, untagged;
 
 	wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
 
 	while (vlan) {
+		untagged = vlan->vlan_desc.untagged;
 		if (os_strcmp(ifname, vlan->ifname) == 0 && !vlan->configured) {
 			vlan->configured = 1;
 
 			if (hapd->conf->vlan_bridge[0]) {
 				os_snprintf(br_name, sizeof(br_name), "%s%d",
 					    hapd->conf->vlan_bridge,
-					    vlan->vlan_id);
+					    untagged);
 			} else if (tagged_interface) {
 				os_snprintf(br_name, sizeof(br_name),
 				            "br%s.%d", tagged_interface,
-					    vlan->vlan_id);
+					    untagged);
 			} else {
 				os_snprintf(br_name, sizeof(br_name),
-				            "brvlan%d", vlan->vlan_id);
+					    "brvlan%d", untagged);
 			}
 
 			dyn_iface_get(hapd, br_name,
@@ -662,15 +663,15 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
 					os_snprintf(vlan_ifname,
 						    sizeof(vlan_ifname),
 						    "%s.%d", tagged_interface,
-						    vlan->vlan_id);
+						    untagged);
 				else
 					os_snprintf(vlan_ifname,
 						    sizeof(vlan_ifname),
-						    "vlan%d", vlan->vlan_id);
+						    "vlan%d", untagged);
 
 				clean = 0;
 				ifconfig_up(tagged_interface);
-				if (!vlan_add(tagged_interface, vlan->vlan_id,
+				if (!vlan_add(tagged_interface, untagged,
 					      vlan_ifname))
 					clean |= DVLAN_CLEAN_VLAN;
 
@@ -701,26 +702,27 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd)
 	struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
 	char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
 	int vlan_naming = hapd->conf->ssid.vlan_naming;
-	int clean;
+	int clean, untagged;
 
 	wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
 
 	first = prev = vlan;
 
 	while (vlan) {
+		untagged = vlan->vlan_desc.untagged;
 		if (os_strcmp(ifname, vlan->ifname) == 0 &&
 		    vlan->configured) {
 			if (hapd->conf->vlan_bridge[0]) {
 				os_snprintf(br_name, sizeof(br_name), "%s%d",
 					    hapd->conf->vlan_bridge,
-					    vlan->vlan_id);
+					    untagged);
 			} else if (tagged_interface) {
 				os_snprintf(br_name, sizeof(br_name),
 				            "br%s.%d", tagged_interface,
-					    vlan->vlan_id);
+					    untagged);
 			} else {
 				os_snprintf(br_name, sizeof(br_name),
-				            "brvlan%d", vlan->vlan_id);
+					    "brvlan%d", untagged);
 			}
 
 			if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
@@ -732,11 +734,11 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd)
 					os_snprintf(vlan_ifname,
 						    sizeof(vlan_ifname),
 						    "%s.%d", tagged_interface,
-						    vlan->vlan_id);
+						    untagged);
 				else
 					os_snprintf(vlan_ifname,
 						    sizeof(vlan_ifname),
-						    "vlan%d", vlan->vlan_id);
+						    "vlan%d", untagged);
 
 				clean = dyn_iface_put(hapd, vlan_ifname);
 
@@ -1037,12 +1039,14 @@ void vlan_deinit(struct hostapd_data *hapd)
 
 struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
 				       struct hostapd_vlan *vlan,
-				       int vlan_id)
+				       int vlan_id,
+				       struct vlan_description vlan_desc)
 {
 	struct hostapd_vlan *n = NULL;
 	char *ifname, *pos;
 
-	if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID ||
+	if (vlan == NULL || vlan_desc.untagged <= 0 ||
+	    vlan_desc.untagged > MAX_VLAN_ID ||
 	    vlan->vlan_id != VLAN_ID_WILDCARD)
 		return NULL;
 
@@ -1061,6 +1065,7 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
 		goto free_ifname;
 
 	n->vlan_id = vlan_id;
+	n->vlan_desc = vlan_desc;
 	n->dynamic_vlan = 1;
 
 	os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
@@ -1087,7 +1092,7 @@ int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
 {
 	struct hostapd_vlan *vlan;
 
-	if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID)
+	if (vlan_id <= 0)
 		return 1;
 
 	wpa_printf(MSG_DEBUG, "VLAN: %s(ifname=%s vlan_id=%d)",
diff --git a/src/ap/vlan_init.h b/src/ap/vlan_init.h
index aeb2dc6..3adc528 100644
--- a/src/ap/vlan_init.h
+++ b/src/ap/vlan_init.h
@@ -15,7 +15,8 @@ int vlan_init(struct hostapd_data *hapd);
 void vlan_deinit(struct hostapd_data *hapd);
 struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
 				       struct hostapd_vlan *vlan,
-				       int vlan_id);
+				       int vlan_id,
+				       struct vlan_description vlan_desc);
 int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id);
 #else /* CONFIG_NO_VLAN */
 static inline int vlan_init(struct hostapd_data *hapd)
@@ -27,9 +28,11 @@ static inline void vlan_deinit(struct hostapd_data *hapd)
 {
 }
 
-static inline struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
-						     struct hostapd_vlan *vlan,
-						     int vlan_id)
+static inline struct hostapd_vlan *
+vlan_add_dynamic(struct hostapd_data *hapd,
+		 struct hostapd_vlan *vlan,
+		 int vlan_id,
+		 struct vlan_description vlan_desc)
 {
 	return NULL;
 }
-- 
2.1.4




More information about the Hostap mailing list