diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk index e7b745787..43ed84701 100644 --- a/wpa_supplicant/Android.mk +++ b/wpa_supplicant/Android.mk @@ -94,6 +94,7 @@ OBJS += src/utils/wpa_debug.c OBJS += src/utils/wpabuf.c OBJS += wmm_ac.c OBJS += op_classes.c +OBJS += rrm.c OBJS_p = wpa_passphrase.c OBJS_p += src/utils/common.c OBJS_p += src/utils/wpa_debug.c diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index e9e83be5e..a7d8310d1 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -104,6 +104,7 @@ OBJS += ../src/utils/common.o OBJS += ../src/utils/wpa_debug.o OBJS += ../src/utils/wpabuf.o OBJS += op_classes.o +OBJS += rrm.o OBJS_p = wpa_passphrase.o OBJS_p += ../src/utils/common.o OBJS_p += ../src/utils/wpa_debug.o diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c new file mode 100644 index 000000000..de7d7e98c --- /dev/null +++ b/wpa_supplicant/rrm.c @@ -0,0 +1,500 @@ +/* + * wpa_supplicant - Radio Measurements + * Copyright (c) 2003-2016, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_common.h" +#include "wpa_supplicant_i.h" +#include "driver_i.h" +#include "bss.h" + + +static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx) +{ + struct rrm_data *rrm = data; + + if (!rrm->notify_neighbor_rep) { + wpa_printf(MSG_ERROR, + "RRM: Unexpected neighbor report timeout"); + return; + } + + wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE"); + rrm->notify_neighbor_rep(rrm->neighbor_rep_cb_ctx, NULL); + + rrm->notify_neighbor_rep = NULL; + rrm->neighbor_rep_cb_ctx = NULL; +} + + +/* + * wpas_rrm_reset - Clear and reset all RRM data in wpa_supplicant + * @wpa_s: Pointer to wpa_supplicant + */ +void wpas_rrm_reset(struct wpa_supplicant *wpa_s) +{ + wpa_s->rrm.rrm_used = 0; + + eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm, + NULL); + 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_rrm_process_neighbor_rep - Handle incoming neighbor report + * @wpa_s: Pointer to wpa_supplicant + * @report: Neighbor report buffer, prefixed by a 1-byte dialog token + * @report_len: Length of neighbor report buffer + */ +void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s, + const u8 *report, size_t report_len) +{ + struct wpabuf *neighbor_rep; + + wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len); + if (report_len < 1) + return; + + if (report[0] != wpa_s->rrm.next_neighbor_rep_token - 1) { + wpa_printf(MSG_DEBUG, + "RRM: Discarding neighbor report with token %d (expected %d)", + report[0], wpa_s->rrm.next_neighbor_rep_token - 1); + return; + } + + eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm, + NULL); + + if (!wpa_s->rrm.notify_neighbor_rep) { + wpa_printf(MSG_ERROR, "RRM: Unexpected neighbor report"); + return; + } + + /* skipping the first byte, which is only an id (dialog token) */ + neighbor_rep = wpabuf_alloc(report_len - 1); + if (neighbor_rep == NULL) + return; + wpabuf_put_data(neighbor_rep, report + 1, report_len - 1); + wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)", + report[0]); + wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx, + neighbor_rep); + wpa_s->rrm.notify_neighbor_rep = NULL; + wpa_s->rrm.neighbor_rep_cb_ctx = NULL; +} + + +#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS) +/* Workaround different, undefined for Windows, error codes used here */ +#define ENOTCONN -1 +#define EOPNOTSUPP -1 +#define ECANCELED -1 +#endif + +/* Measurement Request element + Location Subject + Maximum Age subelement */ +#define MEASURE_REQUEST_LCI_LEN (3 + 1 + 4) +/* Measurement Request element + Location Civic Request */ +#define MEASURE_REQUEST_CIVIC_LEN (3 + 5) + + +/** + * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP + * @wpa_s: Pointer to wpa_supplicant + * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE + * is sent in the request. + * @lci: if set, neighbor request will include LCI request + * @civic: if set, neighbor request will include civic location request + * @cb: Callback function to be called once the requested report arrives, or + * timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds. + * In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's + * the requester's responsibility to free it. + * In the latter case NULL will be sent in 'neighbor_rep'. + * @cb_ctx: Context value to send the callback function + * Returns: 0 in case of success, negative error code otherwise + * + * In case there is a previous request which has not been answered yet, the + * new request fails. The caller may retry after RRM_NEIGHBOR_REPORT_TIMEOUT. + * Request must contain a callback function. + */ +int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, + const struct wpa_ssid_value *ssid, + int lci, int civic, + void (*cb)(void *ctx, + struct wpabuf *neighbor_rep), + void *cb_ctx) +{ + struct wpabuf *buf; + const u8 *rrm_ie; + + if (wpa_s->wpa_state != WPA_COMPLETED || wpa_s->current_ssid == NULL) { + wpa_printf(MSG_DEBUG, "RRM: No connection, no RRM."); + return -ENOTCONN; + } + + if (!wpa_s->rrm.rrm_used) { + wpa_printf(MSG_DEBUG, "RRM: No RRM in current connection."); + return -EOPNOTSUPP; + } + + rrm_ie = wpa_bss_get_ie(wpa_s->current_bss, + WLAN_EID_RRM_ENABLED_CAPABILITIES); + if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) || + !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) { + wpa_printf(MSG_DEBUG, + "RRM: No network support for Neighbor Report."); + return -EOPNOTSUPP; + } + + if (!cb) { + wpa_printf(MSG_DEBUG, + "RRM: Neighbor Report request must provide a callback."); + return -EINVAL; + } + + /* Refuse if there's a live request */ + if (wpa_s->rrm.notify_neighbor_rep) { + wpa_printf(MSG_DEBUG, + "RRM: Currently handling previous Neighbor Report."); + return -EBUSY; + } + + /* 3 = action category + action code + dialog token */ + buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0) + + (lci ? 2 + MEASURE_REQUEST_LCI_LEN : 0) + + (civic ? 2 + MEASURE_REQUEST_CIVIC_LEN : 0)); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, + "RRM: Failed to allocate Neighbor Report Request"); + return -ENOMEM; + } + + wpa_printf(MSG_DEBUG, "RRM: Neighbor report request (for %s), token=%d", + (ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""), + wpa_s->rrm.next_neighbor_rep_token); + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST); + wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token); + if (ssid) { + wpabuf_put_u8(buf, WLAN_EID_SSID); + wpabuf_put_u8(buf, ssid->ssid_len); + wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len); + } + + if (lci) { + /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */ + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + wpabuf_put_u8(buf, MEASURE_REQUEST_LCI_LEN); + + /* + * Measurement token; nonzero number that is unique among the + * Measurement Request elements in a particular frame. + */ + wpabuf_put_u8(buf, 1); /* Measurement Token */ + + /* + * Parallel, Enable, Request, and Report bits are 0, Duration is + * reserved. + */ + wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ + wpabuf_put_u8(buf, MEASURE_TYPE_LCI); /* Measurement Type */ + + /* IEEE P802.11-REVmc/D5.0 9.4.2.21.10 - LCI request */ + /* Location Subject */ + wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); + + /* Optional Subelements */ + /* + * IEEE P802.11-REVmc/D5.0 Figure 9-170 + * The Maximum Age subelement is required, otherwise the AP can + * send only data that was determined after receiving the + * request. Setting it here to unlimited age. + */ + wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE); + wpabuf_put_u8(buf, 2); + wpabuf_put_le16(buf, 0xffff); + } + + if (civic) { + /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */ + wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); + wpabuf_put_u8(buf, MEASURE_REQUEST_CIVIC_LEN); + + /* + * Measurement token; nonzero number that is unique among the + * Measurement Request elements in a particular frame. + */ + wpabuf_put_u8(buf, 2); /* Measurement Token */ + + /* + * Parallel, Enable, Request, and Report bits are 0, Duration is + * reserved. + */ + wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ + /* Measurement Type */ + wpabuf_put_u8(buf, MEASURE_TYPE_LOCATION_CIVIC); + + /* IEEE P802.11-REVmc/D5.0 9.4.2.21.14: + * Location Civic request */ + /* Location Subject */ + wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); + wpabuf_put_u8(buf, 0); /* Civic Location Type: IETF RFC 4776 */ + /* Location Service Interval Units: Seconds */ + wpabuf_put_u8(buf, 0); + /* Location Service Interval: 0 - Only one report is requested + */ + wpabuf_put_le16(buf, 0); + /* No optional subelements */ + } + + wpa_s->rrm.next_neighbor_rep_token++; + + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0) < 0) { + wpa_printf(MSG_DEBUG, + "RRM: Failed to send Neighbor Report Request"); + wpabuf_free(buf); + return -ECANCELED; + } + + wpa_s->rrm.neighbor_rep_cb_ctx = cb_ctx; + wpa_s->rrm.notify_neighbor_rep = cb; + eloop_register_timeout(RRM_NEIGHBOR_REPORT_TIMEOUT, 0, + wpas_rrm_neighbor_rep_timeout_handler, + &wpa_s->rrm, NULL); + + wpabuf_free(buf); + return 0; +} + + +static struct wpabuf * wpas_rrm_build_lci_report(struct wpa_supplicant *wpa_s, + const u8 *request, size_t len, + struct wpabuf *report) +{ + u8 token, type, subject; + u16 max_age = 0; + struct os_reltime t, diff; + unsigned long diff_l; + u8 *ptoken; + const u8 *subelem; + + if (!wpa_s->lci || len < 3 + 4) + return report; + + token = *request++; + /* Measurement request mode isn't used */ + request++; + type = *request++; + subject = *request++; + len -= 4; + + wpa_printf(MSG_DEBUG, + "Measurement request token %u type %u location subject %u", + token, type, subject); + + if (type != MEASURE_TYPE_LCI || subject != LOCATION_SUBJECT_REMOTE) { + wpa_printf(MSG_INFO, + "Not building LCI report - bad type or location subject"); + return report; + } + + /* Subelements are formatted exactly like elements */ + subelem = get_ie(request, len, LCI_REQ_SUBELEM_MAX_AGE); + if (subelem && subelem[1] == 2) + max_age = WPA_GET_LE16(subelem + 2); + + if (os_get_reltime(&t)) + return report; + + os_reltime_sub(&t, &wpa_s->lci_time, &diff); + /* LCI age is calculated in 10th of a second units. */ + diff_l = diff.sec * 10 + diff.usec / 100000; + + if (max_age != 0xffff && max_age < diff_l) + return report; + + if (wpabuf_resize(&report, 2 + wpabuf_len(wpa_s->lci))) + return report; + + wpabuf_put_u8(report, WLAN_EID_MEASURE_REPORT); + wpabuf_put_u8(report, wpabuf_len(wpa_s->lci)); + /* We'll override user's measurement token */ + ptoken = wpabuf_put(report, 0); + wpabuf_put_buf(report, wpa_s->lci); + *ptoken = token; + + return report; +} + + +void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s, + const u8 *src, + const u8 *frame, size_t len) +{ + struct wpabuf *buf, *report; + u8 token; + const u8 *ie, *end; + + if (wpa_s->wpa_state != WPA_COMPLETED) { + wpa_printf(MSG_INFO, + "RRM: Ignoring radio measurement request: Not associated"); + return; + } + + if (!wpa_s->rrm.rrm_used) { + wpa_printf(MSG_INFO, + "RRM: Ignoring radio measurement request: Not RRM network"); + return; + } + + if (len < 3) { + wpa_printf(MSG_INFO, + "RRM: Ignoring too short radio measurement request"); + return; + } + + end = frame + len; + + token = *frame++; + + /* Ignore number of repetitions because it's not used in LCI request */ + frame += 2; + + report = NULL; + while ((ie = get_ie(frame, end - frame, WLAN_EID_MEASURE_REQUEST)) && + ie[1] >= 3) { + u8 msmt_type; + + msmt_type = ie[4]; + wpa_printf(MSG_DEBUG, "RRM request %d", msmt_type); + + switch (msmt_type) { + case MEASURE_TYPE_LCI: + report = wpas_rrm_build_lci_report(wpa_s, ie + 2, ie[1], + report); + break; + default: + wpa_printf(MSG_INFO, + "RRM: Unsupported radio measurement request %d", + msmt_type); + break; + } + + frame = ie + ie[1] + 2; + } + + if (!report) + return; + + buf = wpabuf_alloc(3 + wpabuf_len(report)); + if (!buf) { + wpabuf_free(report); + return; + } + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REPORT); + wpabuf_put_u8(buf, token); + + wpabuf_put_buf(buf, report); + wpabuf_free(report); + + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0)) { + wpa_printf(MSG_ERROR, + "RRM: Radio measurement report failed: Sending Action frame failed"); + } + wpabuf_free(buf); +} + + +void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, + const u8 *src, + const u8 *frame, size_t len, + int rssi) +{ + struct wpabuf *buf; + const struct rrm_link_measurement_request *req; + struct rrm_link_measurement_report report; + + if (wpa_s->wpa_state != WPA_COMPLETED) { + wpa_printf(MSG_INFO, + "RRM: Ignoring link measurement request. Not associated"); + return; + } + + if (!wpa_s->rrm.rrm_used) { + wpa_printf(MSG_INFO, + "RRM: Ignoring link measurement request. Not RRM network"); + return; + } + + if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) { + wpa_printf(MSG_INFO, + "RRM: Measurement report failed. TX power insertion not supported"); + return; + } + + req = (const struct rrm_link_measurement_request *) frame; + if (len < sizeof(*req)) { + wpa_printf(MSG_INFO, + "RRM: Link measurement report failed. Request too short"); + return; + } + + os_memset(&report, 0, sizeof(report)); + report.tpc.eid = WLAN_EID_TPC_REPORT; + report.tpc.len = 2; + report.rsni = 255; /* 255 indicates that RSNI is not available */ + report.dialog_token = req->dialog_token; + + /* + * It's possible to estimate RCPI based on RSSI in dBm. This + * calculation will not reflect the correct value for high rates, + * but it's good enough for Action frames which are transmitted + * with up to 24 Mbps rates. + */ + if (!rssi) + report.rcpi = 255; /* not available */ + else if (rssi < -110) + report.rcpi = 0; + else if (rssi > 0) + report.rcpi = 220; + else + report.rcpi = (rssi + 110) * 2; + + /* action_category + action_code */ + buf = wpabuf_alloc(2 + sizeof(report)); + if (buf == NULL) { + wpa_printf(MSG_ERROR, + "RRM: Link measurement report failed. Buffer allocation failed"); + return; + } + + wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); + wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REPORT); + wpabuf_put_data(buf, &report, sizeof(report)); + wpa_hexdump(MSG_DEBUG, "RRM: Link measurement report:", + wpabuf_head(buf), wpabuf_len(buf)); + + if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src, + wpa_s->own_addr, wpa_s->bssid, + wpabuf_head(buf), wpabuf_len(buf), 0)) { + wpa_printf(MSG_ERROR, + "RRM: Link measurement report failed. Send action failed"); + } + wpabuf_free(buf); +} diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index bbb763e87..2a8cd52c9 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -6283,490 +6283,6 @@ int get_shared_radio_freqs(struct wpa_supplicant *wpa_s, } -static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx) -{ - struct rrm_data *rrm = data; - - if (!rrm->notify_neighbor_rep) { - wpa_printf(MSG_ERROR, - "RRM: Unexpected neighbor report timeout"); - return; - } - - wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE"); - rrm->notify_neighbor_rep(rrm->neighbor_rep_cb_ctx, NULL); - - rrm->notify_neighbor_rep = NULL; - rrm->neighbor_rep_cb_ctx = NULL; -} - - -/* - * wpas_rrm_reset - Clear and reset all RRM data in wpa_supplicant - * @wpa_s: Pointer to wpa_supplicant - */ -void wpas_rrm_reset(struct wpa_supplicant *wpa_s) -{ - wpa_s->rrm.rrm_used = 0; - - eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm, - NULL); - 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_rrm_process_neighbor_rep - Handle incoming neighbor report - * @wpa_s: Pointer to wpa_supplicant - * @report: Neighbor report buffer, prefixed by a 1-byte dialog token - * @report_len: Length of neighbor report buffer - */ -void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s, - const u8 *report, size_t report_len) -{ - struct wpabuf *neighbor_rep; - - wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len); - if (report_len < 1) - return; - - if (report[0] != wpa_s->rrm.next_neighbor_rep_token - 1) { - wpa_printf(MSG_DEBUG, - "RRM: Discarding neighbor report with token %d (expected %d)", - report[0], wpa_s->rrm.next_neighbor_rep_token - 1); - return; - } - - eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm, - NULL); - - if (!wpa_s->rrm.notify_neighbor_rep) { - wpa_printf(MSG_ERROR, "RRM: Unexpected neighbor report"); - return; - } - - /* skipping the first byte, which is only an id (dialog token) */ - neighbor_rep = wpabuf_alloc(report_len - 1); - if (neighbor_rep == NULL) - return; - wpabuf_put_data(neighbor_rep, report + 1, report_len - 1); - wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)", - report[0]); - wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx, - neighbor_rep); - wpa_s->rrm.notify_neighbor_rep = NULL; - wpa_s->rrm.neighbor_rep_cb_ctx = NULL; -} - - -#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS) -/* Workaround different, undefined for Windows, error codes used here */ -#define ENOTCONN -1 -#define EOPNOTSUPP -1 -#define ECANCELED -1 -#endif - -/* Measurement Request element + Location Subject + Maximum Age subelement */ -#define MEASURE_REQUEST_LCI_LEN (3 + 1 + 4) -/* Measurement Request element + Location Civic Request */ -#define MEASURE_REQUEST_CIVIC_LEN (3 + 5) - - -/** - * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP - * @wpa_s: Pointer to wpa_supplicant - * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE - * is sent in the request. - * @lci: if set, neighbor request will include LCI request - * @civic: if set, neighbor request will include civic location request - * @cb: Callback function to be called once the requested report arrives, or - * timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds. - * In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's - * the requester's responsibility to free it. - * In the latter case NULL will be sent in 'neighbor_rep'. - * @cb_ctx: Context value to send the callback function - * Returns: 0 in case of success, negative error code otherwise - * - * In case there is a previous request which has not been answered yet, the - * new request fails. The caller may retry after RRM_NEIGHBOR_REPORT_TIMEOUT. - * Request must contain a callback function. - */ -int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, - const struct wpa_ssid_value *ssid, - int lci, int civic, - void (*cb)(void *ctx, - struct wpabuf *neighbor_rep), - void *cb_ctx) -{ - struct wpabuf *buf; - const u8 *rrm_ie; - - if (wpa_s->wpa_state != WPA_COMPLETED || wpa_s->current_ssid == NULL) { - wpa_printf(MSG_DEBUG, "RRM: No connection, no RRM."); - return -ENOTCONN; - } - - if (!wpa_s->rrm.rrm_used) { - wpa_printf(MSG_DEBUG, "RRM: No RRM in current connection."); - return -EOPNOTSUPP; - } - - rrm_ie = wpa_bss_get_ie(wpa_s->current_bss, - WLAN_EID_RRM_ENABLED_CAPABILITIES); - if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) || - !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) { - wpa_printf(MSG_DEBUG, - "RRM: No network support for Neighbor Report."); - return -EOPNOTSUPP; - } - - if (!cb) { - wpa_printf(MSG_DEBUG, - "RRM: Neighbor Report request must provide a callback."); - return -EINVAL; - } - - /* Refuse if there's a live request */ - if (wpa_s->rrm.notify_neighbor_rep) { - wpa_printf(MSG_DEBUG, - "RRM: Currently handling previous Neighbor Report."); - return -EBUSY; - } - - /* 3 = action category + action code + dialog token */ - buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0) + - (lci ? 2 + MEASURE_REQUEST_LCI_LEN : 0) + - (civic ? 2 + MEASURE_REQUEST_CIVIC_LEN : 0)); - if (buf == NULL) { - wpa_printf(MSG_DEBUG, - "RRM: Failed to allocate Neighbor Report Request"); - return -ENOMEM; - } - - wpa_printf(MSG_DEBUG, "RRM: Neighbor report request (for %s), token=%d", - (ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""), - wpa_s->rrm.next_neighbor_rep_token); - - wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); - wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST); - wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token); - if (ssid) { - wpabuf_put_u8(buf, WLAN_EID_SSID); - wpabuf_put_u8(buf, ssid->ssid_len); - wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len); - } - - if (lci) { - /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */ - wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); - wpabuf_put_u8(buf, MEASURE_REQUEST_LCI_LEN); - - /* - * Measurement token; nonzero number that is unique among the - * Measurement Request elements in a particular frame. - */ - wpabuf_put_u8(buf, 1); /* Measurement Token */ - - /* - * Parallel, Enable, Request, and Report bits are 0, Duration is - * reserved. - */ - wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ - wpabuf_put_u8(buf, MEASURE_TYPE_LCI); /* Measurement Type */ - - /* IEEE P802.11-REVmc/D5.0 9.4.2.21.10 - LCI request */ - /* Location Subject */ - wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); - - /* Optional Subelements */ - /* - * IEEE P802.11-REVmc/D5.0 Figure 9-170 - * The Maximum Age subelement is required, otherwise the AP can - * send only data that was determined after receiving the - * request. Setting it here to unlimited age. - */ - wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE); - wpabuf_put_u8(buf, 2); - wpabuf_put_le16(buf, 0xffff); - } - - if (civic) { - /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */ - wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); - wpabuf_put_u8(buf, MEASURE_REQUEST_CIVIC_LEN); - - /* - * Measurement token; nonzero number that is unique among the - * Measurement Request elements in a particular frame. - */ - wpabuf_put_u8(buf, 2); /* Measurement Token */ - - /* - * Parallel, Enable, Request, and Report bits are 0, Duration is - * reserved. - */ - wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ - /* Measurement Type */ - wpabuf_put_u8(buf, MEASURE_TYPE_LOCATION_CIVIC); - - /* IEEE P802.11-REVmc/D5.0 9.4.2.21.14: - * Location Civic request */ - /* Location Subject */ - wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); - wpabuf_put_u8(buf, 0); /* Civic Location Type: IETF RFC 4776 */ - /* Location Service Interval Units: Seconds */ - wpabuf_put_u8(buf, 0); - /* Location Service Interval: 0 - Only one report is requested - */ - wpabuf_put_le16(buf, 0); - /* No optional subelements */ - } - - wpa_s->rrm.next_neighbor_rep_token++; - - if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, - wpa_s->own_addr, wpa_s->bssid, - wpabuf_head(buf), wpabuf_len(buf), 0) < 0) { - wpa_printf(MSG_DEBUG, - "RRM: Failed to send Neighbor Report Request"); - wpabuf_free(buf); - return -ECANCELED; - } - - wpa_s->rrm.neighbor_rep_cb_ctx = cb_ctx; - wpa_s->rrm.notify_neighbor_rep = cb; - eloop_register_timeout(RRM_NEIGHBOR_REPORT_TIMEOUT, 0, - wpas_rrm_neighbor_rep_timeout_handler, - &wpa_s->rrm, NULL); - - wpabuf_free(buf); - return 0; -} - - -static struct wpabuf * wpas_rrm_build_lci_report(struct wpa_supplicant *wpa_s, - const u8 *request, size_t len, - struct wpabuf *report) -{ - u8 token, type, subject; - u16 max_age = 0; - struct os_reltime t, diff; - unsigned long diff_l; - u8 *ptoken; - const u8 *subelem; - - if (!wpa_s->lci || len < 3 + 4) - return report; - - token = *request++; - /* Measurement request mode isn't used */ - request++; - type = *request++; - subject = *request++; - len -= 4; - - wpa_printf(MSG_DEBUG, - "Measurement request token %u type %u location subject %u", - token, type, subject); - - if (type != MEASURE_TYPE_LCI || subject != LOCATION_SUBJECT_REMOTE) { - wpa_printf(MSG_INFO, - "Not building LCI report - bad type or location subject"); - return report; - } - - /* Subelements are formatted exactly like elements */ - subelem = get_ie(request, len, LCI_REQ_SUBELEM_MAX_AGE); - if (subelem && subelem[1] == 2) - max_age = WPA_GET_LE16(subelem + 2); - - if (os_get_reltime(&t)) - return report; - - os_reltime_sub(&t, &wpa_s->lci_time, &diff); - /* LCI age is calculated in 10th of a second units. */ - diff_l = diff.sec * 10 + diff.usec / 100000; - - if (max_age != 0xffff && max_age < diff_l) - return report; - - if (wpabuf_resize(&report, 2 + wpabuf_len(wpa_s->lci))) - return report; - - wpabuf_put_u8(report, WLAN_EID_MEASURE_REPORT); - wpabuf_put_u8(report, wpabuf_len(wpa_s->lci)); - /* We'll override user's measurement token */ - ptoken = wpabuf_put(report, 0); - wpabuf_put_buf(report, wpa_s->lci); - *ptoken = token; - - return report; -} - - -void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s, - const u8 *src, - const u8 *frame, size_t len) -{ - struct wpabuf *buf, *report; - u8 token; - const u8 *ie, *end; - - if (wpa_s->wpa_state != WPA_COMPLETED) { - wpa_printf(MSG_INFO, - "RRM: Ignoring radio measurement request: Not associated"); - return; - } - - if (!wpa_s->rrm.rrm_used) { - wpa_printf(MSG_INFO, - "RRM: Ignoring radio measurement request: Not RRM network"); - return; - } - - if (len < 3) { - wpa_printf(MSG_INFO, - "RRM: Ignoring too short radio measurement request"); - return; - } - - end = frame + len; - - token = *frame++; - - /* Ignore number of repetitions because it's not used in LCI request */ - frame += 2; - - report = NULL; - while ((ie = get_ie(frame, end - frame, WLAN_EID_MEASURE_REQUEST)) && - ie[1] >= 3) { - u8 msmt_type; - - msmt_type = ie[4]; - wpa_printf(MSG_DEBUG, "RRM request %d", msmt_type); - - switch (msmt_type) { - case MEASURE_TYPE_LCI: - report = wpas_rrm_build_lci_report(wpa_s, ie + 2, ie[1], - report); - break; - default: - wpa_printf(MSG_INFO, - "RRM: Unsupported radio measurement request %d", - msmt_type); - break; - } - - frame = ie + ie[1] + 2; - } - - if (!report) - return; - - buf = wpabuf_alloc(3 + wpabuf_len(report)); - if (!buf) { - wpabuf_free(report); - return; - } - - wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); - wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REPORT); - wpabuf_put_u8(buf, token); - - wpabuf_put_buf(buf, report); - wpabuf_free(report); - - if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src, - wpa_s->own_addr, wpa_s->bssid, - wpabuf_head(buf), wpabuf_len(buf), 0)) { - wpa_printf(MSG_ERROR, - "RRM: Radio measurement report failed: Sending Action frame failed"); - } - wpabuf_free(buf); -} - - -void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, - const u8 *src, - const u8 *frame, size_t len, - int rssi) -{ - struct wpabuf *buf; - const struct rrm_link_measurement_request *req; - struct rrm_link_measurement_report report; - - if (wpa_s->wpa_state != WPA_COMPLETED) { - wpa_printf(MSG_INFO, - "RRM: Ignoring link measurement request. Not associated"); - return; - } - - if (!wpa_s->rrm.rrm_used) { - wpa_printf(MSG_INFO, - "RRM: Ignoring link measurement request. Not RRM network"); - return; - } - - if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) { - wpa_printf(MSG_INFO, - "RRM: Measurement report failed. TX power insertion not supported"); - return; - } - - req = (const struct rrm_link_measurement_request *) frame; - if (len < sizeof(*req)) { - wpa_printf(MSG_INFO, - "RRM: Link measurement report failed. Request too short"); - return; - } - - os_memset(&report, 0, sizeof(report)); - report.tpc.eid = WLAN_EID_TPC_REPORT; - report.tpc.len = 2; - report.rsni = 255; /* 255 indicates that RSNI is not available */ - report.dialog_token = req->dialog_token; - - /* - * It's possible to estimate RCPI based on RSSI in dBm. This - * calculation will not reflect the correct value for high rates, - * but it's good enough for Action frames which are transmitted - * with up to 24 Mbps rates. - */ - if (!rssi) - report.rcpi = 255; /* not available */ - else if (rssi < -110) - report.rcpi = 0; - else if (rssi > 0) - report.rcpi = 220; - else - report.rcpi = (rssi + 110) * 2; - - /* action_category + action_code */ - buf = wpabuf_alloc(2 + sizeof(report)); - if (buf == NULL) { - wpa_printf(MSG_ERROR, - "RRM: Link measurement report failed. Buffer allocation failed"); - return; - } - - wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); - wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REPORT); - wpabuf_put_data(buf, &report, sizeof(report)); - wpa_hexdump(MSG_DEBUG, "RRM: Link measurement report:", - wpabuf_head(buf), wpabuf_len(buf)); - - if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src, - wpa_s->own_addr, wpa_s->bssid, - wpabuf_head(buf), wpabuf_len(buf), 0)) { - wpa_printf(MSG_ERROR, - "RRM: Link measurement report failed. Send action failed"); - } - wpabuf_free(buf); -} - - struct wpa_supplicant * wpas_vendor_elem(struct wpa_supplicant *wpa_s, enum wpa_vendor_elem_frame frame) {