diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index ee6a9db09..ee750fa45 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -1851,11 +1851,14 @@ enum beacon_report_mode { }; /* IEEE Std 802.11-2016, Table 9-88 - Beacon Request subelement IDs */ +/* IEEE P802.11-REVmd/D2.0, Table 9-106 - Optional subelement IDs for + * Beacon request */ #define WLAN_BEACON_REQUEST_SUBELEM_SSID 0 #define WLAN_BEACON_REQUEST_SUBELEM_INFO 1 /* Beacon Reporting */ #define WLAN_BEACON_REQUEST_SUBELEM_DETAIL 2 /* Reporting Detail */ #define WLAN_BEACON_REQUEST_SUBELEM_REQUEST 10 #define WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL 51 /* AP Channel Report */ +#define WLAN_BEACON_REQUEST_SUBELEM_LAST_INDICATION 164 #define WLAN_BEACON_REQUEST_SUBELEM_VENDOR 221 /* @@ -1909,6 +1912,7 @@ struct rrm_measurement_beacon_report { * 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_LAST_INDICATION 164 #define WLAN_BEACON_REPORT_SUBELEM_VENDOR 221 /* IEEE P802.11-REVmd/D2.0, Table 9-232 - Data field format of the @@ -1916,6 +1920,9 @@ struct rrm_measurement_beacon_report { #define REPORTED_FRAME_BODY_SUBELEM_LEN 4 #define REPORTED_FRAME_BODY_MORE_FRAGMENTS BIT(7) +/* IEEE P802.11-REVmd/D2.0, 9.4.2.21.7 - Beacon report */ +#define BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN 3 + /* 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 3ba10c5b5..ab5e6dbdb 100644 --- a/wpa_supplicant/rrm.c +++ b/wpa_supplicant/rrm.c @@ -392,17 +392,66 @@ static void wpas_rrm_send_msr_report_mpdu(struct wpa_supplicant *wpa_s, } +static int wpas_rrm_beacon_rep_update_last_frame(u8 *pos, size_t len) +{ + struct rrm_measurement_report_element *msr_rep; + u8 *end = pos + len; + u8 *msr_rep_end; + + while (end - pos >= (int) sizeof(*msr_rep)) { + msr_rep = (struct rrm_measurement_report_element *) pos; + msr_rep_end = pos + msr_rep->len + 2; + + if (msr_rep->eid != WLAN_EID_MEASURE_REPORT || + msr_rep_end > end) { + /* Should not happen. This indicates a bug. */ + wpa_printf(MSG_ERROR, + "RRM: non-measurement report element in measurement report frame"); + return -1; + } + + if (msr_rep->type == MEASURE_TYPE_BEACON) { + struct rrm_measurement_beacon_report *rep; + u8 *subelem; + + rep = (struct rrm_measurement_beacon_report *) + msr_rep->variable; + subelem = rep->variable; + while (subelem + 2 < msr_rep_end && + subelem[0] != + WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION) + subelem += 2 + subelem[1]; + + if (subelem + 2 < msr_rep_end && + subelem[0] == + WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION && + subelem[1] == 1 && + subelem + + BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN <= end) + subelem[2] = 1; + } + + pos += pos[1] + 2; + } + + return 0; +} + + static void wpas_rrm_send_msr_report(struct wpa_supplicant *wpa_s, struct wpabuf *buf) { int len = wpabuf_len(buf); - const u8 *pos = wpabuf_head_u8(buf), *next = pos; + u8 *pos = wpabuf_mhead_u8(buf), *next = pos; #define MPDU_REPORT_LEN (int) (IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN - 3) while (len) { int send_len = (len > MPDU_REPORT_LEN) ? next - pos : len; + if (send_len == len) + wpas_rrm_beacon_rep_update_last_frame(pos, len); + if (send_len == len || (send_len + next[1] + 2) > MPDU_REPORT_LEN) { wpas_rrm_send_msr_report_mpdu(wpa_s, pos, send_len); @@ -796,12 +845,14 @@ static int wpas_add_beacon_rep_elem(struct beacon_rep_data *data, { int ret; u8 *buf, *pos; + u32 subelems_len = REPORTED_FRAME_BODY_SUBELEM_LEN + + (data->last_indication ? + BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN : 0); /* 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); + buf = os_malloc(sizeof(*rep) + 14 + *ie_len + subelems_len); if (!buf) return -1; @@ -832,11 +883,20 @@ static int wpas_add_beacon_rep_elem(struct beacon_rep_data *data, else pos[3] &= ~REPORTED_FRAME_BODY_MORE_FRAGMENTS; + pos += REPORTED_FRAME_BODY_SUBELEM_LEN; + + if (data->last_indication) { + pos[0] = WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION; + pos[1] = 1; + + /* This field will be updated later if this is the last frame */ + pos[2] = 0; + } + 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); + ret + sizeof(*rep) + subelems_len); out: os_free(buf); return ret; @@ -1051,6 +1111,16 @@ static int wpas_rm_handle_beacon_req_subelem(struct wpa_supplicant *wpa_s, case WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL: /* Skip - it will be processed when freqs are added */ break; + case WLAN_BEACON_REQUEST_SUBELEM_LAST_INDICATION: + if (slen != 1) { + wpa_printf(MSG_DEBUG, + "Beacon request: Invalid last indication request subelement length: %u", + slen); + return -1; + } + + data->last_indication = subelem[0]; + break; default: wpa_printf(MSG_DEBUG, "Beacon request: Unknown subelement id %u", sid); diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index a2f78e685..c2298a216 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -456,6 +456,7 @@ struct wpa_bss_tmp_disallowed { struct beacon_rep_data { u8 token; + u8 last_indication; struct wpa_driver_scan_params scan_params; u8 ssid[SSID_MAX_LEN]; size_t ssid_len;