This patch 1. adds 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. lets tagged VLANs be parsed from RADIUS Access-Accept 3. prints VLAN %d+ with %d=untagged VID if tagged VLANs are set 4. Selects an unused vlan_id > 4096 for new tagged vlan configurations 5. adds EGRESS_VLAN RADIUS attribute parsing also for untagged vlans Signed-off-by: Michael Braun <michael-dev@xxxxxxxxxxxxx> -- v2: squash EGRESS_VLAN for RADIUS request dumper patch v2: rework to make per_sta_vif based on top of this --- src/ap/ap_config.c | 8 ++++++- src/ap/ieee802_11.c | 5 +++-- src/ap/ieee802_11_auth.c | 15 +++++++++---- src/ap/ieee802_1x.c | 15 +++++++++---- src/ap/sta_info.c | 40 +++++++++++++++++++++++++++------ src/ap/vlan.h | 3 +++ src/radius/radius.c | 57 +++++++++++++++++++++++++++++++++++++++++++----- src/radius/radius.h | 4 +++- 8 files changed, 123 insertions(+), 24 deletions(-) diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 6df9d47..f2e5814 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -672,10 +672,16 @@ int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, struct vlan_description vlan_id) { struct hostapd_vlan *v = vlan; + int i; - if (!vlan_id.notempty || vlan_id.untagged <= 0 || + if (!vlan_id.notempty || vlan_id.untagged < 0 || vlan_id.untagged > MAX_VLAN_ID) return 0; + for (i = 0; i < MAX_NUM_TAGGED_VLAN; i++) + if (vlan_id.tagged[i] < 0 || vlan_id.tagged[i] > MAX_VLAN_ID) + return 0; + if (!vlan_id.untagged && !vlan_id.tagged[0]) + return 0; while (v) { if (!os_memcmp(&v->vlan_desc, &vlan_id, sizeof(vlan_id)) diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 11db12a..1d30abb 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_id_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); + "%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 fcd0451..8ab0efc 100644 --- a/src/ap/ieee802_11_auth.c +++ b/src/ap/ieee802_11_auth.c @@ -499,6 +499,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; @@ -556,8 +557,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); @@ -585,8 +590,10 @@ 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 fa87ce2..04b9a25 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -1598,6 +1598,7 @@ 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; + int *untagged, *tagged, *notempty; os_memset(&vlan_desc, 0, sizeof(vlan_desc)); @@ -1665,8 +1666,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 && @@ -1675,8 +1680,10 @@ 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 0bfd13d..342b6ab 100644 --- a/src/ap/sta_info.c +++ b/src/ap/sta_info.c @@ -793,6 +793,23 @@ 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 = NULL; + int vlan_id = MAX_VLAN_ID + 2; +retry: + vlan = hapd->conf->vlan; + while (vlan) { + if (vlan->vlan_id == vlan_id) + break; + vlan = vlan->next; + } + if (!vlan) + return vlan_id; + vlan_id++; + goto retry; +} + int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, struct vlan_description vlan_desc) { @@ -801,10 +818,13 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta, if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) os_memset(&vlan_desc, 0, sizeof(vlan_desc)); - else if (vlan_desc.notempty) { - if (!os_memcmp(&vlan_desc, &sta->vlan_desc, sizeof(vlan_desc))) - return 0; /* nothing to change */ + /* check if there is something to do */ + if (!os_memcmp(&vlan_desc, &sta->vlan_desc, sizeof(vlan_desc))) + return 0; /* nothing to change */ + + /* now the real vlan changed or the sta just needs its own vif */ + if (vlan_desc.notempty) { vlan = hapd->conf->vlan; while (vlan) { if (!os_memcmp(&vlan->vlan_desc, &vlan_desc, @@ -819,12 +839,16 @@ 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); + "wildcard for vlan=%d%s", + vlan_desc.untagged, + vlan_desc.tagged[0] ? "+" : ""); vlan_id = 0; ret = -1; goto done; @@ -837,8 +861,10 @@ 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); + "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 aa681fd..c42be92 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 */ }; #endif diff --git a/src/radius/radius.c b/src/radius/radius.c index 266b29f..5dcfbb5 100644 --- a/src/radius/radius.c +++ b/src/radius/radius.c @@ -256,6 +256,8 @@ static const struct radius_attr_type radius_attrs[] = RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, "WLAN-Group-Mgmt-Pairwise-Cipher", RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_EGRESS_VLANID, "EGRESS-VLANID", + RADIUS_ATTR_HEXDUMP }, }; #define RADIUS_ATTRS ARRAY_SIZE(radius_attrs) @@ -1422,13 +1424,29 @@ struct radius_tunnel_attrs { int vlanid; }; +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 + * @param untagged: pointer to store untagged vid + * @param numtagged: size of tagged + * @param tagged: pointer to store tagged list + * + * Returns: 0 if neither tagged nor untagged configuration is found, 1 else */ -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; @@ -1436,8 +1454,12 @@ int radius_msg_get_vlanid(struct radius_msg *msg) const u8 *data; char buf[10]; size_t dlen; + int taggedidx = 0, vlan_id; os_memset(&tunnel, 0, sizeof(tunnel)); + for (i = 0; i < numtagged; i++) + tagged[i] = 0; + *untagged = 0; for (i = 0; i < msg->attr_used; i++) { attr = radius_get_attr_hdr(msg, i); @@ -1474,21 +1496,46 @@ 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 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) + return 1; + if (taggedidx) + return 1; return 0; } diff --git a/src/radius/radius.h b/src/radius/radius.h index f14de53..c841c89 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, @@ -275,7 +276,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); -- 2.1.4 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap