diff --git a/hostapd/Android.mk b/hostapd/Android.mk index cead73155..799d150ac 100644 --- a/hostapd/Android.mk +++ b/hostapd/Android.mk @@ -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 diff --git a/hostapd/Makefile b/hostapd/Makefile index fd3105e5e..306f19dbb 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -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 diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 503d47979..91eea9c7d 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,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)++; } diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index cb6fb1757..d56599bc6 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 88074f2e6..c66aae8ad 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 e68ec281d..7a15d1815 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; @@ -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 * diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index ec6f8a76b..b1d1660c4 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 " - "%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); } diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c index ec0037a57..d46f38962 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) { @@ -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; diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h index da81c146d..71f53b961 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 d399b1e57..ff32e8b00 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; } @@ -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; diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c index eb37c7886..1021be016 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,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); diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h index 4dc841f4b..daf083457 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; 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); diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 3d7c83913..47b35f044 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); @@ -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, diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index 359f480b6..c382ffd08 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) @@ -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 index 000000000..80ae98445 --- /dev/null +++ b/src/ap/vlan.c @@ -0,0 +1,29 @@ +/* + * hostapd / VLAN definition + * Copyright (c) 2016, Jouni Malinen + * + * 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 index 000000000..e93bbcff5 --- /dev/null +++ b/src/ap/vlan.h @@ -0,0 +1,27 @@ +/* + * hostapd / VLAN definition + * Copyright (c) 2015, Jouni Malinen + * + * 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 */ diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c index e3df164e2..4d93a706a 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 aeb2dc6f7..d17c82c32 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,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; } diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index 52ccac3f0..a3eb52194 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -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); }