From: Michael Braun <michael-dev@xxxxxxxxxxxxx> 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@xxxxxxxxxxxxx> v3: * use vlan_compare * pointer for function args * pointer for vlan in sta and pmksa cache Signed-off-by: Michael Braun <michael-dev@xxxxxxxxxxxxx> --- hostapd/Makefile | 1 + hostapd/config_file.c | 6 ++- hostapd/ctrl_iface.c | 8 ++-- src/ap/ap_config.c | 13 ++++-- src/ap/ap_config.h | 9 ++-- src/ap/ieee802_11.c | 17 +++++--- src/ap/ieee802_11_auth.c | 28 ++++++------ src/ap/ieee802_11_auth.h | 6 ++- src/ap/ieee802_1x.c | 51 ++++++++++++++-------- src/ap/pmksa_cache_auth.c | 25 +++++++++-- src/ap/pmksa_cache_auth.h | 2 +- src/ap/sta_info.c | 108 +++++++++++++++++++++++++++++++++------------- src/ap/sta_info.h | 8 +++- src/ap/vlan.c | 27 ++++++++++++ src/ap/vlan.h | 27 ++++++++++++ src/ap/vlan_init.c | 38 ++++++++-------- src/ap/vlan_init.h | 11 +++-- src/ap/wpa_auth_ie.c | 6 ++- 18 files changed, 285 insertions(+), 106 deletions(-) create mode 100644 src/ap/vlan.c create mode 100644 src/ap/vlan.h diff --git a/hostapd/Makefile b/hostapd/Makefile index 45afedf..e94f8b3 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -190,6 +190,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 diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 503d479..9e49d7b 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..d56599b 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,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); diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 88074f2..d67096e 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -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; } diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 44bcccc..84441a8 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; @@ -688,13 +690,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 * diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index ec6f8a7..be99dda 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 (!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 " + 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..ffa75a4 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 && - !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; 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 607f941..6c79147 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -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; } @@ -1601,10 +1601,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) { @@ -1668,27 +1671,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, @@ -1699,7 +1702,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..73eff85 100644 --- a/src/ap/pmksa_cache_auth.c +++ b/src/ap/pmksa_cache_auth.c @@ -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,7 +149,14 @@ 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_hi = eapol->acct_multi_session_id_hi; entry->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo; @@ -156,6 +166,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 +198,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_desc); eapol->acct_multi_session_id_hi = entry->acct_multi_session_id_hi; eapol->acct_multi_session_id_lo = entry->acct_multi_session_id_lo; @@ -337,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); diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h index b2da379..ef2d331 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_desc; int opportunistic; u32 acct_multi_session_id_hi; diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 68fff4c..23963b7 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,77 @@ 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 */ + + vlan = hapd->conf->vlan; + while (vlan) { + if (!vlan_compare(&vlan->vlan_desc, 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 ? &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 @@ -801,20 +876,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 +902,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..338c29b 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_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 index 0000000..0372c48 --- /dev/null +++ b/src/ap/vlan.c @@ -0,0 +1,27 @@ +/* + * hostapd / VLAN definition + * Copyright (c) 2016, Jouni Malinen <j@xxxxx> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#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 index 0000000..f0e1901 --- /dev/null +++ b/src/ap/vlan.h @@ -0,0 +1,27 @@ +/* + * hostapd / VLAN definition + * Copyright (c) 2015, Jouni Malinen <j@xxxxx> + * + * 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 diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c index e3df164..4d93a70 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,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)", diff --git a/src/ap/vlan_init.h b/src/ap/vlan_init.h index aeb2dc6..811b5a8 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; } diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index 52ccac3..5a9b9bd 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -481,6 +481,7 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, u32 selector; size_t i; const u8 *pmkid = NULL; + struct vlan_description *vlan_desc; if (wpa_auth == NULL || sm == NULL) return WPA_NOT_ENABLED; @@ -712,11 +713,12 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, } } if (sm->pmksa && pmkid) { + 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", + "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); } -- 2.1.4 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap