aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJouni Malinen <j@w1.fi>2006-09-29 23:07:49 (GMT)
committerJouni Malinen <j@w1.fi>2006-09-29 23:07:49 (GMT)
commit245157896006b7ee3490b5c7cddd87360fbee57f (patch)
treea17df4349e0b9a2d0cd03fdfb5cd4516bcc4d893
parent1e2a7c68eaaabdf151fad00f94551e12f09c2569 (diff)
downloadhostap-history-245157896006b7ee3490b5c7cddd87360fbee57f.zip
hostap-history-245157896006b7ee3490b5c7cddd87360fbee57f.tar.gz
hostap-history-245157896006b7ee3490b5c7cddd87360fbee57f.tar.bz2
Merge more dynamic VLAN support from Devicescape hostapd. This commit
adds configuration options for enabling dynamic VLAN feature and basic functionality for adding and removing wireless VLAN interfaces and binding STAs to them based on RADIUS Access-Response attributes. This does not yet complete dynamic VLAN support since IEEE 802.1X and WPA authenticator state machines are not yet modified to create a new group key state machine for each new VLAN. (from Devicescape)
-rw-r--r--hostapd/Makefile8
-rw-r--r--hostapd/config.c145
-rw-r--r--hostapd/config.h26
-rw-r--r--hostapd/driver.h39
-rw-r--r--hostapd/driver_devicescape.c110
-rw-r--r--hostapd/hostapd.conf26
-rw-r--r--hostapd/hostapd.h10
-rw-r--r--hostapd/hostapd.vlan9
-rw-r--r--hostapd/ieee802_11.c20
-rw-r--r--hostapd/ieee802_1x.c12
-rw-r--r--hostapd/priv_netlink.h4
-rw-r--r--hostapd/sta_info.c102
-rw-r--r--hostapd/sta_info.h2
-rw-r--r--hostapd/vlan_init.c823
-rw-r--r--hostapd/vlan_init.h29
15 files changed, 1361 insertions, 4 deletions
diff --git a/hostapd/Makefile b/hostapd/Makefile
index 674a7a8..1697e06 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -40,7 +40,7 @@ OBJS = hostapd.o eloop.o ieee802_1x.o eapol_sm.o radius.o md5.o rc4.o md4.o \
common.o ieee802_11.o config.o ieee802_11_auth.o accounting.o \
sta_info.o radius_client.o sha1.o wpa.o aes_wrap.o ctrl_iface.o \
driver_conf.o os_$(CONFIG_OS).o preauth.o pmksa_cache.o beacon.o \
- hw_features.o wme.o ap_list.o mlme.o
+ hw_features.o wme.o ap_list.o mlme.o vlan_init.o
HOBJS=hlr_auc_gw.o common.o os_$(CONFIG_OS).o milenage.o aes_wrap.o
@@ -312,6 +312,12 @@ ifdef CONFIG_IPV6
CFLAGS += -DCONFIG_IPV6
endif
+ifdef CONFIG_FULL_DYNAMIC_VLAN
+# define CONFIG_FULL_DYNAMIC_VLAN to have hostapd manipulate bridges
+# and vlan interfaces for the vlan feature.
+CFLAGS += -DCONFIG_FULL_DYNAMIC_VLAN
+endif
+
ALL=hostapd hostapd_cli
all: verify_config $(ALL)
diff --git a/hostapd/config.c b/hostapd/config.c
index 07a508e..8c43e6e 100644
--- a/hostapd/config.c
+++ b/hostapd/config.c
@@ -28,6 +28,103 @@
#define MAX_STA_COUNT 2007
+static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss,
+ const char *fname)
+{
+ FILE *f;
+ char buf[128], *pos, *pos2;
+ int line = 0, vlan_id;
+ struct hostapd_vlan *vlan;
+
+ f = fopen(fname, "r");
+ if (!f) {
+ printf("VLAN file '%s' not readable.\n", fname);
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ line++;
+
+ if (buf[0] == '#')
+ continue;
+ pos = buf;
+ while (*pos != '\0') {
+ if (*pos == '\n') {
+ *pos = '\0';
+ break;
+ }
+ pos++;
+ }
+ if (buf[0] == '\0')
+ continue;
+
+ if (buf[0] == '*') {
+ vlan_id = VLAN_ID_WILDCARD;
+ pos = buf + 1;
+ } else {
+ vlan_id = strtol(buf, &pos, 10);
+ if (buf == pos || vlan_id < 1 ||
+ vlan_id > MAX_VLAN_ID) {
+ printf("Invalid VLAN ID at line %d in '%s'\n",
+ line, fname);
+ fclose(f);
+ return -1;
+ }
+ }
+
+ while (*pos == ' ' || *pos == '\t')
+ pos++;
+ pos2 = pos;
+ while (*pos2 != ' ' && *pos2 != '\t' && *pos2 != '\0')
+ pos2++;
+ *pos2 = '\0';
+ if (*pos == '\0' || strlen(pos) > IFNAMSIZ) {
+ printf("Invalid VLAN ifname at line %d in '%s'\n",
+ line, fname);
+ fclose(f);
+ return -1;
+ }
+
+ vlan = malloc(sizeof(*vlan));
+ if (vlan == NULL) {
+ printf("Out of memory while reading VLAN interfaces "
+ "from '%s'\n", fname);
+ fclose(f);
+ return -1;
+ }
+
+ memset(vlan, 0, sizeof(*vlan));
+ vlan->vlan_id = vlan_id;
+ strncpy(vlan->ifname, pos, sizeof(vlan->ifname));
+ if (bss->vlan_tail)
+ bss->vlan_tail->next = vlan;
+ else
+ bss->vlan = vlan;
+ bss->vlan_tail = vlan;
+ }
+
+ fclose(f);
+
+ return 0;
+}
+
+
+static void hostapd_config_free_vlan(struct hostapd_bss_config *bss)
+{
+ struct hostapd_vlan *vlan, *prev;
+
+ vlan = bss->vlan;
+ prev = NULL;
+ while (vlan) {
+ prev = vlan;
+ vlan = vlan->next;
+ free(prev);
+ }
+
+ bss->vlan = NULL;
+}
+
+
/* convert floats with one decimal place to value*10 int, i.e.,
* "1.5" will return 15 */
static int hostapd_config_read_int10(const char *value)
@@ -1560,6 +1657,16 @@ struct hostapd_config * hostapd_config_read(const char *fname)
}
} else if (strcmp(buf, "dynamic_vlan") == 0) {
bss->ssid.dynamic_vlan = atoi(pos);
+ } else if (strcmp(buf, "vlan_file") == 0) {
+ if (hostapd_config_read_vlan_file(bss, pos)) {
+ printf("Line %d: failed to read VLAN file "
+ "'%s'\n", line, pos);
+ errors++;
+ }
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ } else if (strcmp(buf, "vlan_tagged_interface") == 0) {
+ bss->ssid.vlan_tagged_interface = strdup(pos);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
} else if (strcmp(buf, "passive_scan_interval") == 0) {
conf->passive_scan_interval = atoi(pos);
} else if (strcmp(buf, "passive_scan_listen") == 0) {
@@ -1660,6 +1767,16 @@ static void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
}
+static void hostapd_config_free_wep(struct hostapd_wep_keys *keys)
+{
+ int i;
+ for (i = 0; i < NUM_WEP_KEYS; i++) {
+ free(keys->key[i]);
+ keys->key[i] = NULL;
+ }
+}
+
+
static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
{
struct hostapd_wpa_psk *psk, *prev;
@@ -1677,6 +1794,9 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
free(conf->ssid.wpa_passphrase);
free(conf->ssid.wpa_psk_file);
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ free(conf->ssid.vlan_tagged_interface);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
user = conf->eap_user;
while (user) {
@@ -1704,6 +1824,19 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
free(conf->radius_server_clients);
free(conf->test_socket);
free(conf->radius);
+ hostapd_config_free_vlan(conf);
+ if (conf->ssid.dyn_vlan_keys) {
+ struct hostapd_ssid *ssid = &conf->ssid;
+ size_t i;
+ for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) {
+ if (ssid->dyn_vlan_keys[i] == NULL)
+ continue;
+ hostapd_config_free_wep(ssid->dyn_vlan_keys[i]);
+ free(ssid->dyn_vlan_keys[i]);
+ }
+ free(ssid->dyn_vlan_keys);
+ ssid->dyn_vlan_keys = NULL;
+ }
}
@@ -1761,6 +1894,18 @@ int hostapd_rate_found(int *list, int rate)
}
+const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id)
+{
+ struct hostapd_vlan *v = vlan;
+ while (v) {
+ if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD)
+ return v->ifname;
+ v = v->next;
+ }
+ return NULL;
+}
+
+
const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
const u8 *addr, const u8 *prev_psk)
{
diff --git a/hostapd/config.h b/hostapd/config.h
index 3fc6dfa..ac93609 100644
--- a/hostapd/config.h
+++ b/hostapd/config.h
@@ -58,9 +58,31 @@ struct hostapd_ssid {
#define DYNAMIC_VLAN_OPTIONAL 1
#define DYNAMIC_VLAN_REQUIRED 2
int dynamic_vlan;
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ char *vlan_tagged_interface;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+ struct hostapd_wep_keys **dyn_vlan_keys;
+ size_t max_dyn_vlan_keys;
};
+#define VLAN_ID_WILDCARD -1
+
+struct hostapd_vlan {
+ struct hostapd_vlan *next;
+ int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */
+ char ifname[IFNAMSIZ + 1];
+ int dynamic_vlan;
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+
+#define DVLAN_CLEAN_BR 0x1
+#define DVLAN_CLEAN_VLAN 0x2
+#define DVLAN_CLEAN_VLAN_PORT 0x4
+#define DVLAN_CLEAN_WLAN_PORT 0x8
+ int clean;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+};
+
#define PMK_LEN 32
struct hostapd_wpa_psk {
struct hostapd_wpa_psk *next;
@@ -233,6 +255,8 @@ struct hostapd_bss_config {
int wme_enabled;
+ struct hostapd_vlan *vlan, *vlan_tail;
+
macaddr bssid;
};
@@ -303,6 +327,8 @@ int hostapd_rate_found(int *list, int rate);
const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
const u8 *addr, const u8 *prev_psk);
int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf);
+const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan,
+ int vlan_id);
const struct hostapd_eap_user *
hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity,
size_t identity_len, int phase2);
diff --git a/hostapd/driver.h b/hostapd/driver.h
index c799218..81d78f0 100644
--- a/hostapd/driver.h
+++ b/hostapd/driver.h
@@ -1,6 +1,10 @@
#ifndef DRIVER_H
#define DRIVER_H
+enum hostapd_driver_if_type {
+ HOSTAPD_IF_VLAN, HOSTAPD_IF_WDS
+};
+
struct driver_ops {
const char *name; /* as appears in the config file */
@@ -106,6 +110,13 @@ struct driver_ops {
struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv,
u16 *num_modes,
u16 *flags);
+ int (*if_add)(const char *iface, void *priv,
+ enum hostapd_driver_if_type type, char *ifname,
+ const u8 *addr);
+ int (*if_update)(void *priv, enum hostapd_driver_if_type type,
+ char *ifname, const u8 *addr);
+ int (*if_remove)(void *priv, enum hostapd_driver_if_type type,
+ const char *ifname, const u8 *addr);
int (*set_sta_vlan)(void *priv, const u8 *addr, const char *ifname,
int vlan_id);
};
@@ -520,6 +531,34 @@ hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr,
}
static inline int
+hostapd_if_add(struct hostapd_data *hapd, enum hostapd_driver_if_type type,
+ char *ifname, const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->if_add == NULL)
+ return -1;
+ return hapd->driver->if_add(hapd->conf->iface, hapd->driver, type,
+ ifname, addr);
+}
+
+static inline int
+hostapd_if_update(struct hostapd_data *hapd, enum hostapd_driver_if_type type,
+ char *ifname, const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->if_update == NULL)
+ return -1;
+ return hapd->driver->if_update(hapd->driver, type, ifname, addr);
+}
+
+static inline int
+hostapd_if_remove(struct hostapd_data *hapd, enum hostapd_driver_if_type type,
+ char *ifname, const u8 *addr)
+{
+ if (hapd->driver == NULL || hapd->driver->if_remove == NULL)
+ return -1;
+ return hapd->driver->if_remove(hapd->driver, type, ifname, addr);
+}
+
+static inline int
hostapd_passive_scan(struct hostapd_data *hapd, int now, int our_mode_only,
int interval, int listen, int *channel,
int *last_rx)
diff --git a/hostapd/driver_devicescape.c b/hostapd/driver_devicescape.c
index 293c8c8..740b62c 100644
--- a/hostapd/driver_devicescape.c
+++ b/hostapd/driver_devicescape.c
@@ -997,6 +997,113 @@ static int i802_set_short_slot_time(void *priv, int value)
}
+static int i802_if_type(enum hostapd_driver_if_type type)
+{
+ switch (type) {
+ case HOSTAPD_IF_VLAN:
+ return HOSTAP_IF_VLAN;
+ case HOSTAPD_IF_WDS:
+ return HOSTAP_IF_WDS;
+ }
+ return -1;
+}
+
+
+static int i802_if_add(const char *iface, void *priv,
+ enum hostapd_driver_if_type type, char *ifname,
+ const u8 *addr)
+{
+ struct i802_driver_data *drv = priv;
+ struct prism2_hostapd_param *param;
+
+ param = malloc(sizeof(struct prism2_hostapd_param) + ETH_ALEN);
+ if (!param)
+ return -1;
+ memset(param, 0, sizeof(param));
+
+ param->cmd = PRISM2_HOSTAPD_ADD_IF;
+ param->u.if_info.type = i802_if_type(type);
+ if (addr)
+ memcpy(param->u.if_info.data, addr, ETH_ALEN);
+ else
+ memset(param->u.if_info.data, 0, ETH_ALEN);
+ snprintf((char *) param->u.if_info.name, IFNAMSIZ, "%s", ifname);
+
+ /* FIX: should the size have + ETH_ALEN ? */
+ if (hostapd_ioctl_iface(iface, drv, param,
+ sizeof(struct prism2_hostapd_param))) {
+ printf("Could not add iface: %s.\n", ifname);
+ free(param);
+ return -1;
+ }
+
+ snprintf(ifname, IFNAMSIZ, "%s", param->u.if_info.name);
+ free(param);
+ return 0;
+}
+
+
+static int i802_if_update(void *priv, enum hostapd_driver_if_type type,
+ char *ifname, const u8 *addr)
+{
+ struct i802_driver_data *drv = priv;
+ struct prism2_hostapd_param *param;
+
+ param = malloc(sizeof(struct prism2_hostapd_param) + ETH_ALEN);
+ if (!param)
+ return -1;
+ memset(param, 0, sizeof(param));
+
+ param->cmd = PRISM2_HOSTAPD_UPDATE_IF;
+ param->u.if_info.type = i802_if_type(type);
+ if (addr)
+ memcpy(param->u.if_info.data, addr, ETH_ALEN);
+ else
+ memset(param->u.if_info.data, 0, ETH_ALEN);
+ snprintf((char *) param->u.if_info.name, IFNAMSIZ, "%s", ifname);
+
+ /* FIX: should the size have + ETH_ALEN ? */
+ if (hostapd_ioctl(drv, param, sizeof(struct prism2_hostapd_param))) {
+ printf("Could not update iface: %s.\n", ifname);
+ free(param);
+ return -1;
+ }
+
+ snprintf(ifname, IFNAMSIZ, "%s", param->u.if_info.name);
+ free(param);
+ return 0;
+}
+
+
+static int i802_if_remove(void *priv, enum hostapd_driver_if_type type,
+ const char *ifname, const u8 *addr)
+{
+ struct i802_driver_data *drv = priv;
+ struct prism2_hostapd_param *param;
+
+ param = malloc(sizeof(struct prism2_hostapd_param) + ETH_ALEN);
+ if (!param)
+ return -1;
+ memset(param, 0, sizeof(param));
+
+ param->cmd = PRISM2_HOSTAPD_REMOVE_IF;
+ param->u.if_info.type = i802_if_type(type);
+ if (addr)
+ memcpy(param->u.if_info.data, addr, ETH_ALEN);
+ else
+ memset(param->u.if_info.data, 0, ETH_ALEN);
+ snprintf((char *) param->u.if_info.name, IFNAMSIZ, "%s", ifname);
+ if (hostapd_ioctl(drv, param, sizeof(struct prism2_hostapd_param))) {
+ printf("Could not remove iface: %s.\n", ifname);
+ free(param);
+ return -1;
+ }
+
+ free(param);
+ return 0;
+}
+
+
static struct hostapd_hw_modes * i802_get_hw_feature_data(void *priv,
u16 *num_modes,
u16 *flags)
@@ -1944,6 +2051,9 @@ static const struct driver_ops devicescape_driver_ops = {
.set_tx_queue_params = i802_set_tx_queue_params,
.bss_add = i802_bss_add,
.bss_remove = i802_bss_remove,
+ .if_add = i802_if_add,
+ .if_update = i802_if_update,
+ .if_remove = i802_if_remove,
.get_hw_feature_data = i802_get_hw_feature_data,
.set_sta_vlan = i802_set_sta_vlan,
};
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index d125e76..905dd4a 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -412,6 +412,32 @@ own_ip_addr=127.0.0.1
# 60 (1 minute).
#radius_acct_interim_interval=600
+# Dynamic VLAN mode; allow RADIUS authentication server to decide which VLAN
+# is used for the stations. This information is parsed from following RADIUS
+# attributes based on RFC 3580 and RFC 2868: Tunnel-Type (value 13 = VLAN),
+# Tunnel-Medium-Type (value 6 = IEEE 802), Tunnel-Private-Group-ID (value
+# VLANID as a string). vlan_file option below must be configured if dynamic
+# VLANs are used.
+# 0 = disabled (default)
+# 1 = option; use default interface if RADIUS server does not include VLAN ID
+# 2 = required; reject authentication if RADIUS server does not include VLAN ID
+#dynamic_vlan=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
+# multiple BSSIDs or SSIDs. Each line in this text file is defining a new
+# interface and the line must include VLAN ID and interface name separated by
+# white space (space or tab).
+#vlan_file=/etc/hostapd.vlan
+
+# Interface where 802.1q tagged packets should appear when a RADIUS server is
+# used to determine which VLAN a station is on. hostapd creates a bridge for
+# each VLAN. Then hostapd adds a VLAN interface (associated with the interface
+# indicated by 'vlan_tagged_interface') and the appropriate wireless interface
+# to the bridge.
+#vlan_tagged_interface=eth0
+
##### RADIUS authentication server configuration ##############################
diff --git a/hostapd/hostapd.h b/hostapd/hostapd.h
index 5640c82..ae0b5d2 100644
--- a/hostapd/hostapd.h
+++ b/hostapd/hostapd.h
@@ -47,6 +47,8 @@
#pragma pack(push, 1)
#endif /* _MSC_VER */
+#define MAX_VLAN_ID 4094
+
struct ieee8023_hdr {
u8 dest[6];
u8 src[6];
@@ -100,6 +102,10 @@ struct driver_ops;
struct wpa_ctrl_dst;
struct radius_server_data;
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+struct full_dynamic_vlan;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
/**
* struct hostapd_data - hostapd per-BSS data structure
*/
@@ -155,6 +161,10 @@ struct hostapd_data {
struct radius_server_data *radius_srv;
int parameter_set_count;
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ struct full_dynamic_vlan *full_dynamic_vlan;
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
};
diff --git a/hostapd/hostapd.vlan b/hostapd/hostapd.vlan
new file mode 100644
index 0000000..98254fa
--- /dev/null
+++ b/hostapd/hostapd.vlan
@@ -0,0 +1,9 @@
+# VLAN ID to network interface mapping
+1 vlan1
+2 vlan2
+3 vlan3
+100 guest
+# Optional wildcard entry matching all VLAN IDs. The first # in the interface
+# name will be replaced with the VLAN ID. The network interfaces are created
+# (and removed) dynamically based on the use.
+* vlan#
diff --git a/hostapd/ieee802_11.c b/hostapd/ieee802_11.c
index accc132..ad11765 100644
--- a/hostapd/ieee802_11.c
+++ b/hostapd/ieee802_11.c
@@ -636,6 +636,15 @@ static void handle_auth(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt,
}
if (vlan_id > 0) {
+ if (hostapd_get_vlan_id_ifname(hapd->conf->vlan,
+ sta->vlan_id) == NULL) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO, "Invalid VLAN ID "
+ "%d received from RADIUS server",
+ vlan_id);
+ resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto fail;
+ }
sta->vlan_id = vlan_id;
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
@@ -1408,6 +1417,17 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
"Could not add STA to kernel driver");
}
+ if (sta->eapol_sm == NULL) {
+ /*
+ * This STA does not use RADIUS server for EAP authentication,
+ * so bind it to the selected VLAN interface now, since the
+ * interface selection is not going to change anymore.
+ */
+ ap_sta_bind_vlan(hapd, sta, 0);
+ } else if (sta->vlan_id) {
+ /* VLAN ID already set (e.g., by PMKSA caching), so bind STA */
+ ap_sta_bind_vlan(hapd, sta, 0);
+ }
if (sta->flags & WLAN_STA_SHORT_PREAMBLE) {
hostapd_sta_set_flags(hapd, sta->addr,
WLAN_STA_SHORT_PREAMBLE, ~0);
diff --git a/hostapd/ieee802_1x.c b/hostapd/ieee802_1x.c
index 9c71b9d..d06bdec 100644
--- a/hostapd/ieee802_1x.c
+++ b/hostapd/ieee802_1x.c
@@ -1195,7 +1195,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
struct hostapd_data *hapd = data;
struct sta_info *sta;
u32 session_timeout = 0, termination_action, acct_interim_interval;
- int session_timeout_set;
+ int session_timeout_set, old_vlanid = 0;
int eap_timeout;
struct eapol_state_machine *sm;
int override_eapReq = 0;
@@ -1271,9 +1271,13 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
case RADIUS_CODE_ACCESS_ACCEPT:
if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED)
sta->vlan_id = 0;
- else
+ else {
+ old_vlanid = sta->vlan_id;
sta->vlan_id = radius_msg_get_vlanid(msg);
- if (sta->vlan_id > 0) {
+ }
+ if (sta->vlan_id > 0 &&
+ hostapd_get_vlan_id_ifname(hapd->conf->vlan,
+ sta->vlan_id)) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO,
@@ -1288,6 +1292,8 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
break;
}
+ ap_sta_bind_vlan(hapd, sta, old_vlanid);
+
/* RFC 3580, Ch. 3.17 */
if (session_timeout_set && termination_action ==
RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) {
diff --git a/hostapd/priv_netlink.h b/hostapd/priv_netlink.h
index c9810b4..d1f6f66 100644
--- a/hostapd/priv_netlink.h
+++ b/hostapd/priv_netlink.h
@@ -7,6 +7,9 @@
* library, etc..
*/
+#ifndef IFLA_IFNAME
+#define IFLA_IFNAME 3
+#endif
#ifndef IFLA_WIRELESS
#define IFLA_WIRELESS 11
#endif
@@ -15,6 +18,7 @@
#define RTMGRP_LINK 1
#define RTM_BASE 0x10
#define RTM_NEWLINK (RTM_BASE + 0)
+#define RTM_DELLINK (RTM_BASE + 1)
#define NLMSG_ALIGNTO 4
#define NLMSG_ALIGN(len) (((len) + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1))
diff --git a/hostapd/sta_info.c b/hostapd/sta_info.c
index b9ab1c7..1c0c5fb 100644
--- a/hostapd/sta_info.c
+++ b/hostapd/sta_info.c
@@ -35,6 +35,7 @@
#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4
#endif /* CONFIG_NATIVE_WINDOWS */
#include "mlme.h"
+#include "vlan_init.h"
int ap_for_each_sta(struct hostapd_data *hapd,
@@ -398,3 +399,104 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
return sta;
}
+
+
+int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
+ int old_vlanid)
+{
+ const char *iface;
+ struct hostapd_vlan *vlan = NULL;
+
+ /*
+ * Do not proceed furthur if the vlan id remains same. We do not want
+ * duplicate dynamic vlan entries.
+ */
+ if (sta->vlan_id == old_vlanid)
+ return 0;
+
+ /*
+ * During 1x reauth, if the vlan id changes, then remove the old id and
+ * proceed furthur to add the new one.
+ */
+ if (old_vlanid > 0)
+ vlan_remove_dynamic(hapd, old_vlanid);
+
+ iface = hapd->conf->iface;
+ if (sta->ssid->vlan[0])
+ iface = sta->ssid->vlan;
+
+ if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+ sta->vlan_id = 0;
+ else if (sta->vlan_id > 0) {
+ vlan = hapd->conf->vlan;
+ while (vlan) {
+ if (vlan->vlan_id == sta->vlan_id ||
+ vlan->vlan_id == VLAN_ID_WILDCARD) {
+ iface = vlan->ifname;
+ break;
+ }
+ vlan = vlan->next;
+ }
+ }
+
+ if (sta->vlan_id > 0 && vlan == NULL) {
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "could not find VLAN for "
+ "binding station to (vlan_id=%d)",
+ sta->vlan_id);
+ return -1;
+ } else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) {
+ vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id);
+ if (vlan == NULL) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "could not add "
+ "dynamic VLAN interface for vlan_id=%d",
+ sta->vlan_id);
+ return -1;
+ }
+
+ iface = vlan->ifname;
+ if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "could not "
+ "configure encryption for dynamic VLAN "
+ "interface for vlan_id=%d",
+ sta->vlan_id);
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN "
+ "interface '%s'", iface);
+ } else if (vlan && vlan->vlan_id == sta->vlan_id) {
+ if (sta->vlan_id > 0) {
+ vlan->dynamic_vlan++;
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "updated existing "
+ "dynamic VLAN interface '%s'", iface);
+ }
+
+ /*
+ * Update encryption configuration for statically generated
+ * VLAN interface. This is only used for static WEP
+ * configuration for the case where hostapd did not yet know
+ * which keys are to be used when the interface was added.
+ */
+ if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "could not "
+ "configure encryption for VLAN "
+ "interface for vlan_id=%d",
+ sta->vlan_id);
+ }
+ }
+
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG, "binding station to interface "
+ "'%s'", iface);
+
+ return hostapd_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id);
+}
diff --git a/hostapd/sta_info.h b/hostapd/sta_info.h
index 1774c6a..e092335 100644
--- a/hostapd/sta_info.h
+++ b/hostapd/sta_info.h
@@ -30,5 +30,7 @@ void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
void ap_sta_no_session_timeout(struct hostapd_data *hapd,
struct sta_info *sta);
struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr);
+int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
+ int old_vlanid);
#endif /* STA_INFO_H */
diff --git a/hostapd/vlan_init.c b/hostapd/vlan_init.c
new file mode 100644
index 0000000..d551ecf
--- /dev/null
+++ b/hostapd/vlan_init.c
@@ -0,0 +1,823 @@
+/*
+ * hostapd / VLAN initialization
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "hostapd.h"
+#include "driver.h"
+#include "vlan_init.h"
+
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <linux/sockios.h>
+#include <linux/if_vlan.h>
+typedef __uint64_t __u64;
+typedef __uint32_t __u32;
+typedef __int32_t __s32;
+typedef __uint16_t __u16;
+typedef __int16_t __s16;
+typedef __uint8_t __u8;
+#include <linux/if_bridge.h>
+
+#include "priv_netlink.h"
+#include "eloop.h"
+
+
+struct full_dynamic_vlan {
+ int s; /* socket on which to listen for new/removed interfaces. */
+};
+
+
+static int ifconfig_helper(const char *if_name, int up)
+{
+ int fd;
+ struct ifreq ifr;
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("socket[AF_INET,SOCK_STREAM]");
+ return -1;
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, if_name, IFNAMSIZ);
+
+ if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
+ perror("ioctl[SIOCGIFFLAGS]");
+ close(fd);
+ return -1;
+ }
+
+ if (up)
+ ifr.ifr_flags |= IFF_UP;
+ else
+ ifr.ifr_flags &= ~IFF_UP;
+
+ if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
+ perror("ioctl[SIOCSIFFLAGS]");
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+static int ifconfig_up(const char *if_name)
+{
+ return ifconfig_helper(if_name, 1);
+}
+
+
+static int ifconfig_down(const char *if_name)
+{
+ return ifconfig_helper(if_name, 0);
+}
+
+
+/*
+ * These are only available in recent linux headers (without the leading
+ * underscore).
+ */
+#define _GET_VLAN_REALDEV_NAME_CMD 8
+#define _GET_VLAN_VID_CMD 9
+
+/* This value should be 256 ONLY. If it is something else, then hostapd
+ * might crash!, as this value has been hard-coded in 2.4.x kernel
+ * bridging code.
+ */
+#define MAX_BR_PORTS 256
+
+static int br_delif(const char *br_name, const char *if_name)
+{
+ int fd;
+ struct ifreq ifr;
+ unsigned long args[2];
+ int if_index;
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("socket[AF_INET,SOCK_STREAM]");
+ return -1;
+ }
+
+ if_index = if_nametoindex(if_name);
+
+ if (if_index == 0) {
+ printf("Failure determining interface index for '%s'\n",
+ if_name);
+ close(fd);
+ return -1;
+ }
+
+ args[0] = BRCTL_DEL_IF;
+ args[1] = if_index;
+
+ strncpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (__caddr_t) args;
+
+ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) {
+ /* No error if interface already removed. */
+ perror("ioctl[SIOCDEVPRIVATE,BRCTL_DEL_IF]");
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+/*
+ Add interface 'if_name' to the bridge 'br_name'
+
+ returns -1 on error
+ returns 1 if the interface is already part of the bridge
+ returns 0 otherwise
+*/
+static int br_addif(const char *br_name, const char *if_name)
+{
+ int fd;
+ struct ifreq ifr;
+ unsigned long args[2];
+ int if_index;
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("socket[AF_INET,SOCK_STREAM]");
+ return -1;
+ }
+
+ if_index = if_nametoindex(if_name);
+
+ if (if_index == 0) {
+ printf("Failure determining interface index for '%s'\n",
+ if_name);
+ close(fd);
+ return -1;
+ }
+
+ args[0] = BRCTL_ADD_IF;
+ args[1] = if_index;
+
+ strncpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (__caddr_t) args;
+
+ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+ if (errno == EBUSY) {
+ /* The interface is already added. */
+ close(fd);
+ return 1;
+ }
+
+ perror("ioctl[SIOCDEVPRIVATE,BRCTL_ADD_IF]");
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+static int br_delbr(const char *br_name)
+{
+ int fd;
+ unsigned long arg[2];
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("socket[AF_INET,SOCK_STREAM]");
+ return -1;
+ }
+
+ arg[0] = BRCTL_DEL_BRIDGE;
+ arg[1] = (unsigned long) br_name;
+
+ if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) {
+ /* No error if bridge already removed. */
+ perror("ioctl[BRCTL_DEL_BRIDGE]");
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+/*
+ Add a bridge with the name 'br_name'.
+
+ returns -1 on error
+ returns 1 if the bridge already exists
+ returns 0 otherwise
+*/
+static int br_addbr(const char *br_name)
+{
+ int fd;
+ unsigned long arg[2];
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("socket[AF_INET,SOCK_STREAM]");
+ return -1;
+ }
+
+ arg[0] = BRCTL_ADD_BRIDGE;
+ arg[1] = (unsigned long) br_name;
+
+ if (ioctl(fd, SIOCGIFBR, arg) < 0) {
+ if (errno == EEXIST) {
+ /* The bridge is already added. */
+ close(fd);
+ return 1;
+ } else {
+ perror("ioctl[BRCTL_ADD_BRIDGE]");
+ close(fd);
+ return -1;
+ }
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+static int br_getnumports(const char *br_name)
+{
+ int fd;
+ int i;
+ int port_cnt = 0;
+ unsigned long arg[4];
+ int ifindices[MAX_BR_PORTS];
+ struct ifreq ifr;
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("socket[AF_INET,SOCK_STREAM]");
+ return -1;
+ }
+
+ arg[0] = BRCTL_GET_PORT_LIST;
+ arg[1] = (unsigned long) ifindices;
+ arg[2] = MAX_BR_PORTS;
+ arg[3] = 0;
+
+ memset(ifindices, 0, sizeof(ifindices));
+ strncpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (__caddr_t) arg;
+
+ if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+ perror("ioctl[SIOCDEVPRIVATE,BRCTL_GET_PORT_LIST]");
+ close(fd);
+ return -1;
+ }
+
+ for (i = 1; i < MAX_BR_PORTS; i++) {
+ if (ifindices[i] > 0) {
+ port_cnt++;
+ }
+ }
+
+ close(fd);
+ return port_cnt;
+}
+
+
+static int vlan_rem(const char *if_name)
+{
+ int fd;
+ struct vlan_ioctl_args if_request;
+
+ if ((strlen(if_name) + 1) > sizeof(if_request.device1)) {
+ fprintf(stderr, "Interface name to long.\n");
+ return -1;
+ }
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("socket[AF_INET,SOCK_STREAM]");
+ return -1;
+ }
+
+ memset(&if_request, 0, sizeof(if_request));
+
+ strcpy(if_request.device1, if_name);
+ if_request.cmd = DEL_VLAN_CMD;
+
+ if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+ perror("ioctl[SIOCSIFVLAN,DEL_VLAN_CMD]");
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+/*
+ Add a vlan interface with VLAN ID 'vid' and tagged interface
+ 'if_name'.
+
+ returns -1 on error
+ returns 1 if the interface already exists
+ returns 0 otherwise
+*/
+static int vlan_add(const char *if_name, int vid)
+{
+ int fd;
+ struct vlan_ioctl_args if_request;
+
+ ifconfig_up(if_name);
+
+ if ((strlen(if_name) + 1) > sizeof(if_request.device1)) {
+ fprintf(stderr, "Interface name to long.\n");
+ return -1;
+ }
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("socket[AF_INET,SOCK_STREAM]");
+ return -1;
+ }
+
+ memset(&if_request, 0, sizeof(if_request));
+
+ /* Determine if a suitable vlan device already exists. */
+
+ snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d",
+ vid);
+
+ if_request.cmd = _GET_VLAN_VID_CMD;
+
+ if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0) {
+
+ if (if_request.u.VID == vid) {
+ if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD;
+
+ if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0
+ && strncmp(if_request.u.device2, if_name,
+ sizeof(if_request.u.device2)) == 0) {
+ close(fd);
+ return 1;
+ }
+ }
+ }
+
+ /* A suitable vlan device does not already exist, add one. */
+
+ memset(&if_request, 0, sizeof(if_request));
+ strcpy(if_request.device1, if_name);
+ if_request.u.VID = vid;
+ if_request.cmd = ADD_VLAN_CMD;
+
+ if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+ perror("ioctl[SIOCSIFVLAN,ADD_VLAN_CMD]");
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+static int vlan_set_name_type(unsigned int name_type)
+{
+ int fd;
+ struct vlan_ioctl_args if_request;
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ perror("socket[AF_INET,SOCK_STREAM]");
+ return -1;
+ }
+
+ memset(&if_request, 0, sizeof(if_request));
+
+ if_request.u.name_type = name_type;
+ if_request.cmd = SET_VLAN_NAME_TYPE_CMD;
+ if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) {
+ perror("ioctl[SIOCSIFVLAN,SET_VLAN_NAME_TYPE_CMD]");
+ close(fd);
+ return -1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+
+static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
+{
+ char vlan_ifname[IFNAMSIZ];
+ char br_name[IFNAMSIZ];
+ struct hostapd_vlan *vlan = hapd->conf->vlan;
+ char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+
+ while (vlan) {
+ if (strcmp(ifname, vlan->ifname) == 0) {
+
+ snprintf(br_name, sizeof(br_name), "brvlan%d",
+ vlan->vlan_id);
+
+ if (!br_addbr(br_name))
+ vlan->clean |= DVLAN_CLEAN_BR;
+
+ ifconfig_up(br_name);
+
+ if (tagged_interface) {
+
+ if (!vlan_add(tagged_interface, vlan->vlan_id))
+ vlan->clean |= DVLAN_CLEAN_VLAN;
+
+ snprintf(vlan_ifname, sizeof(vlan_ifname),
+ "vlan%d", vlan->vlan_id);
+
+ if (!br_addif(br_name, vlan_ifname))
+ vlan->clean |= DVLAN_CLEAN_VLAN_PORT;
+
+ ifconfig_up(vlan_ifname);
+ }
+
+ if (!br_addif(br_name, ifname))
+ vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
+
+ ifconfig_up(ifname);
+
+ break;
+ }
+ vlan = vlan->next;
+ }
+}
+
+
+static void vlan_dellink(char *ifname, struct hostapd_data *hapd)
+{
+ char vlan_ifname[IFNAMSIZ];
+ char br_name[IFNAMSIZ];
+ struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan;
+ char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
+ int numports;
+
+ first = prev = vlan;
+
+ while (vlan) {
+ if (strcmp(ifname, vlan->ifname) == 0) {
+ snprintf(br_name, sizeof(br_name), "brvlan%d",
+ vlan->vlan_id);
+
+ if (tagged_interface) {
+ snprintf(vlan_ifname, sizeof(vlan_ifname),
+ "vlan%d", vlan->vlan_id);
+
+ numports = br_getnumports(br_name);
+ if (numports == 1) {
+ br_delif(br_name, vlan_ifname);
+
+ vlan_rem(vlan_ifname);
+
+ ifconfig_down(br_name);
+ br_delbr(br_name);
+ }
+ }
+
+ if (vlan == first) {
+ hapd->conf->vlan = vlan->next;
+ } else {
+ prev->next = vlan->next;
+ }
+ free(vlan);
+
+ break;
+ }
+ prev = vlan;
+ vlan = vlan->next;
+ }
+}
+
+
+static void
+vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
+ struct hostapd_data *hapd)
+{
+ struct ifinfomsg *ifi;
+ int attrlen, nlmsg_len, rta_len;
+ struct rtattr *attr;
+
+ if (len < sizeof(*ifi))
+ return;
+
+ ifi = NLMSG_DATA(h);
+
+ nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
+
+ attrlen = h->nlmsg_len - nlmsg_len;
+ if (attrlen < 0)
+ return;
+
+ attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
+
+ rta_len = RTA_ALIGN(sizeof(struct rtattr));
+ while (RTA_OK(attr, attrlen)) {
+ char ifname[IFNAMSIZ + 1];
+
+ if (attr->rta_type == IFLA_IFNAME) {
+ int n = attr->rta_len - rta_len;
+ if (n < 0)
+ break;
+
+ memset(ifname, 0, sizeof(ifname));
+
+ if ((size_t) n > sizeof(ifname))
+ n = sizeof(ifname);
+ memcpy(ifname, ((char *) attr) + rta_len, n);
+
+ if (del)
+ vlan_dellink(ifname, hapd);
+ else
+ vlan_newlink(ifname, hapd);
+ }
+
+ attr = RTA_NEXT(attr, attrlen);
+ }
+}
+
+
+static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ char buf[8192];
+ int left;
+ struct sockaddr_nl from;
+ socklen_t fromlen;
+ struct nlmsghdr *h;
+ struct hostapd_data *hapd = eloop_ctx;
+
+ fromlen = sizeof(from);
+ left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
+ (struct sockaddr *) &from, &fromlen);
+ if (left < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ perror("recvfrom(netlink)");
+ return;
+ }
+
+ h = (struct nlmsghdr *) buf;
+ while (left >= (int) sizeof(*h)) {
+ int len, plen;
+
+ len = h->nlmsg_len;
+ plen = len - sizeof(*h);
+ if (len > left || plen < 0) {
+ printf("Malformed netlink message: "
+ "len=%d left=%d plen=%d", len, left, plen);
+ break;
+ }
+
+ switch (h->nlmsg_type) {
+ case RTM_NEWLINK:
+ vlan_read_ifnames(h, plen, 0, hapd);
+ break;
+ case RTM_DELLINK:
+ vlan_read_ifnames(h, plen, 1, hapd);
+ break;
+ }
+
+ len = NLMSG_ALIGN(len);
+ left -= len;
+ h = (struct nlmsghdr *) ((char *) h + len);
+ }
+
+ if (left > 0) {
+ printf("%d extra bytes in the end of netlink message",
+ left);
+ }
+}
+
+
+static struct full_dynamic_vlan *
+full_dynamic_vlan_init(struct hostapd_data *hapd)
+{
+ struct sockaddr_nl local;
+ struct full_dynamic_vlan *priv;
+
+ priv = malloc(sizeof(*priv));
+
+ if (priv == NULL)
+ return NULL;
+
+ memset(priv, 0, sizeof(*priv));
+
+ vlan_set_name_type(VLAN_NAME_TYPE_PLUS_VID_NO_PAD);
+
+ priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (priv->s < 0) {
+ perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)");
+ free(priv);
+ return NULL;
+ }
+
+ memset(&local, 0, sizeof(local));
+ local.nl_family = AF_NETLINK;
+ local.nl_groups = RTMGRP_LINK;
+ if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) {
+ perror("bind(netlink)");
+ close(priv->s);
+ free(priv);
+ return NULL;
+ }
+
+ if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, 0)) {
+ close(priv->s);
+ free(priv);
+ return NULL;
+ }
+
+ return priv;
+}
+
+
+static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv)
+{
+ if (priv == NULL)
+ return;
+ eloop_unregister_read_sock(priv->s);
+ close(priv->s);
+ free(priv);
+}
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+
+int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
+ struct hostapd_ssid *mssid, const char *dyn_vlan)
+{
+ int i;
+
+ if (dyn_vlan == NULL)
+ return 0;
+
+ /* Static WEP keys are set here; IEEE 802.1X and WPA uses their own
+ * functions for setting up dynamic broadcast keys. */
+ for (i = 0; i < 4; i++) {
+ if (mssid->wep.key[i] &&
+ hostapd_set_encryption(dyn_vlan, hapd, "WEP", NULL,
+ i, mssid->wep.key[i],
+ mssid->wep.len[i],
+ i == mssid->wep.idx)) {
+ printf("VLAN: Could not set WEP encryption for "
+ "dynamic VLAN.\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int vlan_dynamic_add(struct hostapd_data *hapd,
+ struct hostapd_vlan *vlan)
+{
+ while (vlan) {
+ if (vlan->vlan_id != VLAN_ID_WILDCARD &&
+ hostapd_if_add(hapd, HOSTAPD_IF_VLAN, vlan->ifname, NULL))
+ {
+ if (errno != EEXIST) {
+ printf("Could not add VLAN iface: %s: %s\n",
+ vlan->ifname, strerror(errno));
+ return -1;
+ }
+ }
+
+ vlan = vlan->next;
+ }
+
+ return 0;
+}
+
+
+static void vlan_dynamic_remove(struct hostapd_data *hapd,
+ struct hostapd_vlan *vlan)
+{
+ struct hostapd_vlan *next;
+
+ while (vlan) {
+ next = vlan->next;
+
+ if (vlan->vlan_id != VLAN_ID_WILDCARD &&
+ hostapd_if_remove(hapd, HOSTAPD_IF_VLAN, vlan->ifname,
+ NULL)) {
+ printf("Could not remove VLAN iface: %s: %s\n",
+ vlan->ifname, strerror(errno));
+ }
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ if (vlan->clean)
+ vlan_dellink(vlan->ifname, hapd);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+ vlan = next;
+ }
+}
+
+
+int vlan_init(struct hostapd_data *hapd)
+{
+ if (vlan_dynamic_add(hapd, hapd->conf->vlan))
+ return -1;
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+ return 0;
+}
+
+
+void vlan_deinit(struct hostapd_data *hapd)
+{
+ vlan_dynamic_remove(hapd, hapd->conf->vlan);
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+ full_dynamic_vlan_deinit(hapd->full_dynamic_vlan);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+}
+
+
+struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
+ struct hostapd_vlan *vlan,
+ int vlan_id)
+{
+ struct hostapd_vlan *n;
+ char *ifname, *pos;
+
+ if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID ||
+ vlan->vlan_id != VLAN_ID_WILDCARD)
+ return NULL;
+
+ ifname = strdup(vlan->ifname);
+ if (ifname == NULL)
+ return NULL;
+ pos = strchr(ifname, '#');
+ if (pos == NULL) {
+ free(ifname);
+ return NULL;
+ }
+ *pos++ = '\0';
+
+ n = malloc(sizeof(*n));
+ if (n == NULL) {
+ free(ifname);
+ return NULL;
+ }
+
+ memset(n, 0, sizeof(*n));
+ n->vlan_id = vlan_id;
+ n->dynamic_vlan = 1;
+
+ snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id, pos);
+ free(ifname);
+
+ if (hostapd_if_add(hapd, HOSTAPD_IF_VLAN, n->ifname, NULL)) {
+ free(n);
+ return NULL;
+ }
+
+ n->next = hapd->conf->vlan;
+ hapd->conf->vlan = n;
+
+ return n;
+}
+
+
+int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
+{
+ struct hostapd_vlan *vlan;
+
+ if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID)
+ return 1;
+
+ vlan = hapd->conf->vlan;
+ while (vlan) {
+ if (vlan->vlan_id == vlan_id && vlan->dynamic_vlan > 0) {
+ vlan->dynamic_vlan--;
+ break;
+ }
+ vlan = vlan->next;
+ }
+
+ if (vlan == NULL)
+ return 1;
+
+ if (vlan->dynamic_vlan == 0)
+ hostapd_if_remove(hapd, HOSTAPD_IF_VLAN, vlan->ifname, NULL);
+
+ return 0;
+}
diff --git a/hostapd/vlan_init.h b/hostapd/vlan_init.h
new file mode 100644
index 0000000..81e8df9
--- /dev/null
+++ b/hostapd/vlan_init.h
@@ -0,0 +1,29 @@
+/*
+ * hostapd / VLAN initialization
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005, Devicescape Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef VLAN_INIT_H
+#define VLAN_INIT_H
+
+int vlan_init(struct hostapd_data *hapd);
+void vlan_deinit(struct hostapd_data *hapd);
+struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
+ struct hostapd_vlan *vlan,
+ int vlan_id);
+int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id);
+int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
+ struct hostapd_ssid *mssid,
+ const char *dyn_vlan);
+
+#endif /* VLAN_INIT_H */