From 8e44c192da28a2b8755d65df5d4963496da36c2a Mon Sep 17 00:00:00 2001 From: Michael Braun Date: Thu, 21 Jan 2016 14:51:57 +0100 Subject: [PATCH] radius: Add tagged VLAN parsing 1. Add tagged VLAN to struct vlan_description (compile limited number of tagged VLANs per description) For k tagged VLANs, the first k entries in vlan_description.tagged are used. They are sorted in ascending order. All other entries are zero. This way os_memcmp() can find identical configurations. 2. Let tagged VLANs be parsed from RADIUS Access-Accept 3. Print VLAN %d+ with %d=untagged VID if tagged VLANs are set 4. Select an unused vlan_id > 4096 for new tagged VLAN configurations 5. Add EGRESS_VLAN RADIUS attribute parsing also for untagged VLANs Signed-off-by: Michael Braun --- src/ap/ap_config.c | 10 ++++++- src/ap/ieee802_11.c | 5 ++-- src/ap/ieee802_11_auth.c | 14 +++++++--- src/ap/ieee802_1x.c | 16 +++++++++--- src/ap/sta_info.c | 41 +++++++++++++++++++++++------ src/ap/vlan.h | 3 +++ src/ap/wpa_auth_ie.c | 9 ++++--- src/radius/radius.c | 56 ++++++++++++++++++++++++++++++++++++---- src/radius/radius.h | 4 ++- 9 files changed, 129 insertions(+), 29 deletions(-) diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index c66aae8ad..458faa4b8 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -673,10 +673,18 @@ int hostapd_vlan_valid(struct hostapd_vlan *vlan, struct vlan_description *vlan_desc) { struct hostapd_vlan *v = vlan; + int i; - if (!vlan_desc->notempty || vlan_desc->untagged <= 0 || + if (!vlan_desc->notempty || vlan_desc->untagged < 0 || vlan_desc->untagged > MAX_VLAN_ID) return 0; + for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) { + if (vlan_desc->tagged[i] < 0 || + vlan_desc->tagged[i] > MAX_VLAN_ID) + return 0; + } + if (!vlan_desc->untagged && !vlan_desc->tagged[0]) + return 0; while (v) { if (!vlan_compare(&v->vlan_desc, vlan_desc) || diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index b1d1660c4..0620fc4a5 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -1101,8 +1101,9 @@ static void handle_auth(struct hostapd_data *hapd, if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_id)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, - "Invalid VLAN %d received from RADIUS server", - vlan_id.untagged); + "Invalid VLAN %d%s received from RADIUS server", + vlan_id.untagged, + vlan_id.tagged[0] ? "+" : ""); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c index d46f38962..f28070982 100644 --- a/src/ap/ieee802_11_auth.c +++ b/src/ap/ieee802_11_auth.c @@ -502,6 +502,7 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, struct hostapd_acl_query_data *query, *prev; struct hostapd_cached_radius_acl *cache; struct radius_hdr *hdr = radius_msg_get_hdr(msg); + int *untagged, *tagged, *notempty; query = hapd->acl_queries; prev = NULL; @@ -559,8 +560,12 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, cache->acct_interim_interval = 0; } - cache->vlan_id.untagged = radius_msg_get_vlanid(msg); - cache->vlan_id.notempty = !!cache->vlan_id.untagged; + notempty = &cache->vlan_id.notempty; + untagged = &cache->vlan_id.untagged; + tagged = cache->vlan_id.tagged; + *notempty = !!radius_msg_get_vlanid(msg, untagged, + MAX_NUM_TAGGED_VLAN, + tagged); decode_tunnel_passwords(hapd, shared_secret, shared_secret_len, msg, req, cache); @@ -588,8 +593,9 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, hostapd_logger(hapd, query->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, - "Invalid VLAN %d received from RADIUS server", - cache->vlan_id.untagged); + "Invalid VLAN %d%s received from RADIUS server", + cache->vlan_id.untagged, + cache->vlan_id.tagged[0] ? "+" : ""); os_memset(&cache->vlan_id, 0, sizeof(cache->vlan_id)); } if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED && diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index ff32e8b00..50f251cc6 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -1622,6 +1622,9 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, int override_eapReq = 0; struct radius_hdr *hdr = radius_msg_get_hdr(msg); struct vlan_description vlan_desc; +#ifndef CONFIG_NO_VLAN + int *untagged, *tagged, *notempty; +#endif /* CONFIG_NO_VLAN */ os_memset(&vlan_desc, 0, sizeof(vlan_desc)); @@ -1689,8 +1692,12 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, case RADIUS_CODE_ACCESS_ACCEPT: #ifndef CONFIG_NO_VLAN if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED) { - vlan_desc.untagged = radius_msg_get_vlanid(msg); - vlan_desc.notempty = !!vlan_desc.untagged; + notempty = &vlan_desc.notempty; + untagged = &vlan_desc.untagged; + tagged = vlan_desc.tagged; + *notempty = !!radius_msg_get_vlanid(msg, untagged, + MAX_NUM_TAGGED_VLAN, + tagged); } if (vlan_desc.notempty && @@ -1699,8 +1706,9 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, - "Invalid VLAN %d received from RADIUS server", - vlan_desc.untagged); + "Invalid VLAN %d%s received from RADIUS server", + vlan_desc.untagged, + vlan_desc.tagged[0] ? "+" : ""); os_memset(&vlan_desc, 0, sizeof(vlan_desc)); ap_sta_set_vlan(hapd, sta, &vlan_desc); break; diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c index 47b35f044..54b548d3f 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -805,18 +805,37 @@ int ap_sta_wps_cancel(struct hostapd_data *hapd, #endif /* CONFIG_WPS */ +static int ap_sta_get_free_vlan_id(struct hostapd_data *hapd) +{ + struct hostapd_vlan *vlan; + int vlan_id = MAX_VLAN_ID + 2; + +retry: + for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { + if (vlan->vlan_id == vlan_id) { + vlan_id++; + goto retry; + } + } + return vlan_id; +} + + 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) { + 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 */ + /* Check if there is something to do */ + if (!vlan_compare(vlan_desc, sta->vlan_desc)) + return 0; /* nothing to change */ + + /* Now the real VLAN changed or the STA just needs its own vif */ + if (vlan_desc->notempty) { for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) { if (!vlan_compare(&vlan->vlan_desc, vlan_desc)) break; @@ -828,12 +847,17 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, } else if (wildcard_vlan) { vlan = wildcard_vlan; vlan_id = vlan_desc->untagged; + if (vlan_desc->tagged[0]) { + /* Tagged VLAN configuration */ + vlan_id = ap_sta_get_free_vlan_id(hapd); + } } else { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "missing VLAN and wildcard for vlan=%d", - vlan_desc->untagged); + "missing vlan and wildcard for vlan=%d%s", + vlan_desc->untagged, + vlan_desc->tagged[0] ? "+" : ""); vlan_id = 0; ret = -1; goto done; @@ -846,8 +870,9 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, - "could not add dynamic VLAN interface for vlan=%d", - vlan_desc->untagged); + "could not add dynamic VLAN interface for vlan=%d%s", + vlan_desc->untagged, + vlan_desc->tagged[0] ? "+" : ""); vlan_id = 0; ret = -1; goto done; diff --git a/src/ap/vlan.h b/src/ap/vlan.h index e93bbcff5..af84929de 100644 --- a/src/ap/vlan.h +++ b/src/ap/vlan.h @@ -9,9 +9,12 @@ #ifndef VLAN_H #define VLAN_H +#define MAX_NUM_TAGGED_VLAN 32 + struct vlan_description { int notempty; /* 0 : no vlan information present, 1: else */ int untagged; /* >0 802.1q vid */ + int tagged[MAX_NUM_TAGGED_VLAN]; /* first k items, ascending order */ }; #ifndef CONFIG_NO_VLAN diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index a3eb52194..f79783b91 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -712,13 +712,14 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, } } if (sm->pmksa && pmkid) { - struct vlan_description *vlan_desc; + struct vlan_description *vlan; - vlan_desc = sm->pmksa->vlan_desc; + vlan = sm->pmksa->vlan_desc; wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, - "PMKID found from PMKSA cache eap_type=%d vlan=%d", + "PMKID found from PMKSA cache eap_type=%d vlan=%d%s", sm->pmksa->eap_type_authsrv, - vlan_desc ? vlan_desc->untagged : 0); + vlan ? vlan->untagged : 0, + (vlan && vlan->tagged[0]) ? "+" : ""); os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN); } diff --git a/src/radius/radius.c b/src/radius/radius.c index a6304e1cc..b92cce73f 100644 --- a/src/radius/radius.c +++ b/src/radius/radius.c @@ -215,6 +215,7 @@ static const struct radius_attr_type radius_attrs[] = RADIUS_ATTR_INT32 }, { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_EGRESS_VLANID, "EGRESS-VLANID", RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type", @@ -1411,12 +1412,30 @@ struct radius_tunnel_attrs { }; +static int cmp_int(const void *a, const void *b) +{ + int x, y; + + x = *((int *) a); + y = *((int *) b); + return (x - y); +} + + /** * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information + * The k tagged vlans found are sorted by vlan_id and stored in the first k + * items of tagged. + * * @msg: RADIUS message - * Returns: VLAN ID for the first tunnel configuration or 0 if none is found + * @untagged: Pointer to store untagged vid + * @numtagged: Size of tagged + * @tagged: Pointer to store tagged list + * + * Returns: 0 if neither tagged nor untagged configuration is found, 1 otherwise */ -int radius_msg_get_vlanid(struct radius_msg *msg) +int radius_msg_get_vlanid(struct radius_msg *msg, int *untagged, int numtagged, + int *tagged) { struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun; size_t i; @@ -1424,8 +1443,12 @@ int radius_msg_get_vlanid(struct radius_msg *msg) const u8 *data; char buf[10]; size_t dlen; + int j, taggedidx = 0, vlan_id; os_memset(&tunnel, 0, sizeof(tunnel)); + for (j = 0; j < numtagged; j++) + tagged[j] = 0; + *untagged = 0; for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); @@ -1462,21 +1485,44 @@ int radius_msg_get_vlanid(struct radius_msg *msg) break; os_memcpy(buf, data, dlen); buf[dlen] = '\0'; + vlan_id = atoi(buf); + if (vlan_id <= 0) + break; tun->tag_used++; - tun->vlanid = atoi(buf); + tun->vlanid = vlan_id; + break; + case RADIUS_ATTR_EGRESS_VLANID: /* RFC 4675 */ + if (attr->length != 6) + break; + vlan_id = WPA_GET_BE24(data + 1); + if (vlan_id <= 0) + break; + if (data[0] == 0x32) + *untagged = vlan_id; + else if (data[0] == 0x31 && tagged && + taggedidx < numtagged) + tagged[taggedidx++] = vlan_id; break; } } + /* Use tunnel with the lowest tag for untagged VLAN id */ for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) { tun = &tunnel[i]; if (tun->tag_used && tun->type == RADIUS_TUNNEL_TYPE_VLAN && tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 && - tun->vlanid > 0) - return tun->vlanid; + tun->vlanid > 0) { + *untagged = tun->vlanid; + break; + } } + if (taggedidx) + qsort(tagged, taggedidx, sizeof(int), cmp_int); + + if (*untagged > 0 || taggedidx) + return 1; return 0; } diff --git a/src/radius/radius.h b/src/radius/radius.h index 313fc650f..16d3f68b7 100644 --- a/src/radius/radius.h +++ b/src/radius/radius.h @@ -80,6 +80,7 @@ enum { RADIUS_ATTR_USER_NAME = 1, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS = 52, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS = 53, RADIUS_ATTR_EVENT_TIMESTAMP = 55, + RADIUS_ATTR_EGRESS_VLANID = 56, RADIUS_ATTR_NAS_PORT_TYPE = 61, RADIUS_ATTR_TUNNEL_TYPE = 64, RADIUS_ATTR_TUNNEL_MEDIUM_TYPE = 65, @@ -274,7 +275,8 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, const u8 *data, size_t data_len, const u8 *secret, size_t secret_len); int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len); -int radius_msg_get_vlanid(struct radius_msg *msg); +int radius_msg_get_vlanid(struct radius_msg *msg, int *untagged, int numtagged, + int *tagged); char * radius_msg_get_tunnel_password(struct radius_msg *msg, int *keylen, const u8 *secret, size_t secret_len, struct radius_msg *sent_msg, size_t n);