]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
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 1e89ad39d2eeab838c3f40e38814102004756b1b..cb8f2107c0759daae5fc8855f3130533e7bde5fa 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 0201a1d10d8712c424e75ec1b7fc7b75d331c738..467c382cc89f4e60a1b047e9d1da48bd1f7b121c 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 e5c85a89f273f3698180993ab296fceb44f53522..b73630c2b6f795259f133544438dcffe8bcae3a7 100644 (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);
 }
 
+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)
 {
index e1a7e4c01d8dfe1d94e696e240927d1473bd139c..bb6e5f9760ec2b98299a509ec1fa000254ebaa78 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 408ef1e49b857327c91a4fae57ff6ff3f84ca124..0a191dc7993f5b5f6d0d9a8335297ddfe1cf5d58 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 625449663d12a1c5bc34afca2e012f3c5122e4fe..2521b7440b0a30a0e6adcdf89a8d613b38c1af27 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 8d4fe3923d5c7ca967aaf756842e1d8a36344fe1..df18a9341492f5049e8a2770bd9900f8bb2b787d 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 95c99b2d320ad7ad45af43d7e51c83630a3b31bf..3347256ee5a693eb120ce3b1ec537fe2cb8dc1cd 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 2a2d7bacc5c3c6cfc1b0e794a38b2b51bb2c87aa..4d9ba429a1e72c252a4afce1e837fd0e8a688cae 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 6a98387aa26053bb8bfaff82439c8c6913e7deee..f15a1b0d3023be7ed23133b718f83a657602d68e 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 7fa4ed2a0f78143799ea54baff6b1e1ce0b3869b..aa11259e5b205faac9f010b35a1ca6ab7232b7a2 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 bec1e8d8c5e4e004918dcf7194ece455dd074106..034be9576bc491930b11f74b8f83994dab6123bb 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 697fefd9f52cc29f810a519c796eca65111a4f1c..67043a622cd85ad27190a43b7069e363f9e9a24c 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 2e52adf2113e9b6705422832f3f78ca4bef6689a..1cbcde05cb1cf7705430999acf488c97452d73e3 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 */
 }