VLAN: Separate station grouping and uplink configuration
authorMichael Braun <michael-dev@fami-braun.de>
Thu, 21 Jan 2016 13:51:56 +0000 (14:51 +0100)
committerJouni Malinen <j@w1.fi>
Wed, 17 Feb 2016 09:46:11 +0000 (11:46 +0200)
Separate uplink configuration (IEEE 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 a struct
vlan_description vlan_desc member that describes the uplink
configuration requested. Currently this is just an int untagged IEEE
802.1q VID, but can be extended to tagged VLANs and other settings
easily.

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

To ease addition of tagged VLAN support, a member notempty is added to
struct vlan_description. Is is set to 1 if an untagged or tagged VLAN
assignment is requested and needs to be validated. The inverted form
allows os_zalloc() to initialize an empty description.

Though 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 do 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 IEEE 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 IEEE 802.1q VID.

Signed-off-by: Michael Braun <michael-dev@fami-braun.de>
19 files changed:
hostapd/Android.mk
hostapd/Makefile
hostapd/config_file.c
hostapd/ctrl_iface.c
src/ap/ap_config.c
src/ap/ap_config.h
src/ap/ieee802_11.c
src/ap/ieee802_11_auth.c
src/ap/ieee802_11_auth.h
src/ap/ieee802_1x.c
src/ap/pmksa_cache_auth.c
src/ap/pmksa_cache_auth.h
src/ap/sta_info.c
src/ap/sta_info.h
src/ap/vlan.c [new file with mode: 0644]
src/ap/vlan.h [new file with mode: 0644]
src/ap/vlan_init.c
src/ap/vlan_init.h
src/ap/wpa_auth_ie.c

index cead731..799d150 100644 (file)
@@ -176,6 +176,7 @@ ifdef CONFIG_NO_VLAN
 L_CFLAGS += -DCONFIG_NO_VLAN
 else
 OBJS += src/ap/vlan_init.c
+OBJS += src/ap/vlan.c
 ifdef CONFIG_VLAN_NETLINK
 ifdef CONFIG_FULL_DYNAMIC_VLAN
 OBJS += src/ap/vlan_util.c
index fd3105e..306f19d 100644 (file)
@@ -194,6 +194,7 @@ ifdef CONFIG_NO_VLAN
 CFLAGS += -DCONFIG_NO_VLAN
 else
 OBJS += ../src/ap/vlan_init.o
+OBJS += ../src/ap/vlan.o
 ifdef CONFIG_VLAN_NETLINK
 ifdef CONFIG_FULL_DYNAMIC_VLAN
 OBJS += ../src/ap/vlan_util.o
index 503d479..91eea9c 100644 (file)
@@ -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,10 @@ 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;
+               os_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)++;
        }
 
index cb6fb17..d56599b 100644 (file)
@@ -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,8 @@ 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 ||
+                                    !vlan_compare(&vlan_id, sta->vlan_desc)))
                                        ap_sta_disconnect(
                                                hapd, sta, sta->addr,
                                                WLAN_REASON_UNSPECIFIED);
@@ -1346,7 +1347,8 @@ 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 &&
+                                    vlan_compare(&vlan_id, sta->vlan_desc)))
                                        ap_sta_disconnect(
                                                hapd, sta, sta->addr,
                                                WLAN_REASON_UNSPECIFIED);
index 88074f2..c66aae8 100644 (file)
@@ -629,7 +629,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;
 
@@ -669,11 +669,18 @@ int hostapd_rate_found(int *list, int rate)
 }
 
 
-int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id)
+int hostapd_vlan_valid(struct hostapd_vlan *vlan,
+                      struct vlan_description *vlan_desc)
 {
        struct hostapd_vlan *v = vlan;
+
+       if (!vlan_desc->notempty || vlan_desc->untagged <= 0 ||
+           vlan_desc->untagged > MAX_VLAN_ID)
+               return 0;
+
        while (v) {
-               if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD)
+               if (!vlan_compare(&v->vlan_desc, vlan_desc) ||
+                   v->vlan_id == VLAN_ID_WILDCARD)
                        return 1;
                v = v->next;
        }
index e68ec28..7a15d18 100644 (file)
@@ -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;
@@ -690,13 +692,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_valid(struct hostapd_vlan *vlan,
+                      struct vlan_description *vlan_desc);
 const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan,
                                        int vlan_id);
 struct hostapd_radius_attr *
