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) ||
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;
}
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;
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);
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 &&
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));
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 &&
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;
#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;
} 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;
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;
#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
}
}
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);
}
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",
};
+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;
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);
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;
}
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,
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);