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.
This commit is contained in:
Felix Fietkau 2009-12-24 11:46:22 +02:00 committed by Jouni Malinen
parent 09eac1ac56
commit fbbfcbac29
14 changed files with 96 additions and 18 deletions

View file

@ -1534,6 +1534,8 @@ struct hostapd_config * hostapd_config_read(const char *fname)
line, pos); line, pos);
errors++; errors++;
} }
} else if (os_strcmp(buf, "wds_sta") == 0) {
bss->wds_sta = atoi(pos);
} else if (os_strcmp(buf, "ap_max_inactivity") == 0) { } else if (os_strcmp(buf, "ap_max_inactivity") == 0) {
bss->ap_max_inactivity = atoi(pos); bss->ap_max_inactivity = atoi(pos);
} else if (os_strcmp(buf, "country_code") == 0) { } else if (os_strcmp(buf, "country_code") == 0) {

View file

@ -194,6 +194,7 @@ struct hostapd_bss_config {
int num_accept_mac; int num_accept_mac;
struct mac_acl_entry *deny_mac; struct mac_acl_entry *deny_mac;
int num_deny_mac; int num_deny_mac;
int wds_sta;
int auth_algs; /* bitfield of allowed IEEE 802.11 authentication int auth_algs; /* bitfield of allowed IEEE 802.11 authentication
* algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */ * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */

View file

@ -399,6 +399,15 @@ hostapd_set_sta_vlan(const char *ifname, struct hostapd_data *hapd,
return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname, vlan_id); return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname, vlan_id);
} }
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 static inline int
hostapd_driver_commit(struct hostapd_data *hapd) hostapd_driver_commit(struct hostapd_data *hapd)
{ {

View file

@ -299,6 +299,7 @@ static const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
if (len < 24) if (len < 24)
return NULL; return NULL;
switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) { switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
case WLAN_FC_FROMDS | WLAN_FC_TODS:
case WLAN_FC_TODS: case WLAN_FC_TODS:
return hdr->addr1; return hdr->addr1;
case WLAN_FC_FROMDS: case WLAN_FC_FROMDS:
@ -344,11 +345,14 @@ static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd,
const struct ieee80211_hdr *hdr, const struct ieee80211_hdr *hdr,
size_t len) size_t len)
{ {
u16 fc = le_to_host16(hdr->frame_control);
hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len)); hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
if (hapd == NULL || hapd == HAPD_BROADCAST) if (hapd == NULL || hapd == HAPD_BROADCAST)
return; 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));
} }

View file

@ -106,7 +106,7 @@ static void hostapd_dump_state(struct hostapd_data *hapd)
fprintf(f, "\nSTA=" MACSTR "\n", MAC2STR(sta->addr)); fprintf(f, "\nSTA=" MACSTR "\n", MAC2STR(sta->addr));
fprintf(f, 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", " capability=0x%x listen_interval=%d\n",
sta->aid, sta->aid,
sta->flags, 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_MFP ? "[MFP]" : ""),
(sta->flags & WLAN_STA_WPS ? "[WPS]" : ""), (sta->flags & WLAN_STA_WPS ? "[WPS]" : ""),
(sta->flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""), (sta->flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""),
(sta->flags & WLAN_STA_WDS ? "[WDS]" : ""),
(sta->flags & WLAN_STA_NONERP ? "[NonERP]" : ""), (sta->flags & WLAN_STA_NONERP ? "[NonERP]" : ""),
sta->capability, sta->capability,
sta->listen_interval); sta->listen_interval);

View file

@ -346,6 +346,12 @@ wmm_ac_vo_acm=0
# remain asleep). Default: 65535 (no limit apart from field size) # remain asleep). Default: 65535 (no limit apart from field size)
#max_listen_interval=100 #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 ###################################### ##### IEEE 802.11n related configuration ######################################
# ieee80211n: Whether IEEE 802.11n (HT) is enabled # ieee80211n: Whether IEEE 802.11n (HT) is enabled

View file

@ -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; struct sta_info *sta;
sta = ap_get_sta(hapd, src); 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; return;
}
wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA " wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
MACSTR, MAC2STR(src)); MACSTR, MAC2STR(src));

View file

@ -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 update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
const u8 *buf, size_t len, int ack); 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 */ #endif /* IEEE802_11_H */

View file

@ -120,6 +120,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
accounting_sta_stop(hapd, 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) && if (!ap_sta_in_other_bss(hapd, sta, WLAN_STA_ASSOC) &&
!(sta->flags & WLAN_STA_PREAUTH)) !(sta->flags & WLAN_STA_PREAUTH))
hostapd_sta_remove(hapd, sta->addr); hostapd_sta_remove(hapd, sta->addr);

View file

