aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 */