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>
This commit is contained in:
parent
c86bef2913
commit
78a3632765
3 changed files with 73 additions and 13 deletions
|
@ -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,
|
int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
|
||||||
unsigned int wait, const u8 *dst, const u8 *data,
|
unsigned int wait, const u8 *dst, const u8 *data,
|
||||||
size_t len)
|
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)
|
if (hapd->driver == NULL || hapd->driver->send_action == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -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,
|
int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
|
||||||
unsigned int wait, const u8 *dst, const u8 *data,
|
unsigned int wait, const u8 *dst, const u8 *data,
|
||||||
size_t len);
|
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,
|
int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
|
||||||
u16 auth_alg);
|
u16 auth_alg);
|
||||||
int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
|
int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
|
||||||
|
|
|
@ -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,
|
static void gas_serv_req_local_processing(struct hostapd_data *hapd,
|
||||||
const u8 *sa, u8 dialog_token,
|
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;
|
struct wpabuf *buf, *tx_buf;
|
||||||
|
|
||||||
|
@ -1227,15 +1228,22 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd,
|
||||||
return;
|
return;
|
||||||
if (prot)
|
if (prot)
|
||||||
convert_to_protected_dual(tx_buf);
|
convert_to_protected_dual(tx_buf);
|
||||||
hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
|
if (std_addr3)
|
||||||
wpabuf_head(tx_buf), wpabuf_len(tx_buf));
|
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);
|
wpabuf_free(tx_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
|
static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
|
||||||
const u8 *sa,
|
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 *pos = data;
|
||||||
const u8 *end = data + len;
|
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 */
|
wpabuf_put_le16(buf, 0); /* Query Response Length */
|
||||||
if (prot)
|
if (prot)
|
||||||
convert_to_protected_dual(buf);
|
convert_to_protected_dual(buf);
|
||||||
hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
|
if (std_addr3)
|
||||||
wpabuf_head(buf), wpabuf_len(buf));
|
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);
|
wpabuf_free(buf);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1338,13 +1353,15 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
|
||||||
pos += elen;
|
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,
|
static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
|
||||||
const u8 *sa,
|
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 gas_dialog_info *dialog;
|
||||||
struct wpabuf *buf, *tx_buf;
|
struct wpabuf *buf, *tx_buf;
|
||||||
|
@ -1420,8 +1437,14 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
|
||||||
send_resp:
|
send_resp:
|
||||||
if (prot)
|
if (prot)
|
||||||
convert_to_protected_dual(tx_buf);
|
convert_to_protected_dual(tx_buf);
|
||||||
hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
|
if (std_addr3)
|
||||||
wpabuf_head(tx_buf), wpabuf_len(tx_buf));
|
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);
|
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;
|
struct hostapd_data *hapd = ctx;
|
||||||
const struct ieee80211_mgmt *mgmt;
|
const struct ieee80211_mgmt *mgmt;
|
||||||
const u8 *sa, *data;
|
const u8 *sa, *data;
|
||||||
int prot;
|
int prot, std_addr3;
|
||||||
|
|
||||||
mgmt = (const struct ieee80211_mgmt *) buf;
|
mgmt = (const struct ieee80211_mgmt *) buf;
|
||||||
if (len < IEEE80211_HDRLEN + 2)
|
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;
|
prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
|
||||||
sa = mgmt->sa;
|
sa = mgmt->sa;
|
||||||
|
std_addr3 = is_broadcast_ether_addr(mgmt->bssid);
|
||||||
len -= IEEE80211_HDRLEN + 1;
|
len -= IEEE80211_HDRLEN + 1;
|
||||||
data = buf + IEEE80211_HDRLEN + 1;
|
data = buf + IEEE80211_HDRLEN + 1;
|
||||||
switch (data[0]) {
|
switch (data[0]) {
|
||||||
case WLAN_PA_GAS_INITIAL_REQ:
|
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;
|
break;
|
||||||
case WLAN_PA_GAS_COMEBACK_REQ:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue