diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 9320e5730..e450e3b60 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -311,6 +311,33 @@ static int wpas_ctrl_set_band(struct wpa_supplicant *wpa_s, char *band) } +static int wpas_ctrl_iface_set_lci(struct wpa_supplicant *wpa_s, + const char *cmd) +{ + struct wpabuf *lci; + + if (*cmd == '\0' || os_strcmp(cmd, "\"\"") == 0) { + wpabuf_free(wpa_s->lci); + wpa_s->lci = NULL; + return 0; + } + + lci = wpabuf_parse_bin(cmd); + if (!lci) + return -1; + + if (os_get_reltime(&wpa_s->lci_time)) { + wpabuf_free(lci); + return -1; + } + + wpabuf_free(wpa_s->lci); + wpa_s->lci = lci; + + return 0; +} + + static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, char *cmd) { @@ -497,6 +524,8 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s, } else if (os_strcasecmp(cmd, "mbo_cell_capa") == 0) { wpas_mbo_update_cell_capa(wpa_s, atoi(value)); #endif /* CONFIG_MBO */ + } else if (os_strcasecmp(cmd, "lci") == 0) { + ret = wpas_ctrl_iface_set_lci(wpa_s, value); } else { value[-1] = '='; ret = wpa_config_process_global(wpa_s->conf, cmd, -1); diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index dbc01e236..f9b9cd667 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -3278,6 +3278,14 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s, } #endif /* CONFIG_INTERWORKING */ + if (category == WLAN_ACTION_RADIO_MEASUREMENT && + payload[0] == WLAN_RRM_RADIO_MEASUREMENT_REQUEST) { + wpas_rrm_handle_radio_measurement_request(wpa_s, mgmt->sa, + payload + 1, + plen - 1); + return; + } + if (category == WLAN_ACTION_RADIO_MEASUREMENT && payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) { wpas_rrm_process_neighbor_rep(wpa_s, payload + 1, plen - 1); diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index d1c953712..70524cc8b 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -186,6 +186,9 @@ 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->lci) + pos[1] |= WLAN_RRM_CAPS_LCI_MEASUREMENT; + wpa_s->sme.assoc_req_ie_len += rrm_ie_len + 2; wpa_s->rrm.rrm_used = 1; } diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 1f0fcd0ac..bf6dfff70 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -575,6 +575,9 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s) #endif /* CONFIG_MBO */ free_bss_tmp_disallowed(wpa_s); + + wpabuf_free(wpa_s->lci); + wpa_s->lci = NULL; } @@ -6340,6 +6343,147 @@ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, } +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++; + + 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, diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 211f4d11a..e45f662e2 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1058,6 +1058,13 @@ struct wpa_supplicant { * the bss_temp_disallowed list for other purposes as well. */ struct dl_list bss_tmp_disallowed; + + /* + * Content of a measurement report element with type 8 (LCI), + * own location. + */ + struct wpabuf *lci; + struct os_reltime lci_time; }; @@ -1168,6 +1175,9 @@ int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s, void (*cb)(void *ctx, struct wpabuf *neighbor_rep), void *cb_ctx); +void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s, + const u8 *src, + const u8 *frame, size_t len); void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s, const u8 *src, const u8 *frame, size_t len,