[1/5] RRM: Add support for beacon report fragmentation

Message ID 1534945581-27527-1-git-send-email-andrei.otcheretianski@intel.com
State New
Headers show
Series
  • [1/5] RRM: Add support for beacon report fragmentation
Related show

Commit Message

Andrei Otcheretianski Aug. 22, 2018, 1:46 p.m.
From: Avraham Stern <avraham.stern@intel.com>

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 - Beacon Report fragmentation" defines
another method:
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>
---
 src/common/ieee802_11_defs.h |   5 ++
 wpa_supplicant/rrm.c         | 155 ++++++++++++++++++++++++++++---------------
 2 files changed, 105 insertions(+), 55 deletions(-)

Patch

diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index e03a095..c840b2e 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -1885,8 +1885,13 @@  struct rrm_measurement_beacon_report {
 
 /* IEEE Std 802.11-2016, Table 9-112 - Beacon report Subelement IDs */
 #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 - Beacon Report fragmentation */
+#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 f4fbfa7..a001181 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, u8 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 + 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;
+
+	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;
 }