1e5b75505Sopenharmony_ci/* 2e5b75505Sopenharmony_ci * hostapd / Radio Measurement (RRM) 3e5b75505Sopenharmony_ci * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH. 4e5b75505Sopenharmony_ci * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved. 5e5b75505Sopenharmony_ci * Copyright (c) 2016-2017, Jouni Malinen <j@w1.fi> 6e5b75505Sopenharmony_ci * 7e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license. 8e5b75505Sopenharmony_ci * See README for more details. 9e5b75505Sopenharmony_ci */ 10e5b75505Sopenharmony_ci 11e5b75505Sopenharmony_ci#include "utils/includes.h" 12e5b75505Sopenharmony_ci 13e5b75505Sopenharmony_ci#include "utils/common.h" 14e5b75505Sopenharmony_ci#include "common/wpa_ctrl.h" 15e5b75505Sopenharmony_ci#include "hostapd.h" 16e5b75505Sopenharmony_ci#include "ap_drv_ops.h" 17e5b75505Sopenharmony_ci#include "sta_info.h" 18e5b75505Sopenharmony_ci#include "eloop.h" 19e5b75505Sopenharmony_ci#include "neighbor_db.h" 20e5b75505Sopenharmony_ci#include "rrm.h" 21e5b75505Sopenharmony_ci 22e5b75505Sopenharmony_ci#define HOSTAPD_RRM_REQUEST_TIMEOUT 5 23e5b75505Sopenharmony_ci 24e5b75505Sopenharmony_ci 25e5b75505Sopenharmony_cistatic void hostapd_lci_rep_timeout_handler(void *eloop_data, void *user_ctx) 26e5b75505Sopenharmony_ci{ 27e5b75505Sopenharmony_ci struct hostapd_data *hapd = eloop_data; 28e5b75505Sopenharmony_ci 29e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "RRM: LCI request (token %u) timed out", 30e5b75505Sopenharmony_ci hapd->lci_req_token); 31e5b75505Sopenharmony_ci hapd->lci_req_active = 0; 32e5b75505Sopenharmony_ci} 33e5b75505Sopenharmony_ci 34e5b75505Sopenharmony_ci 35e5b75505Sopenharmony_cistatic void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token, 36e5b75505Sopenharmony_ci const u8 *pos, size_t len) 37e5b75505Sopenharmony_ci{ 38e5b75505Sopenharmony_ci if (!hapd->lci_req_active || hapd->lci_req_token != token) { 39e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Unexpected LCI report, token %u", token); 40e5b75505Sopenharmony_ci return; 41e5b75505Sopenharmony_ci } 42e5b75505Sopenharmony_ci 43e5b75505Sopenharmony_ci hapd->lci_req_active = 0; 44e5b75505Sopenharmony_ci eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL); 45e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "LCI report token %u len %zu", token, len); 46e5b75505Sopenharmony_ci} 47e5b75505Sopenharmony_ci 48e5b75505Sopenharmony_ci 49e5b75505Sopenharmony_cistatic void hostapd_range_rep_timeout_handler(void *eloop_data, void *user_ctx) 50e5b75505Sopenharmony_ci{ 51e5b75505Sopenharmony_ci struct hostapd_data *hapd = eloop_data; 52e5b75505Sopenharmony_ci 53e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "RRM: Range request (token %u) timed out", 54e5b75505Sopenharmony_ci hapd->range_req_token); 55e5b75505Sopenharmony_ci hapd->range_req_active = 0; 56e5b75505Sopenharmony_ci} 57e5b75505Sopenharmony_ci 58e5b75505Sopenharmony_ci 59e5b75505Sopenharmony_cistatic void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token, 60e5b75505Sopenharmony_ci const u8 *pos, size_t len) 61e5b75505Sopenharmony_ci{ 62e5b75505Sopenharmony_ci if (!hapd->range_req_active || hapd->range_req_token != token) { 63e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Unexpected range report, token %u", 64e5b75505Sopenharmony_ci token); 65e5b75505Sopenharmony_ci return; 66e5b75505Sopenharmony_ci } 67e5b75505Sopenharmony_ci 68e5b75505Sopenharmony_ci hapd->range_req_active = 0; 69e5b75505Sopenharmony_ci eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL); 70e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Range report token %u len %zu", token, len); 71e5b75505Sopenharmony_ci} 72e5b75505Sopenharmony_ci 73e5b75505Sopenharmony_ci 74e5b75505Sopenharmony_cistatic void hostapd_handle_beacon_report(struct hostapd_data *hapd, 75e5b75505Sopenharmony_ci const u8 *addr, u8 token, u8 rep_mode, 76e5b75505Sopenharmony_ci const u8 *pos, size_t len) 77e5b75505Sopenharmony_ci{ 78e5b75505Sopenharmony_ci char report[2 * 255 + 1]; 79e5b75505Sopenharmony_ci 80e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Beacon report token %u len %zu from " MACSTR, 81e5b75505Sopenharmony_ci token, len, MAC2STR(addr)); 82e5b75505Sopenharmony_ci /* Skip to the beginning of the Beacon report */ 83e5b75505Sopenharmony_ci if (len < 3) 84e5b75505Sopenharmony_ci return; 85e5b75505Sopenharmony_ci pos += 3; 86e5b75505Sopenharmony_ci len -= 3; 87e5b75505Sopenharmony_ci report[0] = '\0'; 88e5b75505Sopenharmony_ci if (wpa_snprintf_hex(report, sizeof(report), pos, len) < 0) 89e5b75505Sopenharmony_ci return; 90e5b75505Sopenharmony_ci wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s", 91e5b75505Sopenharmony_ci MAC2STR(addr), token, rep_mode, report); 92e5b75505Sopenharmony_ci} 93e5b75505Sopenharmony_ci 94e5b75505Sopenharmony_ci 95e5b75505Sopenharmony_cistatic void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd, 96e5b75505Sopenharmony_ci const u8 *buf, size_t len) 97e5b75505Sopenharmony_ci{ 98e5b75505Sopenharmony_ci const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; 99e5b75505Sopenharmony_ci const u8 *pos, *ie, *end; 100e5b75505Sopenharmony_ci u8 token, rep_mode; 101e5b75505Sopenharmony_ci 102e5b75505Sopenharmony_ci end = buf + len; 103e5b75505Sopenharmony_ci token = mgmt->u.action.u.rrm.dialog_token; 104e5b75505Sopenharmony_ci pos = mgmt->u.action.u.rrm.variable; 105e5b75505Sopenharmony_ci 106e5b75505Sopenharmony_ci while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) { 107e5b75505Sopenharmony_ci if (ie[1] < 3) { 108e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Bad Measurement Report element"); 109e5b75505Sopenharmony_ci break; 110e5b75505Sopenharmony_ci } 111e5b75505Sopenharmony_ci 112e5b75505Sopenharmony_ci rep_mode = ie[3]; 113e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Measurement report mode 0x%x type %u", 114e5b75505Sopenharmony_ci rep_mode, ie[4]); 115e5b75505Sopenharmony_ci 116e5b75505Sopenharmony_ci switch (ie[4]) { 117e5b75505Sopenharmony_ci case MEASURE_TYPE_LCI: 118e5b75505Sopenharmony_ci hostapd_handle_lci_report(hapd, token, ie + 2, ie[1]); 119e5b75505Sopenharmony_ci break; 120e5b75505Sopenharmony_ci case MEASURE_TYPE_FTM_RANGE: 121e5b75505Sopenharmony_ci hostapd_handle_range_report(hapd, token, ie + 2, ie[1]); 122e5b75505Sopenharmony_ci break; 123e5b75505Sopenharmony_ci case MEASURE_TYPE_BEACON: 124e5b75505Sopenharmony_ci hostapd_handle_beacon_report(hapd, mgmt->sa, token, 125e5b75505Sopenharmony_ci rep_mode, ie + 2, ie[1]); 126e5b75505Sopenharmony_ci break; 127e5b75505Sopenharmony_ci default: 128e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 129e5b75505Sopenharmony_ci "Measurement report type %u is not supported", 130e5b75505Sopenharmony_ci ie[4]); 131e5b75505Sopenharmony_ci break; 132e5b75505Sopenharmony_ci } 133e5b75505Sopenharmony_ci 134e5b75505Sopenharmony_ci pos = ie + ie[1] + 2; 135e5b75505Sopenharmony_ci } 136e5b75505Sopenharmony_ci} 137e5b75505Sopenharmony_ci 138e5b75505Sopenharmony_ci 139e5b75505Sopenharmony_cistatic u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len) 140e5b75505Sopenharmony_ci{ 141e5b75505Sopenharmony_ci const u8 *subelem; 142e5b75505Sopenharmony_ci 143e5b75505Sopenharmony_ci /* Range Request element + Location Subject + Maximum Age subelement */ 144e5b75505Sopenharmony_ci if (len < 3 + 1 + 4) 145e5b75505Sopenharmony_ci return 0; 146e5b75505Sopenharmony_ci 147e5b75505Sopenharmony_ci /* Subelements are arranged as IEs */ 148e5b75505Sopenharmony_ci subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE); 149e5b75505Sopenharmony_ci if (subelem && subelem[1] == 2) 150e5b75505Sopenharmony_ci return WPA_GET_LE16(subelem + 2); 151e5b75505Sopenharmony_ci 152e5b75505Sopenharmony_ci return 0; 153e5b75505Sopenharmony_ci} 154e5b75505Sopenharmony_ci 155e5b75505Sopenharmony_ci 156e5b75505Sopenharmony_cistatic int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age) 157e5b75505Sopenharmony_ci{ 158e5b75505Sopenharmony_ci struct os_time curr, diff; 159e5b75505Sopenharmony_ci unsigned long diff_l; 160e5b75505Sopenharmony_ci 161e5b75505Sopenharmony_ci if (nr->stationary || max_age == 0xffff) 162e5b75505Sopenharmony_ci return 1; 163e5b75505Sopenharmony_ci 164e5b75505Sopenharmony_ci if (!max_age) 165e5b75505Sopenharmony_ci return 0; 166e5b75505Sopenharmony_ci 167e5b75505Sopenharmony_ci if (os_get_time(&curr)) 168e5b75505Sopenharmony_ci return 0; 169e5b75505Sopenharmony_ci 170e5b75505Sopenharmony_ci os_time_sub(&curr, &nr->lci_date, &diff); 171e5b75505Sopenharmony_ci 172e5b75505Sopenharmony_ci /* avoid overflow */ 173e5b75505Sopenharmony_ci if (diff.sec > 0xffff) 174e5b75505Sopenharmony_ci return 0; 175e5b75505Sopenharmony_ci 176e5b75505Sopenharmony_ci /* LCI age is calculated in 10th of a second units. */ 177e5b75505Sopenharmony_ci diff_l = diff.sec * 10 + diff.usec / 100000; 178e5b75505Sopenharmony_ci 179e5b75505Sopenharmony_ci return max_age > diff_l; 180e5b75505Sopenharmony_ci} 181e5b75505Sopenharmony_ci 182e5b75505Sopenharmony_ci 183e5b75505Sopenharmony_cistatic size_t hostapd_neighbor_report_len(struct wpabuf *buf, 184e5b75505Sopenharmony_ci struct hostapd_neighbor_entry *nr, 185e5b75505Sopenharmony_ci int send_lci, int send_civic) 186e5b75505Sopenharmony_ci{ 187e5b75505Sopenharmony_ci size_t len = 2 + wpabuf_len(nr->nr); 188e5b75505Sopenharmony_ci 189e5b75505Sopenharmony_ci if (send_lci && nr->lci) 190e5b75505Sopenharmony_ci len += 2 + wpabuf_len(nr->lci); 191e5b75505Sopenharmony_ci 192e5b75505Sopenharmony_ci if (send_civic && nr->civic) 193e5b75505Sopenharmony_ci len += 2 + wpabuf_len(nr->civic); 194e5b75505Sopenharmony_ci 195e5b75505Sopenharmony_ci return len; 196e5b75505Sopenharmony_ci} 197e5b75505Sopenharmony_ci 198e5b75505Sopenharmony_ci 199e5b75505Sopenharmony_cistatic void hostapd_send_nei_report_resp(struct hostapd_data *hapd, 200e5b75505Sopenharmony_ci const u8 *addr, u8 dialog_token, 201e5b75505Sopenharmony_ci struct wpa_ssid_value *ssid, u8 lci, 202e5b75505Sopenharmony_ci u8 civic, u16 lci_max_age) 203e5b75505Sopenharmony_ci{ 204e5b75505Sopenharmony_ci struct hostapd_neighbor_entry *nr; 205e5b75505Sopenharmony_ci struct wpabuf *buf; 206e5b75505Sopenharmony_ci u8 *msmt_token; 207e5b75505Sopenharmony_ci 208e5b75505Sopenharmony_ci /* 209e5b75505Sopenharmony_ci * The number and length of the Neighbor Report elements in a Neighbor 210e5b75505Sopenharmony_ci * Report frame is limited by the maximum allowed MMPDU size; + 3 bytes 211e5b75505Sopenharmony_ci * of RRM header. 212e5b75505Sopenharmony_ci */ 213e5b75505Sopenharmony_ci buf = wpabuf_alloc(3 + IEEE80211_MAX_MMPDU_SIZE); 214e5b75505Sopenharmony_ci if (!buf) 215e5b75505Sopenharmony_ci return; 216e5b75505Sopenharmony_ci 217e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); 218e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_RESPONSE); 219e5b75505Sopenharmony_ci wpabuf_put_u8(buf, dialog_token); 220e5b75505Sopenharmony_ci 221e5b75505Sopenharmony_ci dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry, 222e5b75505Sopenharmony_ci list) { 223e5b75505Sopenharmony_ci int send_lci; 224e5b75505Sopenharmony_ci size_t len; 225e5b75505Sopenharmony_ci 226e5b75505Sopenharmony_ci if (ssid->ssid_len != nr->ssid.ssid_len || 227e5b75505Sopenharmony_ci os_memcmp(ssid->ssid, nr->ssid.ssid, ssid->ssid_len) != 0) 228e5b75505Sopenharmony_ci continue; 229e5b75505Sopenharmony_ci 230e5b75505Sopenharmony_ci send_lci = (lci != 0) && hostapd_check_lci_age(nr, lci_max_age); 231e5b75505Sopenharmony_ci len = hostapd_neighbor_report_len(buf, nr, send_lci, civic); 232e5b75505Sopenharmony_ci 233e5b75505Sopenharmony_ci if (len - 2 > 0xff) { 234e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 235e5b75505Sopenharmony_ci "NR entry for " MACSTR " exceeds 0xFF bytes", 236e5b75505Sopenharmony_ci MAC2STR(nr->bssid)); 237e5b75505Sopenharmony_ci continue; 238e5b75505Sopenharmony_ci } 239e5b75505Sopenharmony_ci 240e5b75505Sopenharmony_ci if (len > wpabuf_tailroom(buf)) 241e5b75505Sopenharmony_ci break; 242e5b75505Sopenharmony_ci 243e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT); 244e5b75505Sopenharmony_ci wpabuf_put_u8(buf, len - 2); 245e5b75505Sopenharmony_ci wpabuf_put_buf(buf, nr->nr); 246e5b75505Sopenharmony_ci 247e5b75505Sopenharmony_ci if (send_lci && nr->lci) { 248e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT); 249e5b75505Sopenharmony_ci wpabuf_put_u8(buf, wpabuf_len(nr->lci)); 250e5b75505Sopenharmony_ci /* 251e5b75505Sopenharmony_ci * Override measurement token - the first byte of the 252e5b75505Sopenharmony_ci * Measurement Report element. 253e5b75505Sopenharmony_ci */ 254e5b75505Sopenharmony_ci msmt_token = wpabuf_put(buf, 0); 255e5b75505Sopenharmony_ci wpabuf_put_buf(buf, nr->lci); 256e5b75505Sopenharmony_ci *msmt_token = lci; 257e5b75505Sopenharmony_ci } 258e5b75505Sopenharmony_ci 259e5b75505Sopenharmony_ci if (civic && nr->civic) { 260e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT); 261e5b75505Sopenharmony_ci wpabuf_put_u8(buf, wpabuf_len(nr->civic)); 262e5b75505Sopenharmony_ci /* 263e5b75505Sopenharmony_ci * Override measurement token - the first byte of the 264e5b75505Sopenharmony_ci * Measurement Report element. 265e5b75505Sopenharmony_ci */ 266e5b75505Sopenharmony_ci msmt_token = wpabuf_put(buf, 0); 267e5b75505Sopenharmony_ci wpabuf_put_buf(buf, nr->civic); 268e5b75505Sopenharmony_ci *msmt_token = civic; 269e5b75505Sopenharmony_ci } 270e5b75505Sopenharmony_ci } 271e5b75505Sopenharmony_ci 272e5b75505Sopenharmony_ci hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, 273e5b75505Sopenharmony_ci wpabuf_head(buf), wpabuf_len(buf)); 274e5b75505Sopenharmony_ci wpabuf_free(buf); 275e5b75505Sopenharmony_ci} 276e5b75505Sopenharmony_ci 277e5b75505Sopenharmony_ci 278e5b75505Sopenharmony_cistatic void hostapd_handle_nei_report_req(struct hostapd_data *hapd, 279e5b75505Sopenharmony_ci const u8 *buf, size_t len) 280e5b75505Sopenharmony_ci{ 281e5b75505Sopenharmony_ci const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; 282e5b75505Sopenharmony_ci const u8 *pos, *ie, *end; 283e5b75505Sopenharmony_ci struct wpa_ssid_value ssid = { 284e5b75505Sopenharmony_ci .ssid_len = 0 285e5b75505Sopenharmony_ci }; 286e5b75505Sopenharmony_ci u8 token; 287e5b75505Sopenharmony_ci u8 lci = 0, civic = 0; /* Measurement tokens */ 288e5b75505Sopenharmony_ci u16 lci_max_age = 0; 289e5b75505Sopenharmony_ci 290e5b75505Sopenharmony_ci if (!(hapd->conf->radio_measurements[0] & 291e5b75505Sopenharmony_ci WLAN_RRM_CAPS_NEIGHBOR_REPORT)) 292e5b75505Sopenharmony_ci return; 293e5b75505Sopenharmony_ci 294e5b75505Sopenharmony_ci end = buf + len; 295e5b75505Sopenharmony_ci 296e5b75505Sopenharmony_ci token = mgmt->u.action.u.rrm.dialog_token; 297e5b75505Sopenharmony_ci pos = mgmt->u.action.u.rrm.variable; 298e5b75505Sopenharmony_ci len = end - pos; 299e5b75505Sopenharmony_ci 300e5b75505Sopenharmony_ci ie = get_ie(pos, len, WLAN_EID_SSID); 301e5b75505Sopenharmony_ci if (ie && ie[1] && ie[1] <= SSID_MAX_LEN) { 302e5b75505Sopenharmony_ci ssid.ssid_len = ie[1]; 303e5b75505Sopenharmony_ci os_memcpy(ssid.ssid, ie + 2, ssid.ssid_len); 304e5b75505Sopenharmony_ci } else { 305e5b75505Sopenharmony_ci ssid.ssid_len = hapd->conf->ssid.ssid_len; 306e5b75505Sopenharmony_ci os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len); 307e5b75505Sopenharmony_ci } 308e5b75505Sopenharmony_ci 309e5b75505Sopenharmony_ci while ((ie = get_ie(pos, len, WLAN_EID_MEASURE_REQUEST))) { 310e5b75505Sopenharmony_ci if (ie[1] < 3) 311e5b75505Sopenharmony_ci break; 312e5b75505Sopenharmony_ci 313e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 314e5b75505Sopenharmony_ci "Neighbor report request, measure type %u", 315e5b75505Sopenharmony_ci ie[4]); 316e5b75505Sopenharmony_ci 317e5b75505Sopenharmony_ci switch (ie[4]) { /* Measurement Type */ 318e5b75505Sopenharmony_ci case MEASURE_TYPE_LCI: 319e5b75505Sopenharmony_ci lci = ie[2]; /* Measurement Token */ 320e5b75505Sopenharmony_ci lci_max_age = hostapd_parse_location_lci_req_age(ie + 2, 321e5b75505Sopenharmony_ci ie[1]); 322e5b75505Sopenharmony_ci break; 323e5b75505Sopenharmony_ci case MEASURE_TYPE_LOCATION_CIVIC: 324e5b75505Sopenharmony_ci civic = ie[2]; /* Measurement token */ 325e5b75505Sopenharmony_ci break; 326e5b75505Sopenharmony_ci } 327e5b75505Sopenharmony_ci 328e5b75505Sopenharmony_ci pos = ie + ie[1] + 2; 329e5b75505Sopenharmony_ci len = end - pos; 330e5b75505Sopenharmony_ci } 331e5b75505Sopenharmony_ci 332e5b75505Sopenharmony_ci hostapd_send_nei_report_resp(hapd, mgmt->sa, token, &ssid, lci, civic, 333e5b75505Sopenharmony_ci lci_max_age); 334e5b75505Sopenharmony_ci} 335e5b75505Sopenharmony_ci 336e5b75505Sopenharmony_ci 337e5b75505Sopenharmony_civoid hostapd_handle_radio_measurement(struct hostapd_data *hapd, 338e5b75505Sopenharmony_ci const u8 *buf, size_t len) 339e5b75505Sopenharmony_ci{ 340e5b75505Sopenharmony_ci const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf; 341e5b75505Sopenharmony_ci 342e5b75505Sopenharmony_ci /* 343e5b75505Sopenharmony_ci * Check for enough bytes: header + (1B)Category + (1B)Action + 344e5b75505Sopenharmony_ci * (1B)Dialog Token. 345e5b75505Sopenharmony_ci */ 346e5b75505Sopenharmony_ci if (len < IEEE80211_HDRLEN + 3) 347e5b75505Sopenharmony_ci return; 348e5b75505Sopenharmony_ci 349e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Radio measurement frame, action %u from " MACSTR, 350e5b75505Sopenharmony_ci mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa)); 351e5b75505Sopenharmony_ci 352e5b75505Sopenharmony_ci switch (mgmt->u.action.u.rrm.action) { 353e5b75505Sopenharmony_ci case WLAN_RRM_RADIO_MEASUREMENT_REPORT: 354e5b75505Sopenharmony_ci hostapd_handle_radio_msmt_report(hapd, buf, len); 355e5b75505Sopenharmony_ci break; 356e5b75505Sopenharmony_ci case WLAN_RRM_NEIGHBOR_REPORT_REQUEST: 357e5b75505Sopenharmony_ci hostapd_handle_nei_report_req(hapd, buf, len); 358e5b75505Sopenharmony_ci break; 359e5b75505Sopenharmony_ci default: 360e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "RRM action %u is not supported", 361e5b75505Sopenharmony_ci mgmt->u.action.u.rrm.action); 362e5b75505Sopenharmony_ci break; 363e5b75505Sopenharmony_ci } 364e5b75505Sopenharmony_ci} 365e5b75505Sopenharmony_ci 366e5b75505Sopenharmony_ci 367e5b75505Sopenharmony_ciint hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr) 368e5b75505Sopenharmony_ci{ 369e5b75505Sopenharmony_ci struct wpabuf *buf; 370e5b75505Sopenharmony_ci struct sta_info *sta = ap_get_sta(hapd, addr); 371e5b75505Sopenharmony_ci int ret; 372e5b75505Sopenharmony_ci 373e5b75505Sopenharmony_ci if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) { 374e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 375e5b75505Sopenharmony_ci "Request LCI: Destination address is not connected"); 376e5b75505Sopenharmony_ci return -1; 377e5b75505Sopenharmony_ci } 378e5b75505Sopenharmony_ci 379e5b75505Sopenharmony_ci if (!(sta->rrm_enabled_capa[1] & WLAN_RRM_CAPS_LCI_MEASUREMENT)) { 380e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 381e5b75505Sopenharmony_ci "Request LCI: Station does not support LCI in RRM"); 382e5b75505Sopenharmony_ci return -1; 383e5b75505Sopenharmony_ci } 384e5b75505Sopenharmony_ci 385e5b75505Sopenharmony_ci if (hapd->lci_req_active) { 386e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 387e5b75505Sopenharmony_ci "Request LCI: LCI request is already in process, overriding"); 388e5b75505Sopenharmony_ci hapd->lci_req_active = 0; 389e5b75505Sopenharmony_ci eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, 390e5b75505Sopenharmony_ci NULL); 391e5b75505Sopenharmony_ci } 392e5b75505Sopenharmony_ci 393e5b75505Sopenharmony_ci /* Measurement request (5) + Measurement element with LCI (10) */ 394e5b75505Sopenharmony_ci buf = wpabuf_alloc(5 + 10); 395e5b75505Sopenharmony_ci if (!buf) 396e5b75505Sopenharmony_ci return -1; 397e5b75505Sopenharmony_ci 398e5b75505Sopenharmony_ci hapd->lci_req_token++; 399e5b75505Sopenharmony_ci /* For wraparounds - the token must be nonzero */ 400e5b75505Sopenharmony_ci if (!hapd->lci_req_token) 401e5b75505Sopenharmony_ci hapd->lci_req_token++; 402e5b75505Sopenharmony_ci 403e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); 404e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST); 405e5b75505Sopenharmony_ci wpabuf_put_u8(buf, hapd->lci_req_token); 406e5b75505Sopenharmony_ci wpabuf_put_le16(buf, 0); /* Number of repetitions */ 407e5b75505Sopenharmony_ci 408e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); 409e5b75505Sopenharmony_ci wpabuf_put_u8(buf, 3 + 1 + 4); 410e5b75505Sopenharmony_ci 411e5b75505Sopenharmony_ci wpabuf_put_u8(buf, 1); /* Measurement Token */ 412e5b75505Sopenharmony_ci /* 413e5b75505Sopenharmony_ci * Parallel and Enable bits are 0, Duration, Request, and Report are 414e5b75505Sopenharmony_ci * reserved. 415e5b75505Sopenharmony_ci */ 416e5b75505Sopenharmony_ci wpabuf_put_u8(buf, 0); 417e5b75505Sopenharmony_ci wpabuf_put_u8(buf, MEASURE_TYPE_LCI); 418e5b75505Sopenharmony_ci 419e5b75505Sopenharmony_ci wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE); 420e5b75505Sopenharmony_ci 421e5b75505Sopenharmony_ci wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE); 422e5b75505Sopenharmony_ci wpabuf_put_u8(buf, 2); 423e5b75505Sopenharmony_ci wpabuf_put_le16(buf, 0xffff); 424e5b75505Sopenharmony_ci 425e5b75505Sopenharmony_ci ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, 426e5b75505Sopenharmony_ci wpabuf_head(buf), wpabuf_len(buf)); 427e5b75505Sopenharmony_ci wpabuf_free(buf); 428e5b75505Sopenharmony_ci if (ret) 429e5b75505Sopenharmony_ci return ret; 430e5b75505Sopenharmony_ci 431e5b75505Sopenharmony_ci hapd->lci_req_active = 1; 432e5b75505Sopenharmony_ci 433e5b75505Sopenharmony_ci eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0, 434e5b75505Sopenharmony_ci hostapd_lci_rep_timeout_handler, hapd, NULL); 435e5b75505Sopenharmony_ci 436e5b75505Sopenharmony_ci return 0; 437e5b75505Sopenharmony_ci} 438e5b75505Sopenharmony_ci 439e5b75505Sopenharmony_ci 440e5b75505Sopenharmony_ciint hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr, 441e5b75505Sopenharmony_ci u16 random_interval, u8 min_ap, 442e5b75505Sopenharmony_ci const u8 *responders, unsigned int n_responders) 443e5b75505Sopenharmony_ci{ 444e5b75505Sopenharmony_ci struct wpabuf *buf; 445e5b75505Sopenharmony_ci struct sta_info *sta; 446e5b75505Sopenharmony_ci u8 *len; 447e5b75505Sopenharmony_ci unsigned int i; 448e5b75505Sopenharmony_ci int ret; 449e5b75505Sopenharmony_ci 450e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "Request range: dest addr " MACSTR 451e5b75505Sopenharmony_ci " rand interval %u min AP %u n_responders %u", MAC2STR(addr), 452e5b75505Sopenharmony_ci random_interval, min_ap, n_responders); 453e5b75505Sopenharmony_ci 454e5b75505Sopenharmony_ci if (min_ap == 0 || min_ap > n_responders) { 455e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Request range: Wrong min AP count"); 456e5b75505Sopenharmony_ci return -1; 457e5b75505Sopenharmony_ci } 458e5b75505Sopenharmony_ci 459e5b75505Sopenharmony_ci sta = ap_get_sta(hapd, addr); 460e5b75505Sopenharmony_ci if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) { 461e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 462e5b75505Sopenharmony_ci "Request range: Destination address is not connected"); 463e5b75505Sopenharmony_ci return -1; 464e5b75505Sopenharmony_ci } 465e5b75505Sopenharmony_ci 466e5b75505Sopenharmony_ci if (!(sta->rrm_enabled_capa[4] & WLAN_RRM_CAPS_FTM_RANGE_REPORT)) { 467e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, 468e5b75505Sopenharmony_ci "Request range: Destination station does not support FTM range report in RRM"); 469e5b75505Sopenharmony_ci return -1; 470e5b75505Sopenharmony_ci } 471e5b75505Sopenharmony_ci 472e5b75505Sopenharmony_ci if (hapd->range_req_active) { 473e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 474e5b75505Sopenharmony_ci "Request range: Range request is already in process; overriding"); 475e5b75505Sopenharmony_ci hapd->range_req_active = 0; 476e5b75505Sopenharmony_ci eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, 477e5b75505Sopenharmony_ci NULL); 478e5b75505Sopenharmony_ci } 479e5b75505Sopenharmony_ci 480e5b75505Sopenharmony_ci /* Action + measurement type + token + reps + EID + len = 7 */ 481e5b75505Sopenharmony_ci buf = wpabuf_alloc(7 + 255); 482e5b75505Sopenharmony_ci if (!buf) 483e5b75505Sopenharmony_ci return -1; 484e5b75505Sopenharmony_ci 485e5b75505Sopenharmony_ci hapd->range_req_token++; 486e5b75505Sopenharmony_ci if (!hapd->range_req_token) /* For wraparounds */ 487e5b75505Sopenharmony_ci hapd->range_req_token++; 488e5b75505Sopenharmony_ci 489e5b75505Sopenharmony_ci /* IEEE P802.11-REVmc/D5.0, 9.6.7.2 */ 490e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); 491e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST); 492e5b75505Sopenharmony_ci wpabuf_put_u8(buf, hapd->range_req_token); /* Dialog Token */ 493e5b75505Sopenharmony_ci wpabuf_put_le16(buf, 0); /* Number of Repetitions */ 494e5b75505Sopenharmony_ci 495e5b75505Sopenharmony_ci /* IEEE P802.11-REVmc/D5.0, 9.4.2.21 */ 496e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); 497e5b75505Sopenharmony_ci len = wpabuf_put(buf, 1); /* Length will be set later */ 498e5b75505Sopenharmony_ci 499e5b75505Sopenharmony_ci wpabuf_put_u8(buf, 1); /* Measurement Token */ 500e5b75505Sopenharmony_ci /* 501e5b75505Sopenharmony_ci * Parallel and Enable bits are 0; Duration, Request, and Report are 502e5b75505Sopenharmony_ci * reserved. 503e5b75505Sopenharmony_ci */ 504e5b75505Sopenharmony_ci wpabuf_put_u8(buf, 0); /* Measurement Request Mode */ 505e5b75505Sopenharmony_ci wpabuf_put_u8(buf, MEASURE_TYPE_FTM_RANGE); /* Measurement Type */ 506e5b75505Sopenharmony_ci 507e5b75505Sopenharmony_ci /* IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 */ 508e5b75505Sopenharmony_ci wpabuf_put_le16(buf, random_interval); /* Randomization Interval */ 509e5b75505Sopenharmony_ci wpabuf_put_u8(buf, min_ap); /* Minimum AP Count */ 510e5b75505Sopenharmony_ci 511e5b75505Sopenharmony_ci /* FTM Range Subelements */ 512e5b75505Sopenharmony_ci 513e5b75505Sopenharmony_ci /* 514e5b75505Sopenharmony_ci * Taking the neighbor report part of the range request from neighbor 515e5b75505Sopenharmony_ci * database instead of requesting the separate bits of data from the 516e5b75505Sopenharmony_ci * user. 517e5b75505Sopenharmony_ci */ 518e5b75505Sopenharmony_ci for (i = 0; i < n_responders; i++) { 519e5b75505Sopenharmony_ci struct hostapd_neighbor_entry *nr; 520e5b75505Sopenharmony_ci 521e5b75505Sopenharmony_ci nr = hostapd_neighbor_get(hapd, responders + ETH_ALEN * i, 522e5b75505Sopenharmony_ci NULL); 523e5b75505Sopenharmony_ci if (!nr) { 524e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Missing neighbor report for " 525e5b75505Sopenharmony_ci MACSTR, MAC2STR(responders + ETH_ALEN * i)); 526e5b75505Sopenharmony_ci wpabuf_free(buf); 527e5b75505Sopenharmony_ci return -1; 528e5b75505Sopenharmony_ci } 529e5b75505Sopenharmony_ci 530e5b75505Sopenharmony_ci if (wpabuf_tailroom(buf) < 2 + wpabuf_len(nr->nr)) { 531e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "Too long range request"); 532e5b75505Sopenharmony_ci wpabuf_free(buf); 533e5b75505Sopenharmony_ci return -1; 534e5b75505Sopenharmony_ci } 535e5b75505Sopenharmony_ci 536e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT); 537e5b75505Sopenharmony_ci wpabuf_put_u8(buf, wpabuf_len(nr->nr)); 538e5b75505Sopenharmony_ci wpabuf_put_buf(buf, nr->nr); 539e5b75505Sopenharmony_ci } 540e5b75505Sopenharmony_ci 541e5b75505Sopenharmony_ci /* Action + measurement type + token + reps + EID + len = 7 */ 542e5b75505Sopenharmony_ci *len = wpabuf_len(buf) - 7; 543e5b75505Sopenharmony_ci 544e5b75505Sopenharmony_ci ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, 545e5b75505Sopenharmony_ci wpabuf_head(buf), wpabuf_len(buf)); 546e5b75505Sopenharmony_ci wpabuf_free(buf); 547e5b75505Sopenharmony_ci if (ret) 548e5b75505Sopenharmony_ci return ret; 549e5b75505Sopenharmony_ci 550e5b75505Sopenharmony_ci hapd->range_req_active = 1; 551e5b75505Sopenharmony_ci 552e5b75505Sopenharmony_ci eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0, 553e5b75505Sopenharmony_ci hostapd_range_rep_timeout_handler, hapd, NULL); 554e5b75505Sopenharmony_ci 555e5b75505Sopenharmony_ci return 0; 556e5b75505Sopenharmony_ci} 557e5b75505Sopenharmony_ci 558e5b75505Sopenharmony_ci 559e5b75505Sopenharmony_civoid hostapd_clean_rrm(struct hostapd_data *hapd) 560e5b75505Sopenharmony_ci{ 561e5b75505Sopenharmony_ci hostapd_free_neighbor_db(hapd); 562e5b75505Sopenharmony_ci eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL); 563e5b75505Sopenharmony_ci hapd->lci_req_active = 0; 564e5b75505Sopenharmony_ci eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL); 565e5b75505Sopenharmony_ci hapd->range_req_active = 0; 566e5b75505Sopenharmony_ci} 567e5b75505Sopenharmony_ci 568e5b75505Sopenharmony_ci 569e5b75505Sopenharmony_ciint hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr, 570e5b75505Sopenharmony_ci u8 req_mode, const struct wpabuf *req) 571e5b75505Sopenharmony_ci{ 572e5b75505Sopenharmony_ci struct wpabuf *buf; 573e5b75505Sopenharmony_ci struct sta_info *sta = ap_get_sta(hapd, addr); 574e5b75505Sopenharmony_ci int ret; 575e5b75505Sopenharmony_ci enum beacon_report_mode mode; 576e5b75505Sopenharmony_ci const u8 *pos; 577e5b75505Sopenharmony_ci 578e5b75505Sopenharmony_ci /* Request data: 579e5b75505Sopenharmony_ci * Operating Class (1), Channel Number (1), Randomization Interval (2), 580e5b75505Sopenharmony_ci * Measurement Duration (2), Measurement Mode (1), BSSID (6), 581e5b75505Sopenharmony_ci * Optional Subelements (variable) 582e5b75505Sopenharmony_ci */ 583e5b75505Sopenharmony_ci if (wpabuf_len(req) < 13) { 584e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "Beacon request: Too short request data"); 585e5b75505Sopenharmony_ci return -1; 586e5b75505Sopenharmony_ci } 587e5b75505Sopenharmony_ci pos = wpabuf_head(req); 588e5b75505Sopenharmony_ci mode = pos[6]; 589e5b75505Sopenharmony_ci 590e5b75505Sopenharmony_ci if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) { 591e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 592e5b75505Sopenharmony_ci "Beacon request: " MACSTR " is not connected", 593e5b75505Sopenharmony_ci MAC2STR(addr)); 594e5b75505Sopenharmony_ci return -1; 595e5b75505Sopenharmony_ci } 596e5b75505Sopenharmony_ci 597e5b75505Sopenharmony_ci switch (mode) { 598e5b75505Sopenharmony_ci case BEACON_REPORT_MODE_PASSIVE: 599e5b75505Sopenharmony_ci if (!(sta->rrm_enabled_capa[0] & 600e5b75505Sopenharmony_ci WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE)) { 601e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 602e5b75505Sopenharmony_ci "Beacon request: " MACSTR 603e5b75505Sopenharmony_ci " does not support passive beacon report", 604e5b75505Sopenharmony_ci MAC2STR(addr)); 605e5b75505Sopenharmony_ci return -1; 606e5b75505Sopenharmony_ci } 607e5b75505Sopenharmony_ci break; 608e5b75505Sopenharmony_ci case BEACON_REPORT_MODE_ACTIVE: 609e5b75505Sopenharmony_ci if (!(sta->rrm_enabled_capa[0] & 610e5b75505Sopenharmony_ci WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE)) { 611e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 612e5b75505Sopenharmony_ci "Beacon request: " MACSTR 613e5b75505Sopenharmony_ci " does not support active beacon report", 614e5b75505Sopenharmony_ci MAC2STR(addr)); 615e5b75505Sopenharmony_ci return -1; 616e5b75505Sopenharmony_ci } 617e5b75505Sopenharmony_ci break; 618e5b75505Sopenharmony_ci case BEACON_REPORT_MODE_TABLE: 619e5b75505Sopenharmony_ci if (!(sta->rrm_enabled_capa[0] & 620e5b75505Sopenharmony_ci WLAN_RRM_CAPS_BEACON_REPORT_TABLE)) { 621e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 622e5b75505Sopenharmony_ci "Beacon request: " MACSTR 623e5b75505Sopenharmony_ci " does not support table beacon report", 624e5b75505Sopenharmony_ci MAC2STR(addr)); 625e5b75505Sopenharmony_ci return -1; 626e5b75505Sopenharmony_ci } 627e5b75505Sopenharmony_ci break; 628e5b75505Sopenharmony_ci default: 629e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 630e5b75505Sopenharmony_ci "Beacon request: Unknown measurement mode %d", mode); 631e5b75505Sopenharmony_ci return -1; 632e5b75505Sopenharmony_ci } 633e5b75505Sopenharmony_ci 634e5b75505Sopenharmony_ci buf = wpabuf_alloc(5 + 2 + 3 + wpabuf_len(req)); 635e5b75505Sopenharmony_ci if (!buf) 636e5b75505Sopenharmony_ci return -1; 637e5b75505Sopenharmony_ci 638e5b75505Sopenharmony_ci hapd->beacon_req_token++; 639e5b75505Sopenharmony_ci if (!hapd->beacon_req_token) 640e5b75505Sopenharmony_ci hapd->beacon_req_token++; 641e5b75505Sopenharmony_ci 642e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT); 643e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST); 644e5b75505Sopenharmony_ci wpabuf_put_u8(buf, hapd->beacon_req_token); 645e5b75505Sopenharmony_ci wpabuf_put_le16(buf, 0); /* Number of repetitions */ 646e5b75505Sopenharmony_ci 647e5b75505Sopenharmony_ci /* Measurement Request element */ 648e5b75505Sopenharmony_ci wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST); 649e5b75505Sopenharmony_ci wpabuf_put_u8(buf, 3 + wpabuf_len(req)); 650e5b75505Sopenharmony_ci wpabuf_put_u8(buf, 1); /* Measurement Token */ 651e5b75505Sopenharmony_ci wpabuf_put_u8(buf, req_mode); /* Measurement Request Mode */ 652e5b75505Sopenharmony_ci wpabuf_put_u8(buf, MEASURE_TYPE_BEACON); /* Measurement Type */ 653e5b75505Sopenharmony_ci wpabuf_put_buf(buf, req); 654e5b75505Sopenharmony_ci 655e5b75505Sopenharmony_ci ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr, 656e5b75505Sopenharmony_ci wpabuf_head(buf), wpabuf_len(buf)); 657e5b75505Sopenharmony_ci wpabuf_free(buf); 658e5b75505Sopenharmony_ci if (ret < 0) 659e5b75505Sopenharmony_ci return ret; 660e5b75505Sopenharmony_ci 661e5b75505Sopenharmony_ci return hapd->beacon_req_token; 662e5b75505Sopenharmony_ci} 663e5b75505Sopenharmony_ci 664e5b75505Sopenharmony_ci 665e5b75505Sopenharmony_civoid hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd, 666e5b75505Sopenharmony_ci const struct ieee80211_mgmt *mgmt, 667e5b75505Sopenharmony_ci size_t len, int ok) 668e5b75505Sopenharmony_ci{ 669e5b75505Sopenharmony_ci if (len < 24 + 3) 670e5b75505Sopenharmony_ci return; 671e5b75505Sopenharmony_ci wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_REQ_TX_STATUS MACSTR 672e5b75505Sopenharmony_ci " %u ack=%d", MAC2STR(mgmt->da), 673e5b75505Sopenharmony_ci mgmt->u.action.u.rrm.dialog_token, ok); 674e5b75505Sopenharmony_ci} 675