index ec6f8a7..b1d1660 100644 (file)
@@ -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 (!hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) {
+       if (vlan_id.notempty) {
+               if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_id)) {
                        hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
-                                      HOSTAPD_LEVEL_INFO, "Invalid VLAN ID "
-                                      "%d received from RADIUS server",
-                                      vlan_id);
+                                      HOSTAPD_LEVEL_INFO,
+                                      "Invalid VLAN %d received from RADIUS server",
+                                      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);
        }
index ec0037a..d46f389 100644 (file)
@@ -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)
 {
@@ -222,7 +223,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))
@@ -260,7 +262,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)
 {
@@ -271,7 +274,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)
@@ -556,7 +559,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);
@@ -579,17 +583,17 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
                    !cache->psk)
                        cache->accepted = HOSTAPD_ACL_REJECT;
 
-               if (cache->vlan_id &&
-                   !hostapd_vlan_id_valid(hapd->conf->vlan, cache->vlan_id)) {
+               if (cache->vlan_id.notempty &&
+                   !hostapd_vlan_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;
index da81c14..71f53b9 100644 (file)
@@ -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);
index d399b1e..ff32e8b 100644 (file)
@@ -222,7 +222,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;
        }
@@ -1151,7 +1151,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
                sta->eapol_sm->authFail = FALSE;
                if (sta->eapol_sm->eap)
                        eap_sm_notify_cached(sta->eapol_sm->eap);
-               pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm);
+               pmksa_cache_to_eapol_data(hapd, pmksa, sta->eapol_sm);
                ap_sta_bind_vlan(hapd, sta);
        } else {
                if (reassoc) {
@@ -1617,10 +1617,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) {
@@ -1684,27 +1687,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_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,
@@ -1715,7 +1718,18 @@ 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;
index eb37c78..1021be0 100644 (file)
@@ -38,6 +38,7 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
 
 static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
 {
+       os_free(entry->vlan_desc);
        os_free(entry->identity);
        wpabuf_free(entry->cui);
 #ifndef CONFIG_NO_RADIUS
@@ -126,6 +127,8 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
 static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
                                        struct eapol_state_machine *eapol)
 {
+       struct vlan_description *vlan_desc;
+
        if (eapol == NULL)
                return;
 
@@ -146,15 +149,26 @@ 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;
+
+       vlan_desc = ((struct sta_info *) eapol->sta)->vlan_desc;
+       if (vlan_desc && vlan_desc->notempty) {
+               entry->vlan_desc = os_zalloc(sizeof(struct vlan_description));
+               if (entry->vlan_desc)
+                       *entry->vlan_desc = *vlan_desc;
+       } else {
+               entry->vlan_desc = NULL;
+       }
 
        entry->acct_multi_session_id = eapol->acct_multi_session_id;
 }
 
 
-void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
+void pmksa_cache_to_eapol_data(struct hostapd_data *hapd,
+                              struct rsn_pmksa_cache_entry *entry,
                               struct eapol_state_machine *eapol)
 {
+       struct sta_info *sta;
+
        if (entry == NULL || eapol == NULL)
                return;
 
@@ -185,7 +199,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(hapd, sta, entry->vlan_desc);
 
        eapol->acct_multi_session_id = entry->acct_multi_session_id;
 }
@@ -335,7 +350,13 @@ pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
        radius_copy_class(&entry->radius_class, &old_entry->radius_class);
 #endif /* CONFIG_NO_RADIUS */
        entry->eap_type_authsrv = old_entry->eap_type_authsrv;
-       entry->vlan_id = old_entry->vlan_id;
+       if (old_entry->vlan_desc) {
+               entry->vlan_desc = os_zalloc(sizeof(struct vlan_description));
+               if (entry->vlan_desc)
+                       *entry->vlan_desc = *old_entry->vlan_desc;
+       } else {
+               entry->vlan_desc = NULL;
+       }
        entry->opportunistic = 1;
 
        pmksa_cache_link_entry(pmksa, entry);
index 4dc841f..daf0834 100644 (file)
@@ -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_desc;
        int opportunistic;
 
        u64 acct_multi_session_id;
@@ -56,7 +56,8 @@ struct rsn_pmksa_cache_entry *
 pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
                    const struct rsn_pmksa_cache_entry *old_entry,
                    const u8 *aa, const u8 *pmkid);
-void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
+void pmksa_cache_to_eapol_data(struct hostapd_data *hapd,
+                              struct rsn_pmksa_cache_entry *entry,
                               struct eapol_state_machine *eapol);
 void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
                            struct rsn_pmksa_cache_entry *entry);
