hostapd: Add WDS (4-address frame) mode with per-station interfaces
authorFelix Fietkau <nbd@openwrt.org>
Thu, 24 Dec 2009 09:46:22 +0000 (11:46 +0200)
committerJouni Malinen <j@w1.fi>
Thu, 24 Dec 2009 09:46:22 +0000 (11:46 +0200)
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.

14 files changed:
hostapd/config.c
hostapd/config.h
hostapd/driver_i.h
hostapd/drv_callbacks.c
hostapd/dump_state.c
hostapd/hostapd.conf
hostapd/ieee802_11.c
hostapd/ieee802_11.h
hostapd/sta_info.c
hostapd/sta_info.h
src/drivers/driver.h
src/drivers/driver_ndis.c
src/drivers/driver_nl80211.c
wpa_supplicant/ap.c

index 1e89ad3..cb8f210 100644 (file)
@@ -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) {
index 0201a1d..467c382 100644 (file)
@@ -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} */
index e5c85a8..b73630c 100644 (file)
@@ -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)
index e1a7e4c..bb6e5f9 100644 (file)
@@ -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));
 }
 
 
index 408ef1e..0a191dc 100644 (file)
@@ -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);
index 6254496..2521b74 100644 (file)
@@ -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
index 8d4fe39..df18a93 100644 (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;
 
        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));
index 95c99b2..3347256 100644 (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 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 */
index 2a2d7ba..4d9ba42 100644 (file)
@@ -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);
index 6a98387..f15a1b0 100644 (file)
@@ -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
index 7fa4ed2..aa11259 100644 (file)
@@ -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);
 };
 
 /**
index bec1e8d..034be95 100644 (file)
@@ -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 */
 };
index 697fefd..67043a6 100644 (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,
                                 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 */
 };
index 2e52adf..1cbcde0 100644 (file)
@@ -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 */
 }