]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
radius: Add tagged VLAN parsing
authorMichael Braun <michael-dev@fami-braun.de>
Thu, 21 Jan 2016 13:51:57 +0000 (14:51 +0100)
committerJouni Malinen <j@w1.fi>
Wed, 17 Feb 2016 09:46:13 +0000 (11:46 +0200)
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 <michael-dev@fami-braun.de>
src/ap/ap_config.c
src/ap/ieee802_11.c
src/ap/ieee802_11_auth.c
src/ap/ieee802_1x.c
src/ap/sta_info.c
src/ap/vlan.h
src/ap/wpa_auth_ie.c
src/radius/radius.c
src/radius/radius.h

index c66aae8adcbaf4e2d8d2997342b04eaea583b4b8..458faa4b8e31d01b6afc9b497095406f85bb0d69 100644 (file)
@@ -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) ||
index b1d1660c42027399277829ac44bdc0206e2d2c17..0620fc4a57d228802b1a2db82033351a5f6c5c5d 100644 (file)
@@ -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;
                }
index d46f38962ec981e8063d91e9c507c5e406cfb20e..f280709823b715a9754b737a69c01a4d6754e143 100644 (file)
@@ -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 &&
index ff32e8b00d0c1955f36de8589447607e614e3cff..50f251cc6f515368d96d5b843495bffd69531c76 100644 (file)
@@ -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;
index 47b35f044321e8d061a7cc9ab9aa8db451db2631..54b548d3fafd01cb9b1a0d8389839e74da23cc1a 100644 (file)
@@ -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;
index e93bbcff58ebf3c4fc6a103449a6924a842342a9..af84929decdc4bb8957005fa51a2b1aa2f5833ef 100644 (file)
@@ -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
index a3eb52194dfb1c6366c2471ae62876afe9fdc7aa..f79783b91929beed9bd57591fb35472928700ee9 100644 (file)
@@ -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);
        }
 
index a6304e1ccb4421af0e160ed29809ce4b24313b72..b92cce73fdc4c2524a29756523987edcdeba56d8 100644 (file)
@@ -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;
 }
 
index 313fc650f4c64d6a93ba7b006521ddd771421070..16d3f68b7c8853c1f8ae1474893939a3eb5df535 100644 (file)
@@ -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);