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