]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
hostapd: Fix Public Action frame addressing (BSSID field)
authorJouni Malinen <jouni@qca.qualcomm.com>
Fri, 10 Jun 2016 18:30:03 +0000 (21:30 +0300)
committerJouni Malinen <j@w1.fi>
Fri, 10 Jun 2016 18:44:49 +0000 (21:44 +0300)
IEEE Std 802.11-2012, 10.19 (Public Action frame addressing) specifies
that the wildcard BSSID value is used in Public Action frames that are
transmitted to a STA that is not a member of the same BSS. hostapd used
to use the actual BSSID value for all such frames regardless of whether
the destination STA is a member of the BSS.

Fix this by using the wildcard BSSID in cases the destination STA is not
a member of the BSS. Leave group addressed case as-is (i.e., the actual
BSSID), since both values are accepted. No such frames are currently
used, though.

This version is still using the AP BSSID value in the Address 3 field
for GAS response frames when replying to a GAS request with AP BSSID
instead of Wildcard BSSID. This is left as a workaround to avoid
interoperability issues with deployed STA implementations that are still
using the non-compliant address and that might be unable to process the
standard compliant case.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
src/ap/ap_drv_ops.c
src/ap/ap_drv_ops.h
src/ap/gas_serv.c

index e4fd0c51df9dbd2c851d379c6926dfad3bbb3eea..532b72ff57196b16ccc9407eff7fab8f63bfbd9b 100644 (file)
@@ -674,6 +674,36 @@ int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper,
 int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
                            unsigned int wait, const u8 *dst, const u8 *data,
                            size_t len)
+{
+       const u8 *bssid;
+       const u8 wildcard_bssid[ETH_ALEN] = {
+               0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+       };
+
+       if (hapd->driver == NULL || hapd->driver->send_action == NULL)
+               return 0;
+       bssid = hapd->own_addr;
+       if (!is_multicast_ether_addr(dst) &&
+           len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
+               struct sta_info *sta;
+
+               /*
+                * Public Action frames to a STA that is not a member of the BSS
+                * shall use wildcard BSSID value.
+                */
+               sta = ap_get_sta(hapd, dst);
+               if (!sta || !(sta->flags & WLAN_STA_ASSOC))
+                       bssid = wildcard_bssid;
+       }
+       return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
+                                        hapd->own_addr, bssid, data, len, 0);
+}
+
+
+int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
+                                    unsigned int freq,
+                                    unsigned int wait, const u8 *dst,
+                                    const u8 *data, size_t len)
 {
        if (hapd->driver == NULL || hapd->driver->send_action == NULL)
                return 0;
index 73ac3184890c0a28ec29760f6de3cf291b9f6975..6406d130bc0f6b9d403eacb2c24bf41b9da2abef 100644 (file)
@@ -99,6 +99,10 @@ int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
 int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
                            unsigned int wait, const u8 *dst, const u8 *data,
                            size_t len);
+int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
+                                    unsigned int freq,
+                                    unsigned int wait, const u8 *dst,
+                                    const u8 *data, size_t len);
 int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
                         u16 auth_alg);
 int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
index 179dc7a71d535abb85fe62d9449e620373b58afa..2b6efa849545d49c46f457673140b7d3a630c7a1 100644 (file)
@@ -1166,7 +1166,8 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
 
 static void gas_serv_req_local_processing(struct hostapd_data *hapd,
                                          const u8 *sa, u8 dialog_token,
-                                         struct anqp_query_info *qi, int prot)
+                                         struct anqp_query_info *qi, int prot,
+                                         int std_addr3)
 {
        struct wpabuf *buf, *tx_buf;
 
@@ -1227,15 +1228,22 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd,
                return;
        if (prot)
                convert_to_protected_dual(tx_buf);
-       hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
-                               wpabuf_head(tx_buf), wpabuf_len(tx_buf));
+       if (std_addr3)
+               hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+                                       wpabuf_head(tx_buf),
+                                       wpabuf_len(tx_buf));
+       else
+               hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
+                                                wpabuf_head(tx_buf),
+                                                wpabuf_len(tx_buf));
        wpabuf_free(tx_buf);
 }
 
 
 static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
                                        const u8 *sa,
-                                       const u8 *data, size_t len, int prot)
+                                       const u8 *data, size_t len, int prot,
+                                       int std_addr3)
 {
        const u8 *pos = data;
        const u8 *end = data + len;
@@ -1287,8 +1295,15 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
                wpabuf_put_le16(buf, 0); /* Query Response Length */
                if (prot)
                        convert_to_protected_dual(buf);
-               hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
-                                       wpabuf_head(buf), wpabuf_len(buf));
+               if (std_addr3)
+                       hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+                                               wpabuf_head(buf),
+                                               wpabuf_len(buf));
+               else
+                       hostapd_drv_send_action_addr3_ap(hapd,
+                                                        hapd->iface->freq, 0,
+                                                        sa, wpabuf_head(buf),
+                                                        wpabuf_len(buf));
                wpabuf_free(buf);
                return;
        }
@@ -1338,13 +1353,15 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
                pos += elen;
        }
 
-       gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot);
+       gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot,
+                                     std_addr3);
 }
 
 
 static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
                                         const u8 *sa,
-                                        const u8 *data, size_t len, int prot)
+                                        const u8 *data, size_t len, int prot,
+                                        int std_addr3)
 {
        struct gas_dialog_info *dialog;
        struct wpabuf *buf, *tx_buf;
@@ -1420,8 +1437,14 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
 send_resp:
        if (prot)
                convert_to_protected_dual(tx_buf);
-       hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
-                               wpabuf_head(tx_buf), wpabuf_len(tx_buf));
+       if (std_addr3)
+               hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+                                       wpabuf_head(tx_buf),
+                                       wpabuf_len(tx_buf));
+       else
+               hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
+                                                wpabuf_head(tx_buf),
+                                                wpabuf_len(tx_buf));
        wpabuf_free(tx_buf);
 }
 
@@ -1432,7 +1455,7 @@ static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
        struct hostapd_data *hapd = ctx;
        const struct ieee80211_mgmt *mgmt;
        const u8 *sa, *data;
-       int prot;
+       int prot, std_addr3;
 
        mgmt = (const struct ieee80211_mgmt *) buf;
        if (len < IEEE80211_HDRLEN + 2)
@@ -1447,14 +1470,17 @@ static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
         */
        prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
        sa = mgmt->sa;
+       std_addr3 = is_broadcast_ether_addr(mgmt->bssid);
        len -= IEEE80211_HDRLEN + 1;
        data = buf + IEEE80211_HDRLEN + 1;
        switch (data[0]) {
        case WLAN_PA_GAS_INITIAL_REQ:
-               gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot);
+               gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot,
+                                           std_addr3);
                break;
        case WLAN_PA_GAS_COMEBACK_REQ:
-               gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot);
+               gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot,
+                                            std_addr3);
                break;
        }
 }