index 3d7c839..47b35f0 100644 (file)
@@ -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);
@@ -266,6 +267,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
@@ -802,6 +805,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) {
+               vlan_desc = NULL;
+       } else if (vlan_desc && vlan_desc->notempty) {
+               if (!vlan_compare(vlan_desc, sta->vlan_desc))
+                       return 0; /* nothing to change */
+
+               for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+                       if (!vlan_compare(&vlan->vlan_desc, vlan_desc))
+                               break;
+                       if (vlan->vlan_id == VLAN_ID_WILDCARD)
+                               wildcard_vlan = vlan;
+               }
+               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 ? &vlan->vlan_desc : NULL;
+
+       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
@@ -814,20 +889,11 @@ 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;
-               vlan = hapd->conf->vlan;
-               while (vlan) {
+       if (sta->vlan_id > 0) {
+               for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
                        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;
        }
@@ -847,24 +913,7 @@ 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,
index 359f480..c382ffd 100644 (file)
@@ -15,6 +15,7 @@
 #endif /* CONFIG_MESH */
 
 #include "list.h"
+#include "vlan.h"
 
 /* STA flags */
 #define WLAN_STA_AUTH BIT(0)
@@ -118,6 +119,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;
@@ -220,6 +222,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_desc);
 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.c b/src/ap/vlan.c
new file mode 100644 (file)
index 0000000..80ae984
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * hostapd / VLAN definition
+ * Copyright (c) 2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "ap/vlan.h"
+
+/* compare the two arguments, NULL is treated as empty
+ * return zero iff they are equal
+ */
+int vlan_compare(struct vlan_description *a, struct vlan_description *b)
+{
+       const int a_empty = !a || !a->notempty;
+       const int b_empty = !b || !b->notempty;
+
+       if (a_empty && b_empty)
+               return 0;
+       if (a_empty || b_empty)
+               return 1;
+       if (a->untagged != b->untagged)
+               return 1;
+       return 0;
+}
diff --git a/src/ap/vlan.h b/src/ap/vlan.h
new file mode 100644 (file)
index 0000000..e93bbcf
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * hostapd / VLAN definition
+ * Copyright (c) 2015, Jouni Malinen <j@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 */
+};
+
+#ifndef CONFIG_NO_VLAN
+int vlan_compare(struct vlan_description *a, struct vlan_description *b);
+#else /* CONFIG_NO_VLAN */
+static inline int
+vlan_compare(struct vlan_description *a, struct vlan_description *b)
+{
+       return 0;
+}
+#endif /* CONFIG_NO_VLAN */
+
+#endif /* VLAN_H */
index e3df164..4d93a70 100644 (file)
@@ -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,13 +1039,13 @@ 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 ||
-           vlan->vlan_id != VLAN_ID_WILDCARD)
+       if (vlan == NULL || vlan->vlan_id != VLAN_ID_WILDCARD)
                return NULL;
 
        wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)",
@@ -1061,6 +1063,8 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
                goto free_ifname;
 
        n->vlan_id = vlan_id;
+       if (vlan_desc)
+               n->vlan_desc = *vlan_desc;
        n->dynamic_vlan = 1;
 
        os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
@@ -1087,7 +1091,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)",
index aeb2dc6..d17c82c 100644 (file)
@@ -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,9 @@ 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;
 }
index 52ccac3..a3eb521 100644 (file)
@@ -712,11 +712,13 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
                }
        }
        if (sm->pmksa && pmkid) {
+               struct vlan_description *vlan_desc;
+
+               vlan_desc = sm->pmksa->vlan_desc;
                wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
-                                "PMKID found from PMKSA cache "
-                                "eap_type=%d vlan_id=%d",
+                                "PMKID found from PMKSA cache eap_type=%d vlan=%d",
                                 sm->pmksa->eap_type_authsrv,
-                                sm->pmksa->vlan_id);
+                                vlan_desc ? vlan_desc->untagged : 0);
                os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN);
        }