aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2009-12-24 09:46:22 (GMT)
committerJouni Malinen <j@w1.fi>2009-12-24 09:46:22 (GMT)
commitfbbfcbac29c0b3d2c6702e2e1123e93087747386 (patch)
tree6f3ebcf3426a194203a78e72695d4cadc716e30b
parent09eac1ac56257e8c0251d1164e28fac7fd7c404f (diff)
downloadhostap-fbbfcbac29c0b3d2c6702e2e1123e93087747386.zip
hostap-fbbfcbac29c0b3d2c6702e2e1123e93087747386.tar.gz
hostap-fbbfcbac29c0b3d2c6702e2e1123e93087747386.tar.bz2
hostapd: Add WDS (4-address frame) mode with per-station interfaces
This mode allows associated stations to use 4-address frames to allow layer 2 bridging to be used. At least for the time being, this is only supported with driver=nl80211.
-rw-r--r--hostapd/config.c2
-rw-r--r--hostapd/config.h1
-rw-r--r--hostapd/driver_i.h9
-rw-r--r--hostapd/drv_callbacks.c6
-rw-r--r--hostapd/dump_state.c3
-rw-r--r--hostapd/hostapd.conf6
-rw-r--r--hostapd/ieee802_11.c13
-rw-r--r--hostapd/ieee802_11.h3
-rw-r--r--hostapd/sta_info.c1
-rw-r--r--hostapd/sta_info.h1
-rw-r--r--src/drivers/driver.h10
-rw-r--r--src/drivers/driver_ndis.c3
-rw-r--r--src/drivers/driver_nl80211.c51
-rw-r--r--wpa_supplicant/ap.c5
14 files changed, 96 insertions, 18 deletions
diff --git a/hostapd/config.c b/hostapd/config.c
index 1e89ad3..cb8f210 100644
--- a/hostapd/config.c
+++ b/hostapd/config.c
@@ -1534,6 +1534,8 @@ struct hostapd_config * hostapd_config_read(const char *fname)
line, pos);
errors++;
}
+ } else if (os_strcmp(buf, "wds_sta") == 0) {
+ bss->wds_sta = atoi(pos);
} else if (os_strcmp(buf, "ap_max_inactivity") == 0) {
bss->ap_max_inactivity = atoi(pos);
} else if (os_strcmp(buf, "country_code") == 0) {
diff --git a/hostapd/config.h b/hostapd/config.h
index 0201a1d..467c382 100644
--- a/hostapd/config.h
+++ b/hostapd/config.h
@@ -194,6 +194,7 @@ struct hostapd_bss_config {
int num_accept_mac;
struct mac_acl_entry *deny_mac;
int num_deny_mac;
+ int wds_sta;
int auth_algs; /* bitfield of allowed IEEE 802.11 authentication
* algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */
diff --git a/hostapd/driver_i.h b/hostapd/driver_i.h
index e5c85a8..b73630c 100644
--- a/hostapd/driver_i.h
+++ b/hostapd/driver_i.h
@@ -400,6 +400,15 @@ hostapd_set_sta_vlan(const char *ifname, struct hostapd_data *hapd,
}
static inline int
+hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid,
+ int val)
+{
+ if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
+ return 0;
+ return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val);
+}
+
+static inline int
hostapd_driver_commit(struct hostapd_data *hapd)
{
if (hapd->driver == NULL || hapd->driver->commit == NULL)
diff --git a/hostapd/drv_callbacks.c b/hostapd/drv_callbacks.c
index e1a7e4c..bb6e5f9 100644
--- a/hostapd/drv_callbacks.c
+++ b/hostapd/drv_callbacks.c
@@ -299,6 +299,7 @@ static const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
if (len < 24)
return NULL;
switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
+ case WLAN_FC_FROMDS | WLAN_FC_TODS:
case WLAN_FC_TODS:
return hdr->addr1;
case WLAN_FC_FROMDS:
@@ -344,11 +345,14 @@ static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd,
const struct ieee80211_hdr *hdr,
size_t len)
{
+ u16 fc = le_to_host16(hdr->frame_control);
hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
if (hapd == NULL || hapd == HAPD_BROADCAST)
return;
- ieee802_11_rx_from_unknown(hapd, hdr->addr2);
+ ieee802_11_rx_from_unknown(hapd, hdr->addr2,
+ (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+ (WLAN_FC_TODS | WLAN_FC_FROMDS));
}
diff --git a/hostapd/dump_state.c b/hostapd/dump_state.c
index 408ef1e..0a191dc 100644
--- a/hostapd/dump_state.c
+++ b/hostapd/dump_state.c
@@ -106,7 +106,7 @@ static void hostapd_dump_state(struct hostapd_data *hapd)
fprintf(f, "\nSTA=" MACSTR "\n", MAC2STR(sta->addr));
fprintf(f,
- " AID=%d flags=0x%x %s%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
+ " AID=%d flags=0x%x %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
" capability=0x%x listen_interval=%d\n",
sta->aid,
sta->flags,
@@ -126,6 +126,7 @@ static void hostapd_dump_state(struct hostapd_data *hapd)
(sta->flags & WLAN_STA_MFP ? "[MFP]" : ""),
(sta->flags & WLAN_STA_WPS ? "[WPS]" : ""),
(sta->flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""),
+ (sta->flags & WLAN_STA_WDS ? "[WDS]" : ""),
(sta->flags & WLAN_STA_NONERP ? "[NonERP]" : ""),
sta->capability,
sta->listen_interval);
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 6254496..2521b74 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -346,6 +346,12 @@ wmm_ac_vo_acm=0
# remain asleep). Default: 65535 (no limit apart from field size)
#max_listen_interval=100
+# WDS (4-address frame) mode with per-station virtual interfaces
+# (only supported with driver=nl80211)
+# This mode allows associated stations to use 4-address frames to allow layer 2
+# bridging to be used.
+#wds_sta=1
+
##### IEEE 802.11n related configuration ######################################
# ieee80211n: Whether IEEE 802.11n (HT) is enabled
diff --git a/hostapd/ieee802_11.c b/hostapd/ieee802_11.c
index 8d4fe39..df18a93 100644
--- a/hostapd/ieee802_11.c
+++ b/hostapd/ieee802_11.c
@@ -1689,13 +1689,22 @@ void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
}
-void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src)
+void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+ int wds)
{
struct sta_info *sta;
sta = ap_get_sta(hapd, src);
- if (sta && (sta->flags & WLAN_STA_ASSOC))
+ if (sta && (sta->flags & WLAN_STA_ASSOC)) {
+ if (wds && !(sta->flags & WLAN_STA_WDS)) {
+ wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for "
+ "STA " MACSTR " (aid %u)",
+ MAC2STR(sta->addr), sta->aid);
+ sta->flags |= WLAN_STA_WDS;
+ hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1);
+ }
return;
+ }
wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
MACSTR, MAC2STR(src));
diff --git a/hostapd/ieee802_11.h b/hostapd/ieee802_11.h
index 95c99b2..3347256 100644
--- a/hostapd/ieee802_11.h
+++ b/hostapd/ieee802_11.h
@@ -65,6 +65,7 @@ u16 copy_sta_ht_capab(struct sta_info *sta, const u8 *ht_capab,
void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
const u8 *buf, size_t len, int ack);
-void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src);
+void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+ int wds);
#endif /* IEEE802_11_H */
diff --git a/hostapd/sta_info.c b/hostapd/sta_info.c
index 2a2d7ba..4d9ba42 100644
--- a/hostapd/sta_info.c
+++ b/hostapd/sta_info.c
@@ -120,6 +120,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
accounting_sta_stop(hapd, sta);
+ hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 0);
if (!ap_sta_in_other_bss(hapd, sta, WLAN_STA_ASSOC) &&
!(sta->flags & WLAN_STA_PREAUTH))
hostapd_sta_remove(hapd, sta->addr);
diff --git a/hostapd/sta_info.h b/hostapd/sta_info.h
index 6a98387..f15a1b0 100644
--- a/hostapd/sta_info.h
+++ b/hostapd/sta_info.h
@@ -30,6 +30,7 @@
#define WLAN_STA_HT BIT(11)
#define WLAN_STA_WPS BIT(12)
#define WLAN_STA_MAYBE_WPS BIT(13)
+#define WLAN_STA_WDS BIT(14)
#define WLAN_STA_NONERP BIT(31)
/* Maximum number of supported rates (from both Supported Rates and Extended
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 7fa4ed2..aa11259 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1527,6 +1527,16 @@ struct wpa_driver_ops {
* Returns: 0 on success, -1 on failure
*/
int (*set_supp_port)(void *priv, int authorized);
+
+ /**
+ * set_wds_sta - Bind a station into a 4-address WDS (AP only)
+ * @priv: Private driver interface data
+ * @addr: MAC address of the associated station
+ * @aid: Association ID
+ * @val: 1 = bind to 4-address WDS; 0 = unbind
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val);
};
/**
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
index bec1e8d..034be95 100644
--- a/src/drivers/driver_ndis.c
+++ b/src/drivers/driver_ndis.c
@@ -3244,5 +3244,6 @@ const struct wpa_driver_ops wpa_driver_ndis_ops = {
NULL /* set_ht_params */,
NULL /* set_wps_beacon_ie */,
NULL /* set_wps_probe_resp_ie */,
- NULL /* set_supp_port */
+ NULL /* set_supp_port */,
+ NULL /* set_wds_sta */
};
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 697fefd..67043a6 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -128,6 +128,9 @@ static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
static struct i802_bss * get_bss(struct wpa_driver_nl80211_data *drv,
int ifindex);
static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
+static int wpa_driver_nl80211_if_remove(void *priv,
+ enum wpa_driver_if_type type,
+ const char *ifname);
#endif /* HOSTAPD */
@@ -2553,7 +2556,7 @@ static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
const char *ifname,
enum nl80211_iftype iftype,
- const u8 *addr)
+ const u8 *addr, int wds)
{
struct nl_msg *msg, *flags = NULL;
int ifidx;
@@ -2584,6 +2587,8 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
if (err)
goto nla_put_failure;
+ } else if (wds) {
+ NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, wds);
}
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
@@ -2616,11 +2621,11 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
const char *ifname, enum nl80211_iftype iftype,
- const u8 *addr)
+ const u8 *addr, int wds)
{
int ret;
- ret = nl80211_create_iface_once(drv, ifname, iftype, addr);
+ ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds);
/* if error occured and interface exists already */
if (ret == -ENFILE && if_nametoindex(ifname)) {
@@ -2630,7 +2635,8 @@ static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
nl80211_remove_iface(drv, if_nametoindex(ifname));
/* Try to create the interface again */
- ret = nl80211_create_iface_once(drv, ifname, iftype, addr);
+ ret = nl80211_create_iface_once(drv, ifname, iftype, addr,
+ wds);
}
return ret;
@@ -2817,7 +2823,7 @@ static struct sock_filter msock_filter_insns[] = {
#if 0
/*
- * drop non-data frames, WDS frames
+ * drop non-data frames
*/
/* load the lower byte of the frame control field */
BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0),
@@ -2825,13 +2831,13 @@ static struct sock_filter msock_filter_insns[] = {
BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0c),
/* drop non-data frames */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 8, 0, FAIL),
+#endif
/* load the upper byte of the frame control field */
- BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0),
+ BPF_STMT(BPF_LD | BPF_B | BPF_IND, 1),
/* mask off toDS/fromDS */
BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x03),
- /* drop WDS frames */
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, FAIL, 0),
-#endif
+ /* accept WDS frames */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, PASS, 0),
/*
* add header length to index
@@ -2937,7 +2943,8 @@ nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
buf[IFNAMSIZ - 1] = '\0';
drv->monitor_ifidx =
- nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL);
+ nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
+ 0);
if (drv->monitor_ifidx < 0)
return -1;
@@ -4065,6 +4072,26 @@ static int i802_set_sta_vlan(void *priv, const u8 *addr,
}
+static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val)
+{
+ struct wpa_driver_nl80211_data *drv = priv;
+ char name[16];
+
+ os_snprintf(name, sizeof(name), "%s.sta%d", drv->ifname, aid);
+ if (val) {
+ if (nl80211_create_iface(priv, name, NL80211_IFTYPE_AP_VLAN,
+ NULL, 1) < 0)
+ return -1;
+ hostapd_set_iface_flags(drv, name, 1);
+ return i802_set_sta_vlan(priv, addr, name, 0);
+ } else {
+ i802_set_sta_vlan(priv, addr, drv->ifname, 0);
+ return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN,
+ name);
+ }
+}
+
+
static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
{
struct wpa_driver_nl80211_data *drv = eloop_ctx;
@@ -4262,7 +4289,8 @@ static int wpa_driver_nl80211_if_add(const char *iface, void *priv,
#endif /* HOSTAPD */
ifidx = nl80211_create_iface(drv, ifname,
- wpa_driver_nl80211_if_type(type), addr);
+ wpa_driver_nl80211_if_type(type), addr,
+ 0);
if (ifidx < 0) {
#ifdef HOSTAPD
os_free(bss);
@@ -4363,5 +4391,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.set_short_slot_time = i802_set_short_slot_time,
.set_tx_queue_params = i802_set_tx_queue_params,
.set_sta_vlan = i802_set_sta_vlan,
+ .set_wds_sta = i802_set_wds_sta,
#endif /* HOSTAPD */
};
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 2e52adf..1cbcde0 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -507,7 +507,10 @@ void ap_rx_from_unknown_sta(void *ctx, const struct ieee80211_hdr *hdr,
{
#ifdef NEED_AP_MLME
struct wpa_supplicant *wpa_s = ctx;
- ieee802_11_rx_from_unknown(wpa_s->ap_iface->bss[0], hdr->addr2);
+ u16 fc = le_to_host16(hdr->frame_control);
+ ieee802_11_rx_from_unknown(wpa_s->ap_iface->bss[0], hdr->addr2,
+ (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+ (WLAN_FC_TODS | WLAN_FC_FROMDS));
#endif /* NEED_AP_MLME */
}