From: Avraham Stern <avraham.stern@xxxxxxxxx> Beacon Report Radio Measurement is defined in IEEE802.11REVmc_D5.0 section 11.11.9.1. Beacon Report is implemented by triggering scan on the requested channels with the requested parameters. Signed-off-by: Avraham Stern <avraham.stern@xxxxxxxxx> --- src/common/ieee802_11_defs.h | 19 + wpa_supplicant/Makefile | 2 +- wpa_supplicant/events.c | 4 + wpa_supplicant/rrm.c | 721 ++++++++++++++++++++++++++++++++++++++ wpa_supplicant/scan.c | 2 + wpa_supplicant/sme.c | 5 + wpa_supplicant/wpa_supplicant.c | 1 + wpa_supplicant/wpa_supplicant_i.h | 17 + 8 files changed, 770 insertions(+), 1 deletion(-) diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h index 0e3a5ac..85c2023 100644 --- a/src/common/ieee802_11_defs.h +++ b/src/common/ieee802_11_defs.h @@ -743,6 +743,25 @@ enum lci_req_subelem { #define FILS_CACHE_ID_LEN 2 #define FILS_MAX_KEY_AUTH_LEN 48 +/* + * IEEE P802.11-REVmc/D5.0 Table 9-89 - Reporting Detail values + */ +enum beacon_report_detail { + BEACON_REPORT_DETAIL_NONE, + BEACON_REPORT_DETAIL_REQUESTED_ONLY, + BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS, +}; + +/* + * IEEE P802.11-REVmc/D5.0 Table 9-86 - Measurement Mode definitions for Beacon + * request + */ +enum beacon_report_mode { + BEACON_REPORT_MODE_PASSIVE, + BEACON_REPORT_MODE_ACTIVE, + BEACON_REPORT_MODE_TABLE, +}; + #ifdef _MSC_VER #pragma pack(push, 1) #endif /* _MSC_VER */ diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index 7b5b7cd..eca72df 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -114,6 +114,7 @@ OBJS_c += ../src/utils/common.o OBJS_c += ../src/common/cli.o OBJS += wmm_ac.o OBJS += rrm.o +OBJS += ../src/utils/bitfield.o ifndef CONFIG_OS ifdef CONFIG_NATIVE_WINDOWS @@ -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 diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index a57b1da..afdda00 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 69f7df9..fde6ce8 100644 --- a/wpa_supplicant/rrm.c +++ b/wpa_supplicant/rrm.c @@ -13,6 +13,9 @@ #include "driver_i.h" #include "bss.h" #include "common/ieee802_11_common.h" +#include "scan.h" +#include "p2p_supplicant.h" +#include "common/hw_features_common.h" static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx) @@ -46,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); } @@ -422,11 +426,647 @@ 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 = 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_zalloc((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+80MHz operating + * classes are like 80MHz 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; + + while (len > 2) { + const struct oper_class_map *op; + const u8 *ap_channel_elem = + get_ie(subelems, len, + WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL); + struct hostapd_hw_modes *mode; + + if (!ap_channel_elem) + break; + + op = get_oper_class(country, ap_channel_elem[2]); + if (!op) { + wpa_printf(MSG_DEBUG, + "Beacon request: invalid operating class in AP Channel Report subelement %d", + ap_channel_elem[2]); + goto out; + } + + len -= ap_channel_elem[1] + 2 + (ap_channel_elem - subelems); + subelems = ap_channel_elem + 2 + ap_channel_elem[1]; + + mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op->mode); + if (!mode) + continue; + + /* + * For 80+80MHz operating classes, this AP Channel Report + * element should be followed by another element specifying + * the second 80MHz channel. For now just add this 80MHz + * channel, the second 80Mhz 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 + * IEEE802.11-REVmcD5.0 section 11.11.9.1. + */ + new_freqs = wpas_add_channels(op, mode, active, + ap_channel_elem + 3, + ap_channel_elem[1] - 1); + 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 = 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; + + ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION); + if (ie) { + ht_oper = (struct ieee80211_ht_operation *)(ie + 2); + + if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) + sec_chan = 1; + else if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) + sec_chan = -1; + } + + ie = get_ie(ies, ies_len, WLAN_EID_VHT_OPERATION); + if (ie) { + vht_oper = (struct ieee80211_vht_operation *)(ie + 2); + + switch (vht_oper->vht_op_info_chwidth) { + case 2: + vht = VHT_CHANWIDTH_80MHZ; + break; + case 3: + vht = VHT_CHANWIDTH_160MHZ; + break; + case 4: + vht = VHT_CHANWIDTH_80P80MHZ; + break; + default: + vht = VHT_CHANWIDTH_USE_HT; + } + } + + 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 = 255 - sizeof(struct rrm_measurement_beacon_report) - + sizeof(struct rrm_measurement_report_element) + 2; + + if (detail > 2) { + 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 += 2; + 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 IEEE802.11REVmc_D5.0, section 9.4.2.22.7, if the + * reported frame body subelement causes the element to exceed the + * maximum element size, it should be 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 when there is no more room in the measurement + * element. + */ + while (ies_len > 2 && rem_len) { + if (detail == BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS || + bitfield_is_set(eids, ies[0])) { + if ((ies[1] + 2 > buf + buf_len - pos) || + ies[1] + 2 > rem_len) + break; + + os_memcpy(pos, ies, ies[1] + 2); + pos += ies[1] + 2; + rem_len -= ies[1] + 2; + } + + ies_len -= ies[1] + 2; + ies += ies[1] + 2; + } + + /* TODO: + * Truncate some IEs as specified in IEEE Std 802.11-2012, 8.4.2.24.7 */ + + /* 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) && + os_memcmp(data->bssid, bss->bssid, ETH_ALEN)) + return 0; + + if (data->ssid_len && (data->ssid_len != bss->ssid_len || + os_memcmp(data->ssid, bss->ssid, bss->ssid_len))) + return 0; + + rep = (void *)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], (u64)0, + (u64)0) < 0) + break; + } + + if (!(*buf)) + wpas_beacon_rep_no_results(wpa_s, buf); + + wpa_hexdump(MSG_DEBUG, "RRM: Radio Measurement report:", + wpabuf_head(*buf), wpabuf_len(*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)) { + struct wpabuf *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_REFUSE, + MEASURE_TYPE_BEACON, NULL, 0)) { + wpa_printf(MSG_ERROR, "RRM: Memory allocation failed"); + return; + } + + wpas_rrm_send_msr_report(wpa_s, buf); + wpas_clear_beacon_rep_data(wpa_s); + wpabuf_free(buf); + } +} + + +/** + * 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, + 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; + u8 *subelems = req->variable; + size_t elems_len = len - sizeof(*req); + u16 rand_interval = le_to_host16(req->rand_interval); + u32 interval_usec; + u32 _rand; + u8 report_info; + u8 i; + int ret = 0; + + if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT)) + return 0; + + 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; + } + + switch (subelems[0]) { + case WLAN_BEACON_REQUEST_SUBELEM_SSID: + if (!subelems[1]) { + wpa_printf(MSG_DEBUG, + "SSID subelement with zero length - wildcard SSID"); + break; + } + + if (subelems[1] > SSID_MAX_LEN) { + wpa_printf(MSG_DEBUG, + "Invalid SSID suelement length: %u", + subelems[1]); + ret = -1; + goto out; + } + + data->ssid_len = subelems[1]; + os_memcpy(data->ssid, subelems + 2, data->ssid_len); + break; + case WLAN_BEACON_REQUEST_SUBELEM_INFO: + if (subelems[1] != 2) { + wpa_printf(MSG_DEBUG, + "Invalid reporting information subelement length: %u", + subelems[1]); + ret = -1; + goto out; + } + + report_info = subelems[2]; + if (report_info != 0) { + wpa_printf(MSG_DEBUG, + "reporting information=%u is not supported", + report_info); + goto out; + } + + break; + case WLAN_BEACON_REQUEST_SUBELEM_DETAIL: + if (subelems[1] != 1) { + wpa_printf(MSG_DEBUG, + "Invalid reporting detail subelement length: %u", + subelems[1]); + ret = -1; + goto out; + } + + data->report_detail = subelems[2]; + if (data->report_detail > + BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS) { + wpa_printf(MSG_DEBUG, + "Invalid reporting detail: %u", + subelems[2]); + goto out; + } + + 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); + ret = -1; + goto out; + } + + if (!subelems[1]) { + wpa_printf(MSG_DEBUG, + "Invalid request subelement length: %u", + subelems[0]); + ret = -1; + goto out; + } + + if (data->eids) { + wpa_printf(MSG_DEBUG, + "Beacon Request: Request subelement appears more than once"); + ret = -1; + goto out; + } + + data->eids = bitfield_alloc(255); + if (!data->eids) { + wpa_printf(MSG_DEBUG, + "Failed to allocate EIDs bitmap"); + ret = -1; + goto out; + } + + for (i = 0; i < subelems[1]; i++) + bitfield_set(data->eids, subelems[i + 2]); + + 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", + subelems[0]); + } + + 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, struct rrm_measurement_request_element *req, struct wpabuf **buf) { + u8 duration_mandatory; + wpa_printf(MSG_DEBUG, "Measurement request type %d token %d", req->type, req->token); @@ -446,9 +1086,17 @@ wpas_rrm_handle_msr_req_element(struct wpa_supplicant *wpa_s, 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, + (void *)req->variable, + req->len - 3, buf); default: wpa_printf(MSG_INFO, "RRM: unsupported radio measurement type %hhu", @@ -622,3 +1270,76 @@ 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, "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)) + 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)) + 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(MSG_DEBUG, "RRM: Radio Measurement report:", + wpabuf_head(buf), wpabuf_len(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/scan.c b/wpa_supplicant/scan.c index def2977..ea9b94b 100644 --- a/wpa_supplicant/scan.c +++ b/wpa_supplicant/scan.c @@ -2348,6 +2348,8 @@ wpa_scan_clone_params(const struct wpa_driver_scan_params *src) params->p2p_probe = src->p2p_probe; params->only_new_results = src->only_new_results; params->low_priority = src->low_priority; + params->duration = src->duration; + params->duration_mandatory = src->duration_mandatory; if (src->sched_scan_plans_num > 0) { params->sched_scan_plans = diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index 133ceec..ce116f6 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 2a8cd52..53e122b 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 b182dda..0aab202 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -16,6 +16,7 @@ #include "wps/wps_defs.h" #include "config_ssid.h" #include "wmm_ac.h" +#include "utils/bitfield.h" extern const char *const wpa_supplicant_version; extern const char *const wpa_supplicant_license; @@ -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; @@ -1310,4 +1323,8 @@ struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, struct wpa_ssid *group, int only_first_ssid, int debug_print); +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); #endif /* WPA_SUPPLICANT_I_H */ -- 1.9.1 _______________________________________________ Hostap mailing list Hostap@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/hostap