From 3ec118780a6a2bdfd4543d8cb6a896cc022abb01 Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Wed, 22 Aug 2018 16:46:17 +0300 Subject: [PATCH] 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 --- src/common/ieee802_11_defs.h | 8 ++ wpa_supplicant/rrm.c | 155 ++++++++++++++++++++++------------- 2 files changed, 108 insertions(+), 55 deletions(-) diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index ad6dbd71a..ee6a9db09 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -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 */ diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c index f4fbfa719..3ba10c5b5 100644 --- a/wpa_supplicant/rrm.c +++ b/wpa_supplicant/rrm.c @@ -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; }