VLAN: Add per-STA vif option
authorMichael Braun <michael-dev@fami-braun.de>
Thu, 21 Jan 2016 13:52:00 +0000 (14:52 +0100)
committerJouni Malinen <j@w1.fi>
Wed, 17 Feb 2016 09:46:13 +0000 (11:46 +0200)
This allows the stations to be assigned to their own vif. It does not
need dynamic_vlan to be set. Make hostapd call ap_sta_set_vlan even if
!vlan_desc.notempty, so vlan_id can be assigned regardless.

Signed-off-by: Michael Braun <michael-dev@fami-braun.de>
hostapd/config_file.c
hostapd/hostapd.conf
src/ap/ap_config.h
src/ap/ieee802_11.c
src/ap/sta_info.c
src/ap/vlan_init.c

index 91eea9c..76f02ca 100644 (file)
@@ -2769,6 +2769,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
 #ifndef CONFIG_NO_VLAN
        } else if (os_strcmp(buf, "dynamic_vlan") == 0) {
                bss->ssid.dynamic_vlan = atoi(pos);
+       } else if (os_strcmp(buf, "per_sta_vif") == 0) {
+               bss->ssid.per_sta_vif = atoi(pos);
        } else if (os_strcmp(buf, "vlan_file") == 0) {
                if (hostapd_config_read_vlan_file(bss, pos)) {
                        wpa_printf(MSG_ERROR, "Line %d: failed to read VLAN file '%s'",
index 2c083f5..d535eec 100644 (file)
@@ -978,6 +978,17 @@ own_ip_addr=127.0.0.1
 # 2 = required; reject authentication if RADIUS server does not include VLAN ID
 #dynamic_vlan=0
 
+# Per-Station AP_VLAN interface mode
+# If enabled, each station is assigned its own AP_VLAN interface.
+# This implies per-station group keying and ebtables filtering of inter-STA
+# traffic (when passed through the AP).
+# If the sta is not assigned to any VLAN, then its AP_VLAN interface will be
+# added to the bridge given by the "bridge" configuration option (see above).
+# Otherwise, it will be added to the per-VLAN bridge.
+# 0 = disabled (default)
+# 1 = enabled
+#per_sta_vif=0
+
 # VLAN interface list for dynamic VLAN mode is read from a separate text file.
 # This list is used to map VLAN ID from the RADIUS server to a network
 # interface. Each station is bound to one interface in the same way as with
index 7a15d18..763eaaa 100644 (file)
@@ -104,6 +104,7 @@ struct hostapd_ssid {
 #define DYNAMIC_VLAN_NAMING_WITH_DEVICE 1
 #define DYNAMIC_VLAN_NAMING_END 2
        int vlan_naming;
+       int per_sta_vif;
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
        char *vlan_tagged_interface;
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
index 0620fc4..de32f66 100644 (file)
@@ -1097,23 +1097,23 @@ static void handle_auth(struct hostapd_data *hapd,
        sta->last_seq_ctrl = seq_ctrl;
        sta->last_subtype = WLAN_FC_STYPE_AUTH;
 
-       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 %d%s received from RADIUS server",
-                                      vlan_id.untagged,
-                                      vlan_id.tagged[0] ? "+" : "");
-                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-                       goto fail;
-               }
-               if (ap_sta_set_vlan(hapd, sta, &vlan_id) < 0) {
-                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-                       goto fail;
-               }
+       if (vlan_id.notempty &&
+           !hostapd_vlan_valid(hapd->conf->vlan, &vlan_id)) {
                hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
-                              HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
+                              HOSTAPD_LEVEL_INFO,
+                              "Invalid VLAN %d%s received from RADIUS server",
+                              vlan_id.untagged,
+                              vlan_id.tagged[0] ? "+" : "");
+               resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+               goto fail;
+       }
+       if (ap_sta_set_vlan(hapd, sta, &vlan_id) < 0) {
+               resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+               goto fail;
        }
+       if (sta->vlan_id)
+               hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+                              HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
 
        hostapd_free_psk_list(sta->psk);
        if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) {
index 54b548d..a8c0308 100644 (file)
@@ -831,11 +831,35 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
                vlan_desc = NULL;
 
        /* Check if there is something to do */
-       if (!vlan_compare(vlan_desc, sta->vlan_desc))
+       if (hapd->conf->ssid.per_sta_vif && !sta->vlan_id) {
+               /* This sta is lacking its own vif */
+       } else if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED &&
+                  !hapd->conf->ssid.per_sta_vif && sta->vlan_id) {
+               /* sta->vlan_id needs to be reset */
+       } else 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) {
+       if (hapd->conf->ssid.per_sta_vif) {
+               /* Assign a new vif, always */
+               /* find a free vlan_id sufficiently big */
+               vlan_id = ap_sta_get_free_vlan_id(hapd);
+               /* Get wildcard VLAN */
+               for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
+                       if (vlan->vlan_id == VLAN_ID_WILDCARD)
+                               break;
+               }
+               if (!vlan) {
+                       hostapd_logger(hapd, sta->addr,
+                                      HOSTAPD_MODULE_IEEE80211,
+                                      HOSTAPD_LEVEL_DEBUG,
+                                      "per_sta_vif missing wildcard");
+                       vlan_id = 0;
+                       ret = -1;
+                       goto done;
+               }
+       } else if (vlan_desc && vlan_desc->notempty) {
                for (vlan = hapd->conf->vlan; vlan; vlan = vlan->next) {
                        if (!vlan_compare(&vlan->vlan_desc, vlan_desc))
                                break;
index 56959e5..8709e3d 100644 (file)
@@ -686,7 +686,7 @@ static void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
 {
        char br_name[IFNAMSIZ];
        struct hostapd_vlan *vlan;
-       int untagged, *tagged, i;
+       int untagged, *tagged, i, notempty;
 
        wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
 
@@ -701,10 +701,16 @@ static void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
 
        vlan->configured = 1;
 
+       notempty = vlan->vlan_desc.notempty;
        untagged = vlan->vlan_desc.untagged;
        tagged = vlan->vlan_desc.tagged;
 
-       if (untagged > 0 && untagged <= MAX_VLAN_ID) {
+       if (!notempty) {
+               /* Non-VLAN STA */
+               if (hapd->conf->bridge[0] &&
+                   !br_addif(hapd->conf->bridge, ifname))
+                       vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
+       } else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
                vlan_bridge_name(br_name, hapd, untagged);
 
                vlan_get_bridge(br_name, hapd, untagged);
@@ -792,6 +798,7 @@ static void vlan_dellink(const char *ifname, struct hostapd_data *hapd)
                return;
 
        if (vlan->configured) {
+               int notempty = vlan->vlan_desc.notempty;
                int untagged = vlan->vlan_desc.untagged;
                int *tagged = vlan->vlan_desc.tagged;
                char br_name[IFNAMSIZ];
@@ -808,7 +815,12 @@ static void vlan_dellink(const char *ifname, struct hostapd_data *hapd)
                        vlan_put_bridge(br_name, hapd, tagged[i]);
                }
 
-               if (untagged > 0 && untagged <= MAX_VLAN_ID) {
+               if (!notempty) {
+                       /* Non-VLAN STA */
+                       if (hapd->conf->bridge[0] &&
+                           (vlan->clean & DVLAN_CLEAN_WLAN_PORT))
+                               br_delif(hapd->conf->bridge, ifname);
+               } else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
                        vlan_bridge_name(br_name, hapd, untagged);
 
                        if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
@@ -1064,7 +1076,8 @@ int vlan_init(struct hostapd_data *hapd)
        hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd);
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
 
-       if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED &&
+       if ((hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED ||
+            hapd->conf->ssid.per_sta_vif) &&
            !hapd->conf->vlan) {
                /* dynamic vlans enabled but no (or empty) vlan_file given */
                struct hostapd_vlan *vlan;