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:
parent
09eac1ac56
commit
fbbfcbac29
14 changed files with 96 additions and 18 deletions
|
@ -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) {
|
||||
|
|
|
@ -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} */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue