From 76196ddb2b61425e28306bbb3c8600877cfc822a Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Wed, 28 Dec 2016 15:06:47 +0200 Subject: [PATCH] wpa_supplicant: Add support for Beacon Report Radio Measurement Beacon Report Radio Measurement is defined in IEEE Std 802.11-2016, 11.11.9.1. Beacon Report is implemented by triggering a scan on the requested channels with the requested parameters. Signed-off-by: Avraham Stern --- wpa_supplicant/Android.mk | 2 +- wpa_supplicant/Makefile | 5 +- wpa_supplicant/events.c | 4 + wpa_supplicant/rrm.c | 767 ++++++++++++++++++++++++++++++ wpa_supplicant/sme.c | 5 + wpa_supplicant/wpa_supplicant.c | 1 + wpa_supplicant/wpa_supplicant_i.h | 17 + 7 files changed, 796 insertions(+), 5 deletions(-) diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index 43ed84701..3c877f090 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -92,6 +92,7 @@ OBJS += eap_register.c OBJS += src/utils/common.c OBJS += src/utils/wpa_debug.c OBJS += src/utils/wpabuf.c +OBJS += src/utils/bitfield.c OBJS += wmm_ac.c OBJS += op_classes.c OBJS += rrm.c @@ -311,7 +312,6 @@ OBJS += src/p2p/p2p_invitation.c OBJS += src/p2p/p2p_dev_disc.c OBJS += src/p2p/p2p_group.c OBJS += src/ap/p2p_hostapd.c -OBJS += src/utils/bitfield.c L_CFLAGS += -DCONFIG_P2P NEED_GAS=y NEED_OFFCHANNEL=y diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index a7d8310d1..dae691105 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -103,6 +103,7 @@ OBJS += eap_register.o OBJS += ../src/utils/common.o OBJS += ../src/utils/wpa_debug.o OBJS += ../src/utils/wpabuf.o +OBJS += ../src/utils/bitfield.o OBJS += op_classes.o OBJS += rrm.o OBJS_p = wpa_passphrase.o @@ -352,7 +353,6 @@ OBJS += ../src/p2p/p2p_invitation.o OBJS += ../src/p2p/p2p_dev_disc.o OBJS += ../src/p2p/p2p_group.o OBJS += ../src/ap/p2p_hostapd.o -OBJS += ../src/utils/bitfield.o CFLAGS += -DCONFIG_P2P NEED_GAS=y NEED_OFFCHANNEL=y @@ -1604,9 +1604,6 @@ OBJS += ../src/crypto/crypto_module_tests.o ifdef CONFIG_WPS OBJS += ../src/wps/wps_module_tests.o endif -ifndef CONFIG_P2P -OBJS += ../src/utils/bitfield.o -endif endif OBJS += ../src/drivers/driver_common.o diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index a57b1dad2..afdda00bd 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -1736,6 +1736,10 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s, if (sme_proc_obss_scan(wpa_s) > 0) goto scan_work_done; + if (own_request && + wpas_beacon_rep_scan_process(wpa_s, scan_res, &data->scan_info) > 0) + goto scan_work_done; + if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) goto scan_work_done; diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c index 5223e8ef3..dfa042230 100644 --- a/wpa_supplicant/rrm.c +++ b/wpa_supplicant/rrm.c @@ -14,6 +14,8 @@ #include "wpa_supplicant_i.h" #include "driver_i.h" #include "bss.h" +#include "scan.h" +#include "p2p_supplicant.h" static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx) @@ -47,6 +49,7 @@ void wpas_rrm_reset(struct wpa_supplicant *wpa_s) if (wpa_s->rrm.notify_neighbor_rep) wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL); wpa_s->rrm.next_neighbor_rep_token = 1; + wpas_clear_beacon_rep_data(wpa_s); } @@ -423,12 +426,696 @@ static void wpas_rrm_send_msr_report(struct wpa_supplicant *wpa_s, } +static int wpas_add_channel(u8 op_class, u8 chan, u8 num_primary_channels, + int *freqs) +{ + size_t i; + + for (i = 0; i < num_primary_channels; i++) { + u8 primary_chan = chan - (2 * num_primary_channels - 2) + i * 4; + + freqs[i] = ieee80211_chan_to_freq(NULL, op_class, primary_chan); + if (freqs[i] < 0) { + wpa_printf(MSG_DEBUG, + "Beacon Report: Invalid channel %u", + chan); + return -1; + } + } + + return 0; +} + + +static int * wpas_add_channels(const struct oper_class_map *op, + struct hostapd_hw_modes *mode, int active, + const u8 *channels, const u8 size) +{ + int *freqs, *next_freq; + u8 num_primary_channels, i; + u8 num_chans; + + num_chans = channels ? size : + (op->max_chan - op->min_chan) / op->inc + 1; + + if (op->bw == BW80 || op->bw == BW80P80) + num_primary_channels = 4; + else if (op->bw == BW160) + num_primary_channels = 8; + else + num_primary_channels = 1; + + /* one extra place for the zero-terminator */ + freqs = os_calloc(num_chans * num_primary_channels + 1, sizeof(*freqs)); + if (!freqs) { + wpa_printf(MSG_ERROR, + "Beacon Report: Failed to allocate freqs array"); + return NULL; + } + + next_freq = freqs; + for (i = 0; i < num_chans; i++) { + u8 chan = channels ? channels[i] : op->min_chan + i * op->inc; + enum chan_allowed res = verify_channel(mode, chan, op->bw); + + if (res == NOT_ALLOWED || (res == NO_IR && active)) + continue; + + if (wpas_add_channel(op->op_class, chan, num_primary_channels, + next_freq) < 0) { + os_free(freqs); + return NULL; + } + + next_freq += num_primary_channels; + } + + if (!freqs[0]) { + os_free(freqs); + return NULL; + } + + return freqs; +} + + +static int * wpas_op_class_freqs(const struct oper_class_map *op, + struct hostapd_hw_modes *mode, int active) +{ + u8 channels_80mhz[] = { 42, 58, 106, 122, 138, 155 }; + u8 channels_160mhz[] = { 50, 114 }; + + /* + * When adding all channels in the operating class, 80 + 80 MHz + * operating classes are like 80 MHz channels because we add all valid + * channels anyway. + */ + if (op->bw == BW80 || op->bw == BW80P80) + return wpas_add_channels(op, mode, active, channels_80mhz, + ARRAY_SIZE(channels_80mhz)); + + if (op->bw == BW160) + return wpas_add_channels(op, mode, active, channels_160mhz, + ARRAY_SIZE(channels_160mhz)); + + return wpas_add_channels(op, mode, active, NULL, 0); +} + + +static int * wpas_channel_report_freqs(struct wpa_supplicant *wpa_s, int active, + const char *country, const u8 *subelems, + size_t len) +{ + int *freqs = NULL, *new_freqs; + const u8 *end = subelems + len; + + while (end - subelems > 2) { + const struct oper_class_map *op; + const u8 *ap_chan_elem, *pos; + u8 left; + struct hostapd_hw_modes *mode; + + ap_chan_elem = get_ie(subelems, end - subelems, + WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL); + if (!ap_chan_elem) + break; + pos = ap_chan_elem + 2; + left = ap_chan_elem[1]; + if (left < 1) + break; + subelems = ap_chan_elem + 2 + left; + + op = get_oper_class(country, *pos); + if (!op) { + wpa_printf(MSG_DEBUG, + "Beacon request: unknown operating class in AP Channel Report subelement %u", + *pos); + goto out; + } + pos++; + left--; + + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op->mode); + if (!mode) + continue; + + /* + * For 80 + 80 MHz operating classes, this AP Channel Report + * element should be followed by another element specifying + * the second 80 MHz channel. For now just add this 80 MHz + * channel, the second 80 MHz channel will be added when the + * next element is parsed. + * TODO: Verify that this AP Channel Report element is followed + * by a corresponding AP Channel Report element as specified in + * IEEE Std 802.11-2016, 11.11.9.1. + */ + new_freqs = wpas_add_channels(op, mode, active, pos, left); + if (new_freqs) + int_array_concat(&freqs, new_freqs); + + os_free(new_freqs); + } + + return freqs; +out: + os_free(freqs); + return NULL; +} + + +static int * wpas_beacon_request_freqs(struct wpa_supplicant *wpa_s, + u8 op_class, u8 chan, int active, + const u8 *subelems, size_t len) +{ + int *freqs = NULL, *ext_freqs = NULL; + struct hostapd_hw_modes *mode; + const char *country = NULL; + const struct oper_class_map *op; + const u8 *elem; + + if (!wpa_s->current_bss) + return NULL; + elem = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_COUNTRY); + if (elem && elem[1] >= 2) + country = (const char *) (elem + 2); + + op = get_oper_class(country, op_class); + if (!op) { + wpa_printf(MSG_DEBUG, + "Beacon request: invalid operating class %d", + op_class); + return NULL; + } + + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op->mode); + if (!mode) + return NULL; + + switch (chan) { + case 0: + freqs = wpas_op_class_freqs(op, mode, active); + if (!freqs) + return NULL; + break; + case 255: + /* freqs will be added from AP channel subelements */ + break; + default: + freqs = wpas_add_channels(op, mode, active, &chan, 1); + if (!freqs) + return NULL; + break; + } + + ext_freqs = wpas_channel_report_freqs(wpa_s, active, country, subelems, + len); + if (ext_freqs) { + int_array_concat(&freqs, ext_freqs); + os_free(ext_freqs); + } + + return freqs; +} + + +int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len, + u8 *op_class, u8 *chan, u8 *phy_type) +{ + const u8 *ie; + int sec_chan = 0, vht = 0; + struct ieee80211_ht_operation *ht_oper = NULL; + struct ieee80211_vht_operation *vht_oper = NULL; + u8 seg0, seg1; + + ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION); + if (ie && ie[1] >= sizeof(struct ieee80211_ht_operation)) { + u8 sec_chan_offset; + + ht_oper = (struct ieee80211_ht_operation *) (ie + 2); + sec_chan_offset = ht_oper->ht_param & + HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; + if (sec_chan_offset == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) + sec_chan = 1; + else if (sec_chan_offset == + HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) + sec_chan = -1; + } + + ie = get_ie(ies, ies_len, WLAN_EID_VHT_OPERATION); + if (ie && ie[1] >= sizeof(struct ieee80211_vht_operation)) { + vht_oper = (struct ieee80211_vht_operation *) (ie + 2); + + switch (vht_oper->vht_op_info_chwidth) { + case 1: + seg0 = vht_oper->vht_op_info_chan_center_freq_seg0_idx; + seg1 = vht_oper->vht_op_info_chan_center_freq_seg1_idx; + if (seg1 && abs(seg1 - seg0) == 8) + vht = VHT_CHANWIDTH_160MHZ; + else if (seg1) + vht = VHT_CHANWIDTH_80P80MHZ; + else + vht = VHT_CHANWIDTH_80MHZ; + break; + case 2: + vht = VHT_CHANWIDTH_160MHZ; + break; + case 3: + vht = VHT_CHANWIDTH_80P80MHZ; + break; + default: + vht = VHT_CHANWIDTH_USE_HT; + break; + } + } + + if (ieee80211_freq_to_channel_ext(freq, sec_chan, vht, op_class, + chan) == NUM_HOSTAPD_MODES) { + wpa_printf(MSG_DEBUG, + "Cannot determine operating class and channel"); + return -1; + } + + *phy_type = ieee80211_get_phy_type(freq, ht_oper != NULL, + vht_oper != NULL); + if (*phy_type == PHY_TYPE_UNSPECIFIED) { + wpa_printf(MSG_DEBUG, "Cannot determine phy type"); + return -1; + } + + return 0; +} + + +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) +{ + u8 *ies = (u8 *) (bss + 1); + size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len; + u8 *pos = buf; + int rem_len; + + rem_len = 255 - sizeof(struct rrm_measurement_beacon_report) - + sizeof(struct rrm_measurement_report_element) - 2; + + if (detail > BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS) { + wpa_printf(MSG_DEBUG, + "Beacon Request: Invalid reporting detail: %d", + detail); + return -1; + } + + if (detail == BEACON_REPORT_DETAIL_NONE) + return 0; + + /* + * Minimal frame body subelement size: EID(1) + length(1) + TSF(8) + + * beacon interval(2) + capabilities(2) = 14 bytes + */ + if (buf_len < 14) + return 0; + + *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; + + rem_len -= pos - buf; + + /* + * According to IEEE Std 802.11-2016, 9.4.2.22.7, if the reported frame + * body subelement causes the element to exceed the maximum element + * size, the subelement is truncated so that the last IE is a complete + * IE. So even when required to report all IEs, add elements one after + * the other and stop once there is no more room in the measurement + * element. + */ + 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. + */ + + if (2 + elen > buf + buf_len - pos || + 2 + elen > rem_len) + break; + + *pos++ = ies[0]; + *pos++ = elen; + os_memcpy(pos, ies + 2, elen); + pos += elen; + rem_len -= 2 + elen; + } + + ies_len -= 2 + ies[1]; + ies += 2 + ies[1]; + } + + /* Now the length is known */ + buf[1] = pos - buf - 2; + return pos - buf; +} + + +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[2000]; + struct rrm_measurement_beacon_report *rep; + + if (os_memcmp(data->bssid, broadcast_ether_addr, ETH_ALEN) != 0 && + os_memcmp(data->bssid, bss->bssid, ETH_ALEN) != 0) + return 0; + + if (data->ssid_len && + (data->ssid_len != bss->ssid_len || + os_memcmp(data->ssid, bss->ssid, bss->ssid_len) != 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) + return 0; + + 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); + + ret = wpas_beacon_rep_add_frame_body(data->eids, data->report_detail, + bss, rep->variable, + sizeof(buf) - sizeof(*rep)); + if (ret < 0) + return -1; + + if (wpabuf_resize(wpa_buf, + sizeof(struct rrm_measurement_report_element) + + sizeof(*rep) + ret)) { + wpa_printf(MSG_ERROR, "RRM: Memory allocation failed"); + return -1; + } + + return wpas_rrm_report_elem(*wpa_buf, wpa_s->beacon_rep_data.token, + MEASUREMENT_REPORT_MODE_ACCEPT, + MEASURE_TYPE_BEACON, buf, + ret + sizeof(*rep)); +} + + +static int wpas_beacon_rep_no_results(struct wpa_supplicant *wpa_s, + struct wpabuf **buf) +{ + if (wpabuf_resize(buf, 5)) { + wpa_printf(MSG_DEBUG, "RRM: Memory allocation failed"); + return -1; + } + + return wpas_rrm_report_elem(*buf, wpa_s->beacon_rep_data.token, + MEASUREMENT_REPORT_MODE_ACCEPT, + MEASURE_TYPE_BEACON, NULL, 0); +} + + +static void wpas_beacon_rep_table(struct wpa_supplicant *wpa_s, + struct wpabuf **buf) +{ + size_t i; + + for (i = 0; i < wpa_s->last_scan_res_used; i++) { + if (wpas_add_beacon_rep(wpa_s, buf, wpa_s->last_scan_res[i], + 0, 0) < 0) + break; + } + + if (!(*buf)) + wpas_beacon_rep_no_results(wpa_s, buf); + + wpa_hexdump_buf(MSG_DEBUG, "RRM: Radio Measurement report", *buf); +} + + +static void wpas_rrm_refuse_request(struct wpa_supplicant *wpa_s) +{ + struct wpabuf *buf; + + buf = wpabuf_alloc(sizeof(struct rrm_measurement_beacon_report)); + if (!buf || + wpas_rrm_report_elem(buf, wpa_s->beacon_rep_data.token, + MEASUREMENT_REPORT_MODE_REJECT_REFUSED, + MEASURE_TYPE_BEACON, NULL, 0)) { + wpa_printf(MSG_ERROR, "RRM: Memory allocation failed"); + wpabuf_free(buf); + return; + } + + wpas_rrm_send_msr_report(wpa_s, buf); + wpas_clear_beacon_rep_data(wpa_s); + wpabuf_free(buf); +} + + +static void wpas_rrm_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + struct wpa_driver_scan_params *params = + &wpa_s->beacon_rep_data.scan_params; + + if (!wpa_s->current_bss) + return; + + if (wpa_s->scanning || wpas_p2p_in_progress(wpa_s) || + wpa_supplicant_trigger_scan(wpa_s, params)) + wpas_rrm_refuse_request(wpa_s); +} + + +static int wpas_rm_handle_beacon_req_subelem(struct wpa_supplicant *wpa_s, + struct beacon_rep_data *data, + u8 sid, u8 slen, const u8 *subelem) +{ + u8 report_info, i; + + switch (sid) { + case WLAN_BEACON_REQUEST_SUBELEM_SSID: + if (!slen) { + wpa_printf(MSG_DEBUG, + "SSID subelement with zero length - wildcard SSID"); + break; + } + + if (slen > SSID_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "Invalid SSID subelement length: %u", slen); + return -1; + } + + data->ssid_len = slen; + os_memcpy(data->ssid, subelem, data->ssid_len); + break; + case WLAN_BEACON_REQUEST_SUBELEM_INFO: + if (slen != 2) { + wpa_printf(MSG_DEBUG, + "Invalid reporting information subelement length: %u", + slen); + return -1; + } + + report_info = subelem[0]; + if (report_info != 0) { + wpa_printf(MSG_DEBUG, + "reporting information=%u is not supported", + report_info); + return 0; + } + break; + case WLAN_BEACON_REQUEST_SUBELEM_DETAIL: + if (slen != 1) { + wpa_printf(MSG_DEBUG, + "Invalid reporting detail subelement length: %u", + slen); + return -1; + } + + data->report_detail = subelem[0]; + if (data->report_detail > + BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS) { + wpa_printf(MSG_DEBUG, "Invalid reporting detail: %u", + subelem[0]); + return 0; + } + + break; + case WLAN_BEACON_REQUEST_SUBELEM_REQUEST: + if (data->report_detail != + BEACON_REPORT_DETAIL_REQUESTED_ONLY) { + wpa_printf(MSG_DEBUG, + "Beacon request: request subelement is present but report detail is %u", + data->report_detail); + return -1; + } + + if (!slen) { + wpa_printf(MSG_DEBUG, + "Invalid request subelement length: %u", + slen); + return -1; + } + + if (data->eids) { + wpa_printf(MSG_DEBUG, + "Beacon Request: Request subelement appears more than once"); + return -1; + } + + data->eids = bitfield_alloc(255); + if (!data->eids) { + wpa_printf(MSG_DEBUG, "Failed to allocate EIDs bitmap"); + return -1; + } + + for (i = 0; i < slen; i++) + bitfield_set(data->eids, subelem[i]); + break; + case WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL: + /* Skip - it will be processed when freqs are added */ + break; + default: + wpa_printf(MSG_DEBUG, + "Beacon request: Unknown subelement id %u", sid); + break; + } + + return 1; +} + + +/** + * Returns 0 if the next element can be processed, 1 if some operation was + * triggered, and -1 if processing failed (i.e., the element is in invalid + * format or an internal error occurred). + */ +static int +wpas_rm_handle_beacon_req(struct wpa_supplicant *wpa_s, + u8 elem_token, int duration_mandatory, + const struct rrm_measurement_beacon_request *req, + size_t len, struct wpabuf **buf) +{ + struct beacon_rep_data *data = &wpa_s->beacon_rep_data; + struct wpa_driver_scan_params *params = &data->scan_params; + const u8 *subelems; + size_t elems_len; + u16 rand_interval; + u32 interval_usec; + u32 _rand; + int ret = 0, res; + + if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT)) + return 0; + + if (len < sizeof(*req)) + return -1; + subelems = req->variable; + elems_len = len - sizeof(*req); + rand_interval = le_to_host16(req->rand_interval); + + os_memset(params, 0, sizeof(*params)); + + data->token = elem_token; + + /* default reporting detail is all fixed length fields and all + * elements */ + data->report_detail = BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS; + os_memcpy(data->bssid, req->bssid, ETH_ALEN); + + while (elems_len >= 2) { + if (subelems[1] > elems_len - 2) { + wpa_printf(MSG_DEBUG, + "Beacon Request: Truncated subelement"); + ret = -1; + goto out; + } + + res = wpas_rm_handle_beacon_req_subelem( + wpa_s, data, subelems[0], subelems[1], &subelems[2]); + if (res != 1) { + ret = res; + goto out; + } + + elems_len -= 2 + subelems[1]; + subelems += 2 + subelems[1]; + } + + if (req->mode == BEACON_REPORT_MODE_TABLE) { + wpas_beacon_rep_table(wpa_s, buf); + goto out; + } + + params->freqs = wpas_beacon_request_freqs( + wpa_s, req->oper_class, req->channel, + req->mode == BEACON_REPORT_MODE_ACTIVE, + req->variable, len - sizeof(*req)); + if (!params->freqs) { + wpa_printf(MSG_DEBUG, "Beacon request: No valid channels"); + goto out; + } + + params->duration = le_to_host16(req->duration); + params->duration_mandatory = duration_mandatory; + if (!params->duration) { + wpa_printf(MSG_DEBUG, "Beacon request: Duration is 0"); + ret = -1; + goto out; + } + + params->only_new_results = 1; + + if (req->mode == BEACON_REPORT_MODE_ACTIVE) { + params->ssids[params->num_ssids].ssid = data->ssid; + params->ssids[params->num_ssids++].ssid_len = data->ssid_len; + } + + if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0) + _rand = os_random(); + interval_usec = (_rand % (rand_interval + 1)) * 1024; + eloop_register_timeout(0, interval_usec, wpas_rrm_scan_timeout, wpa_s, + NULL); + return 1; +out: + wpas_clear_beacon_rep_data(wpa_s); + return ret; +} + + static int wpas_rrm_handle_msr_req_element( struct wpa_supplicant *wpa_s, const struct rrm_measurement_request_element *req, struct wpabuf **buf) { + int duration_mandatory; + wpa_printf(MSG_DEBUG, "Measurement request type %d token %d", req->type, req->token); @@ -446,9 +1133,17 @@ wpas_rrm_handle_msr_req_element( goto reject; } + duration_mandatory = + !!(req->mode & MEASUREMENT_REQUEST_MODE_DURATION_MANDATORY); + switch (req->type) { case MEASURE_TYPE_LCI: return wpas_rrm_build_lci_report(wpa_s, req, buf); + case MEASURE_TYPE_BEACON: + return wpas_rm_handle_beacon_req(wpa_s, req->token, + duration_mandatory, + (const void *) req->variable, + req->len - 3, buf); default: wpa_printf(MSG_INFO, "RRM: Unsupported radio measurement type %u", @@ -622,3 +1317,75 @@ void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, } wpabuf_free(buf); } + + +int wpas_beacon_rep_scan_process(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res, + struct scan_info *info) +{ + size_t i = 0; + struct wpabuf *buf = NULL; + + if (!wpa_s->beacon_rep_data.token) + return 0; + + if (!wpa_s->current_bss) + goto out; + + /* If the measurement was aborted, don't report partial results */ + if (info->aborted) + goto out; + + wpa_printf(MSG_DEBUG, "RRM: TSF BSSID: " MACSTR " current BSS: " MACSTR, + MAC2STR(info->scan_start_tsf_bssid), + MAC2STR(wpa_s->current_bss->bssid)); + if (os_memcmp(info->scan_start_tsf_bssid, wpa_s->current_bss->bssid, + ETH_ALEN) != 0) + goto out; + + for (i = 0; i < scan_res->num; i++) { + struct wpa_bss *bss = + wpa_bss_get_bssid(wpa_s, scan_res->res[i]->bssid); + + if (!bss) + continue; + + if (os_memcmp(scan_res->res[i]->tsf_bssid, + wpa_s->current_bss->bssid, ETH_ALEN) != 0) + continue; + + /* + * Don't report results that were not received during the + * current measurement. + */ + if (info->scan_start_tsf > scan_res->res[i]->parent_tsf) + continue; + + if (wpas_add_beacon_rep(wpa_s, &buf, bss, info->scan_start_tsf, + scan_res->res[i]->parent_tsf) < 0) + break; + } + + if (!buf && wpas_beacon_rep_no_results(wpa_s, &buf)) + goto out; + + wpa_hexdump_buf(MSG_DEBUG, "RRM: Radio Measurement report", buf); + + wpas_rrm_send_msr_report(wpa_s, buf); + wpabuf_free(buf); + +out: + wpas_clear_beacon_rep_data(wpa_s); + return 1; +} + + +void wpas_clear_beacon_rep_data(struct wpa_supplicant *wpa_s) +{ + struct beacon_rep_data *data = &wpa_s->beacon_rep_data; + + eloop_cancel_timeout(wpas_rrm_scan_timeout, wpa_s, NULL); + bitfield_free(data->eids); + os_free(data->scan_params.freqs); + os_memset(data, 0, sizeof(*data)); +} diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 133ceec96..ce116f640 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -187,6 +187,11 @@ static void sme_auth_handle_rrm(struct wpa_supplicant *wpa_s, if (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION) *pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT; + if (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT) + *pos |= WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE | + WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE | + WLAN_RRM_CAPS_BEACON_REPORT_TABLE; + if (wpa_s->lci) pos[1] |= WLAN_RRM_CAPS_LCI_MEASUREMENT; diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 2a8cd52c9..53e122bca 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -585,6 +585,7 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) wpabuf_free(wpa_s->lci); wpa_s->lci = NULL; + wpas_clear_beacon_rep_data(wpa_s); } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index b5b7d7e34..d911402c9 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -9,6 +9,7 @@ #ifndef WPA_SUPPLICANT_I_H #define WPA_SUPPLICANT_I_H +#include "utils/bitfield.h" #include "utils/list.h" #include "common/defs.h" #include "common/sae.h" @@ -449,6 +450,17 @@ struct wpa_bss_tmp_disallowed { struct os_reltime disallowed_until; }; +struct beacon_rep_data { + u8 token; + struct wpa_driver_scan_params scan_params; + u8 ssid[SSID_MAX_LEN]; + size_t ssid_len; + u8 bssid[ETH_ALEN]; + enum beacon_report_detail report_detail; + struct bitfield *eids; +}; + + /** * struct wpa_supplicant - Internal data for wpa_supplicant interface * @@ -1048,6 +1060,7 @@ struct wpa_supplicant { u8 last_tspecs_count; struct rrm_data rrm; + struct beacon_rep_data beacon_rep_data; #ifdef CONFIG_FST struct fst_iface *fst; @@ -1199,6 +1212,10 @@ void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *frame, size_t len, int rssi); +int wpas_beacon_rep_scan_process(struct wpa_supplicant *wpa_s, + struct wpa_scan_results *scan_res, + struct scan_info *info); +void wpas_clear_beacon_rep_data(struct wpa_supplicant *wpa_s); /* MBO functions */