Browse Source

hostapd: Fix Public Action frame addressing (BSSID field)

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>
Jouni Malinen 8 years ago
parent
commit
78a3632765
3 changed files with 73 additions and 13 deletions
  1. 30 0
      src/ap/ap_drv_ops.c
  2. 4 0
      src/ap/ap_drv_ops.h
  3. 39 13
      src/ap/gas_serv.c

+ 30 - 0
src/ap/ap_drv_ops.c

@@ -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;

+ 4 - 0
src/ap/ap_drv_ops.h

@@ -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,

+ 39 - 13
src/ap/gas_serv.c

@@ -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;
 	}
 }