WNM: Use a dynamically allocated buffer for BTM query and response

BSS transition management query and response use a large static buffer
for the frame because the candidate list may require a lot of space.
However, in most cases the actually used space will be much less than
the buffer (since the candidate list is short or completely missing).

Use a dynamically allocated buffer instead and allocate it according
to the actual space needed.

While at it, remove unneeded filling of the MAC header in the Action
frames, since this gets ignored and wpa_drv_send_action() adds the MAC
header anyway.

Signed-off-by: Avraham Stern <avraham.stern@intel.com>
This commit is contained in:
Avraham Stern 2017-03-08 14:37:38 +02:00 committed by Jouni Malinen
parent 5d9fa5ead2
commit 13bf18eda5

View File

@ -780,36 +780,40 @@ static u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
} }
static int wnm_add_nei_rep(u8 *buf, size_t len, const u8 *bssid, u32 bss_info, static int wnm_add_nei_rep(struct wpabuf **buf, const u8 *bssid,
u8 op_class, u8 chan, u8 phy_type, u8 pref) u32 bss_info, u8 op_class, u8 chan, u8 phy_type,
u8 pref)
{ {
u8 *pos = buf; if (wpabuf_len(*buf) + 18 >
IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN) {
if (len < 18) {
wpa_printf(MSG_DEBUG, wpa_printf(MSG_DEBUG,
"WNM: Not enough room for Neighbor Report element"); "WNM: No room in frame for Neighbor Report element");
return -1; return -1;
} }
*pos++ = WLAN_EID_NEIGHBOR_REPORT; if (wpabuf_resize(buf, 18) < 0) {
wpa_printf(MSG_DEBUG,
"WNM: Failed to allocate memory for Neighbor Report element");
return -1;
}
wpabuf_put_u8(*buf, WLAN_EID_NEIGHBOR_REPORT);
/* length: 13 for basic neighbor report + 3 for preference subelement */ /* length: 13 for basic neighbor report + 3 for preference subelement */
*pos++ = 16; wpabuf_put_u8(*buf, 16);
os_memcpy(pos, bssid, ETH_ALEN); wpabuf_put_data(*buf, bssid, ETH_ALEN);
pos += ETH_ALEN; wpabuf_put_le32(*buf, bss_info);
WPA_PUT_LE32(pos, bss_info); wpabuf_put_u8(*buf, op_class);
pos += 4; wpabuf_put_u8(*buf, chan);
*pos++ = op_class; wpabuf_put_u8(*buf, phy_type);
*pos++ = chan; wpabuf_put_u8(*buf, WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE);
*pos++ = phy_type; wpabuf_put_u8(*buf, 1);
*pos++ = WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE; wpabuf_put_u8(*buf, pref);
*pos++ = 1; return 0;
*pos++ = pref;
return pos - buf;
} }
static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s, static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s,
struct wpa_bss *bss, u8 *buf, size_t len, struct wpa_bss *bss, struct wpabuf **buf,
u8 pref) u8 pref)
{ {
const u8 *ie; const u8 *ie;
@ -858,20 +862,19 @@ static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s,
info = wnm_get_bss_info(wpa_s, bss); info = wnm_get_bss_info(wpa_s, bss);
return wnm_add_nei_rep(buf, len, bss->bssid, info, op_class, chan, return wnm_add_nei_rep(buf, bss->bssid, info, op_class, chan, phy_type,
phy_type, pref); pref);
} }
static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len) static void wnm_add_cand_list(struct wpa_supplicant *wpa_s, struct wpabuf **buf)
{ {
u8 *pos = buf;
unsigned int i, pref = 255; unsigned int i, pref = 255;
struct os_reltime now; struct os_reltime now;
struct wpa_ssid *ssid = wpa_s->current_ssid; struct wpa_ssid *ssid = wpa_s->current_ssid;
if (!ssid) if (!ssid)
return 0; return;
/* /*
* TODO: Define when scan results are no longer valid for the candidate * TODO: Define when scan results are no longer valid for the candidate
@ -879,7 +882,7 @@ static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len)
*/ */
os_get_reltime(&now); os_get_reltime(&now);
if (os_reltime_expired(&now, &wpa_s->last_scan, 10)) if (os_reltime_expired(&now, &wpa_s->last_scan, 10))
return 0; return;
wpa_printf(MSG_DEBUG, wpa_printf(MSG_DEBUG,
"WNM: Add candidate list to BSS Transition Management Response frame"); "WNM: Add candidate list to BSS Transition Management Response frame");
@ -888,36 +891,31 @@ static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len)
int res; int res;
if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0)) { if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0)) {
res = wnm_nei_rep_add_bss(wpa_s, bss, pos, len, pref--); res = wnm_nei_rep_add_bss(wpa_s, bss, buf, pref--);
if (res == -2) if (res == -2)
continue; /* could not build entry for BSS */ continue; /* could not build entry for BSS */
if (res < 0) if (res < 0)
break; /* no more room for candidates */ break; /* no more room for candidates */
if (pref == 1) if (pref == 1)
break; break;
pos += res;
len -= res;
} }
} }
wpa_hexdump(MSG_DEBUG, wpa_hexdump_buf(MSG_DEBUG,
"WNM: BSS Transition Management Response candidate list", "WNM: BSS Transition Management Response candidate list",
buf, pos - buf); *buf);
return pos - buf;
} }
#define BTM_RESP_MIN_SIZE 5 + ETH_ALEN
static void wnm_send_bss_transition_mgmt_resp( static void wnm_send_bss_transition_mgmt_resp(
struct wpa_supplicant *wpa_s, u8 dialog_token, struct wpa_supplicant *wpa_s, u8 dialog_token,
enum bss_trans_mgmt_status_code status, enum bss_trans_mgmt_status_code status,
enum mbo_transition_reject_reason reason, enum mbo_transition_reject_reason reason,
u8 delay, const u8 *target_bssid) u8 delay, const u8 *target_bssid)
{ {
u8 buf[2000], *pos; struct wpabuf *buf;
struct ieee80211_mgmt *mgmt;
size_t len;
int res; int res;
wpa_printf(MSG_DEBUG, wpa_printf(MSG_DEBUG,
@ -930,52 +928,62 @@ static void wnm_send_bss_transition_mgmt_resp(
return; return;
} }
mgmt = (struct ieee80211_mgmt *) buf; buf = wpabuf_alloc(BTM_RESP_MIN_SIZE);
os_memset(&buf, 0, sizeof(buf)); if (!buf) {
os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); wpa_printf(MSG_DEBUG,
os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); "WNM: Failed to allocate memory for BTM response");
os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); return;
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, }
WLAN_FC_STYPE_ACTION);
mgmt->u.action.category = WLAN_ACTION_WNM; wpabuf_put_u8(buf, WLAN_ACTION_WNM);
mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP; wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP);
mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token; wpabuf_put_u8(buf, dialog_token);
mgmt->u.action.u.bss_tm_resp.status_code = status; wpabuf_put_u8(buf, status);
mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay; wpabuf_put_u8(buf, delay);
pos = mgmt->u.action.u.bss_tm_resp.variable;
if (target_bssid) { if (target_bssid) {
os_memcpy(pos, target_bssid, ETH_ALEN); wpabuf_put_data(buf, target_bssid, ETH_ALEN);
pos += ETH_ALEN;
} else if (status == WNM_BSS_TM_ACCEPT) { } else if (status == WNM_BSS_TM_ACCEPT) {
/* /*
* P802.11-REVmc clarifies that the Target BSSID field is always * P802.11-REVmc clarifies that the Target BSSID field is always
* present when status code is zero, so use a fake value here if * present when status code is zero, so use a fake value here if
* no BSSID is yet known. * no BSSID is yet known.
*/ */
os_memset(pos, 0, ETH_ALEN); wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN);
pos += ETH_ALEN;
} }
if (status == WNM_BSS_TM_ACCEPT) if (status == WNM_BSS_TM_ACCEPT)
pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos); wnm_add_cand_list(wpa_s, &buf);
#ifdef CONFIG_MBO #ifdef CONFIG_MBO
if (status != WNM_BSS_TM_ACCEPT && if (status != WNM_BSS_TM_ACCEPT &&
wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) { wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) {
pos += wpas_mbo_ie_bss_trans_reject( u8 mbo[10];
wpa_s, pos, buf + sizeof(buf) - pos, reason); size_t ret;
ret = wpas_mbo_ie_bss_trans_reject(wpa_s, mbo, sizeof(mbo),
reason);
if (ret) {
if (wpabuf_resize(&buf, ret) < 0) {
wpabuf_free(buf);
wpa_printf(MSG_DEBUG,
"WNM: Failed to allocate memory for MBO IE");
return;
}
wpabuf_put_data(buf, mbo, ret);
}
} }
#endif /* CONFIG_MBO */ #endif /* CONFIG_MBO */
len = pos - (u8 *) &mgmt->u.action.category;
res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
wpa_s->own_addr, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid,
&mgmt->u.action.category, len, 0); wpabuf_head_u8(buf), wpabuf_len(buf), 0);
if (res < 0) { if (res < 0) {
wpa_printf(MSG_DEBUG, wpa_printf(MSG_DEBUG,
"WNM: Failed to send BSS Transition Management Response"); "WNM: Failed to send BSS Transition Management Response");
} }
wpabuf_free(buf);
} }
@ -1475,12 +1483,12 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
} }
#define BTM_QUERY_MIN_SIZE 4
int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
u8 query_reason, int cand_list) u8 query_reason, int cand_list)
{ {
u8 buf[2000], *pos; struct wpabuf *buf;
struct ieee80211_mgmt *mgmt;
size_t len;
int ret; int ret;
wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to " wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
@ -1488,28 +1496,23 @@ int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
MAC2STR(wpa_s->bssid), query_reason, MAC2STR(wpa_s->bssid), query_reason,
cand_list ? " candidate list" : ""); cand_list ? " candidate list" : "");
mgmt = (struct ieee80211_mgmt *) buf; buf = wpabuf_alloc(BTM_QUERY_MIN_SIZE);
os_memset(&buf, 0, sizeof(buf)); if (!buf)
os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); return -1;
os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); wpabuf_put_u8(buf, WLAN_ACTION_WNM);
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_QUERY);
WLAN_FC_STYPE_ACTION); wpabuf_put_u8(buf, 1);
mgmt->u.action.category = WLAN_ACTION_WNM; wpabuf_put_u8(buf, query_reason);
mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY;
mgmt->u.action.u.bss_tm_query.dialog_token = 1;
mgmt->u.action.u.bss_tm_query.query_reason = query_reason;
pos = mgmt->u.action.u.bss_tm_query.variable;
if (cand_list) if (cand_list)
pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos); wnm_add_cand_list(wpa_s, &buf);
len = pos - (u8 *) &mgmt->u.action.category;
ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
wpa_s->own_addr, wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid,
&mgmt->u.action.category, len, 0); wpabuf_head_u8(buf), wpabuf_len(buf), 0);
wpabuf_free(buf);
return ret; return ret;
} }