RRM: Add support for beacon report fragmentation

When the frame body subelement would cause the measurement report
element to exceed the maximum element size, the frame body subelement
used to be truncated. In addition, some elements were always truncated
in order to keep the reported frame body short (e.g. RSN IE).

Alternatively, IEEE P802.11-REVmd/D2.0, 9.4.2.21.7 extension to Beacon
reporting can be used: The frame body subelement is fragmented across
multiple beacon report elements, and the reported frame body fragment ID
subelement is added.

Use beacon report fragmentation instead of truncating the frame body
as this method gives the AP a more complete information about the
reported APs.

Signed-off-by: Avraham Stern <avraham.stern@intel.com>
This commit is contained in:
Avraham Stern 2018-08-22 16:46:17 +03:00 committed by Jouni Malinen
parent 6a94fdf21a
commit 3ec118780a
2 changed files with 108 additions and 55 deletions

View file

@ -1905,9 +1905,17 @@ struct rrm_measurement_beacon_report {
} STRUCT_PACKED;
/* IEEE Std 802.11-2016, Table 9-112 - Beacon report Subelement IDs */
/* IEEE P802.11-REVmd/D2.0, Table 9-130 - Optional subelement IDs for
* Beacon report */
#define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY 1
#define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY_FRAGMENT_ID 2
#define WLAN_BEACON_REPORT_SUBELEM_VENDOR 221
/* IEEE P802.11-REVmd/D2.0, Table 9-232 - Data field format of the
* Reported Frame Body Fragment ID subelement */
#define REPORTED_FRAME_BODY_SUBELEM_LEN 4
#define REPORTED_FRAME_BODY_MORE_FRAGMENTS BIT(7)
/* IEEE Std 802.11ad-2012 - Multi-band element */
struct multi_band_ie {
u8 eid; /* WLAN_EID_MULTI_BAND */

View file

@ -707,15 +707,17 @@ static int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len,
static int wpas_beacon_rep_add_frame_body(struct bitfield *eids,
enum beacon_report_detail detail,
struct wpa_bss *bss, u8 *buf,
size_t buf_len)
size_t buf_len, u8 **ies_buf,
size_t *ie_len, int add_fixed)
{
u8 *ies = (u8 *) (bss + 1);
size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len;
u8 *ies = *ies_buf;
size_t ies_len = *ie_len;
u8 *pos = buf;
int rem_len;
rem_len = 255 - sizeof(struct rrm_measurement_beacon_report) -
sizeof(struct rrm_measurement_report_element) - 2;
sizeof(struct rrm_measurement_report_element) - 2 -
REPORTED_FRAME_BODY_SUBELEM_LEN;
if (detail > BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS) {
wpa_printf(MSG_DEBUG,
@ -731,18 +733,21 @@ static int wpas_beacon_rep_add_frame_body(struct bitfield *eids,
* Minimal frame body subelement size: EID(1) + length(1) + TSF(8) +
* beacon interval(2) + capabilities(2) = 14 bytes
*/
if (buf_len < 14)
return 0;
if (add_fixed && buf_len < 14)
return -1;
*pos++ = WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY;
/* The length will be filled later */
pos++;
WPA_PUT_LE64(pos, bss->tsf);
pos += sizeof(bss->tsf);
WPA_PUT_LE16(pos, bss->beacon_int);
pos += 2;
WPA_PUT_LE16(pos, bss->caps);
pos += 2;
if (add_fixed) {
WPA_PUT_LE64(pos, bss->tsf);
pos += sizeof(bss->tsf);
WPA_PUT_LE16(pos, bss->beacon_int);
pos += 2;
WPA_PUT_LE16(pos, bss->caps);
pos += 2;
}
rem_len -= pos - buf;
@ -757,15 +762,7 @@ static int wpas_beacon_rep_add_frame_body(struct bitfield *eids,
while (ies_len > 2 && 2U + ies[1] <= ies_len && rem_len > 0) {
if (detail == BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS ||
(eids && bitfield_is_set(eids, ies[0]))) {
u8 eid = ies[0], elen = ies[1];
if ((eid == WLAN_EID_TIM || eid == WLAN_EID_RSN) &&
elen > 4)
elen = 4;
/*
* TODO: Truncate IBSS DFS element as described in
* IEEE Std 802.11-2016, 9.4.2.22.7.
*/
u8 elen = ies[1];
if (2 + elen > buf + buf_len - pos ||
2 + elen > rem_len)
@ -782,22 +779,80 @@ static int wpas_beacon_rep_add_frame_body(struct bitfield *eids,
ies += 2 + ies[1];
}
*ie_len = ies_len;
*ies_buf = ies;
/* Now the length is known */
buf[1] = pos - buf - 2;
return pos - buf;
}
static int wpas_add_beacon_rep_elem(struct beacon_rep_data *data,
struct wpa_bss *bss,
struct wpabuf **wpa_buf,
struct rrm_measurement_beacon_report *rep,
u8 **ie, size_t *ie_len, u8 idx)
{
int ret;
u8 *buf, *pos;
/* Maximum element length: Beacon Report element + Reported Frame Body
* subelement + all IEs of the reported Beacon frame + Reported Frame
* Body Fragment ID subelement */
buf = os_malloc(sizeof(*rep) + 14 + *ie_len +
REPORTED_FRAME_BODY_SUBELEM_LEN);
if (!buf)
return -1;
os_memcpy(buf, rep, sizeof(*rep));
ret = wpas_beacon_rep_add_frame_body(data->eids, data->report_detail,
bss, buf + sizeof(*rep),
14 + *ie_len, ie, ie_len,
idx == 0);
if (ret < 0)
goto out;
pos = buf + ret + sizeof(*rep);
pos[0] = WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY_FRAGMENT_ID;
pos[1] = 2;
/*
* Only one Beacon Report Measurement is supported at a time, so
* the Beacon Report ID can always be set to 1.
*/
pos[2] = 1;
/* Fragment ID Number (bits 0..6) and More Frame Body Fragments (bit 7)
*/
pos[3] = idx;
if (data->report_detail != BEACON_REPORT_DETAIL_NONE && *ie_len)
pos[3] |= REPORTED_FRAME_BODY_MORE_FRAGMENTS;
else
pos[3] &= ~REPORTED_FRAME_BODY_MORE_FRAGMENTS;
ret = wpas_rrm_report_elem(wpa_buf, data->token,
MEASUREMENT_REPORT_MODE_ACCEPT,
MEASURE_TYPE_BEACON, buf,
ret + sizeof(*rep) +
REPORTED_FRAME_BODY_SUBELEM_LEN);
out:
os_free(buf);
return ret;
}
static int wpas_add_beacon_rep(struct wpa_supplicant *wpa_s,
struct wpabuf **wpa_buf, struct wpa_bss *bss,
u64 start, u64 parent_tsf)
{
struct beacon_rep_data *data = &wpa_s->beacon_rep_data;
u8 *ie = (u8 *) (bss + 1);
size_t ie_len = bss->ie_len + bss->beacon_ie_len;
int ret;
u8 *buf;
struct rrm_measurement_beacon_report *rep;
u8 *ies = (u8 *) (bss + 1);
u8 *pos = ies;
size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len;
struct rrm_measurement_beacon_report rep;
u8 idx = 0;
if (os_memcmp(data->bssid, broadcast_ether_addr, ETH_ALEN) != 0 &&
os_memcmp(data->bssid, bss->bssid, ETH_ALEN) != 0)
@ -808,39 +863,29 @@ static int wpas_add_beacon_rep(struct wpa_supplicant *wpa_s,
os_memcmp(data->ssid, bss->ssid, bss->ssid_len) != 0))
return 0;
/* Maximum element length: beacon report element + reported frame body
* subelement + all IEs of the reported beacon */
buf = os_malloc(sizeof(*rep) + 14 + ie_len);
if (!buf)
return -1;
if (wpas_get_op_chan_phy(bss->freq, ies, ies_len, &rep.op_class,
&rep.channel, &rep.report_info) < 0)
return 0;
rep = (struct rrm_measurement_beacon_report *) buf;
if (wpas_get_op_chan_phy(bss->freq, ie, ie_len, &rep->op_class,
&rep->channel, &rep->report_info) < 0) {
ret = 0;
goto out;
}
rep.start_time = host_to_le64(start);
rep.duration = host_to_le16(data->scan_params.duration);
rep.rcpi = rssi_to_rcpi(bss->level);
rep.rsni = 255; /* 255 indicates that RSNI is not available */
os_memcpy(rep.bssid, bss->bssid, ETH_ALEN);
rep.antenna_id = 0; /* unknown */
rep.parent_tsf = host_to_le32(parent_tsf);
rep->start_time = host_to_le64(start);
rep->duration = host_to_le16(data->scan_params.duration);
rep->rcpi = rssi_to_rcpi(bss->level);
rep->rsni = 255; /* 255 indicates that RSNI is not available */
os_memcpy(rep->bssid, bss->bssid, ETH_ALEN);
rep->antenna_id = 0; /* unknown */
rep->parent_tsf = host_to_le32(parent_tsf);
do {
int ret;
ret = wpas_beacon_rep_add_frame_body(data->eids, data->report_detail,
bss, rep->variable, 14 + ie_len);
if (ret < 0)
goto out;
ret = wpas_add_beacon_rep_elem(data, bss, wpa_buf, &rep,
&pos, &ies_len, idx++);
if (ret)
return ret;
} while (data->report_detail != BEACON_REPORT_DETAIL_NONE &&
ies_len >= 2);
ret = wpas_rrm_report_elem(wpa_buf, wpa_s->beacon_rep_data.token,
MEASUREMENT_REPORT_MODE_ACCEPT,
MEASURE_TYPE_BEACON, buf,
ret + sizeof(*rep));
out:
os_free(buf);
return ret;
return 0;
}