@ -30,6 +30,7 @@
#define WLAN_STA_HT BIT(11) #define WLAN_STA_HT BIT(11)
#define WLAN_STA_WPS BIT(12) #define WLAN_STA_WPS BIT(12)
#define WLAN_STA_MAYBE_WPS BIT(13) #define WLAN_STA_MAYBE_WPS BIT(13)
#define WLAN_STA_WDS BIT(14)
#define WLAN_STA_NONERP BIT(31) #define WLAN_STA_NONERP BIT(31)
/* Maximum number of supported rates (from both Supported Rates and Extended /* Maximum number of supported rates (from both Supported Rates and Extended

View file

@ -1527,6 +1527,16 @@ struct wpa_driver_ops {
* Returns: 0 on success, -1 on failure * Returns: 0 on success, -1 on failure
*/ */
int (*set_supp_port)(void *priv, int authorized); 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);
}; };
/** /**

View file

@ -3244,5 +3244,6 @@ const struct wpa_driver_ops wpa_driver_ndis_ops = {
NULL /* set_ht_params */, NULL /* set_ht_params */,
NULL /* set_wps_beacon_ie */, NULL /* set_wps_beacon_ie */,
NULL /* set_wps_probe_resp_ie */, NULL /* set_wps_probe_resp_ie */,
NULL /* set_supp_port */ NULL /* set_supp_port */,
NULL /* set_wds_sta */
}; };

View file

@ -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, static struct i802_bss * get_bss(struct wpa_driver_nl80211_data *drv,
int ifindex); int ifindex);
static int i802_set_freq(void *priv, struct hostapd_freq_params *freq); 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 */ #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, static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
const char *ifname, const char *ifname,
enum nl80211_iftype iftype, enum nl80211_iftype iftype,
const u8 *addr) const u8 *addr, int wds)
{ {
struct nl_msg *msg, *flags = NULL; struct nl_msg *msg, *flags = NULL;
int ifidx; int ifidx;
@ -2584,6 +2587,8 @@ static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
if (err) if (err)
goto nla_put_failure; goto nla_put_failure;
} else if (wds) {
NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, wds);
} }
ret = send_and_recv_msgs(drv, msg, NULL, NULL); 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, static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
const char *ifname, enum nl80211_iftype iftype, const char *ifname, enum nl80211_iftype iftype,
const u8 *addr) const u8 *addr, int wds)
{ {
int ret; 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 error occured and interface exists already */
if (ret == -ENFILE && if_nametoindex(ifname)) { 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)); nl80211_remove_iface(drv, if_nametoindex(ifname));
/* Try to create the interface again */ /* 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; return ret;
@ -2817,7 +2823,7 @@ static struct sock_filter msock_filter_insns[] = {
#if 0 #if 0
/* /*
* drop non-data frames, WDS frames * drop non-data frames
*/ */
/* load the lower byte of the frame control field */ /* load the lower byte of the frame control field */
BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), 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), BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0c),
/* drop non-data frames */ /* drop non-data frames */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 8, 0, FAIL), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 8, 0, FAIL),
#endif
/* load the upper byte of the frame control field */ /* 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 */ /* mask off toDS/fromDS */
BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x03), BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x03),
/* drop WDS frames */ /* accept WDS frames */
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, FAIL, 0), BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 3, PASS, 0),
#endif
/* /*
* add header length to index * add header length to index
@ -2937,7 +2943,8 @@ nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
buf[IFNAMSIZ - 1] = '\0'; buf[IFNAMSIZ - 1] = '\0';
drv->monitor_ifidx = 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) if (drv->monitor_ifidx < 0)
return -1; 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) static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
{ {
struct wpa_driver_nl80211_data *drv = eloop_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 */ #endif /* HOSTAPD */
ifidx = nl80211_create_iface(drv, ifname, ifidx = nl80211_create_iface(drv, ifname,
wpa_driver_nl80211_if_type(type), addr); wpa_driver_nl80211_if_type(type), addr,
0);
if (ifidx < 0) { if (ifidx < 0) {
#ifdef HOSTAPD #ifdef HOSTAPD
os_free(bss); 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_short_slot_time = i802_set_short_slot_time,
.set_tx_queue_params = i802_set_tx_queue_params, .set_tx_queue_params = i802_set_tx_queue_params,
.set_sta_vlan = i802_set_sta_vlan, .set_sta_vlan = i802_set_sta_vlan,
.set_wds_sta = i802_set_wds_sta,
#endif /* HOSTAPD */ #endif /* HOSTAPD */
}; };

View file

@ -507,7 +507,10 @@ void ap_rx_from_unknown_sta(void *ctx, const struct ieee80211_hdr *hdr,
{ {
#ifdef NEED_AP_MLME #ifdef NEED_AP_MLME
struct wpa_supplicant *wpa_s = ctx; 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 */ #endif /* NEED_AP_MLME */
} }