2 * hostapd / Radio Measurement (RRM)
3 * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
4 * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
6 * This software may be distributed under the terms of the BSD license.
7 * See README for more details.
10 #include "utils/includes.h"
12 #include "utils/common.h"
14 #include "ap_drv_ops.h"
17 #include "neighbor_db.h"
20 #define HOSTAPD_RRM_REQUEST_TIMEOUT 5
23 static void hostapd_lci_rep_timeout_handler(void *eloop_data, void *user_ctx)
25 struct hostapd_data *hapd = eloop_data;
27 wpa_printf(MSG_DEBUG, "RRM: LCI request (token %u) timed out",
29 hapd->lci_req_active = 0;
33 static void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token,
34 const u8 *pos, size_t len)
36 if (!hapd->lci_req_active || hapd->lci_req_token != token) {
37 wpa_printf(MSG_DEBUG, "Unexpected LCI report, token %u", token);
41 hapd->lci_req_active = 0;
42 eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
43 wpa_printf(MSG_DEBUG, "LCI report token %u len %zu", token, len);
47 static void hostapd_range_rep_timeout_handler(void *eloop_data, void *user_ctx)
49 struct hostapd_data *hapd = eloop_data;
51 wpa_printf(MSG_DEBUG, "RRM: Range request (token %u) timed out",
52 hapd->range_req_token);
53 hapd->range_req_active = 0;
57 static void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token,
58 const u8 *pos, size_t len)
60 if (!hapd->range_req_active || hapd->range_req_token != token) {
61 wpa_printf(MSG_DEBUG, "Unexpected range report, token %u",
66 hapd->range_req_active = 0;
67 eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
68 wpa_printf(MSG_DEBUG, "Range report token %u len %zu", token, len);
72 static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd,
73 const u8 *buf, size_t len)
75 const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
76 const u8 *pos, *ie, *end;
80 token = mgmt->u.action.u.rrm.dialog_token;
81 pos = mgmt->u.action.u.rrm.variable;
83 while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) {
85 wpa_printf(MSG_DEBUG, "Bad Measurement Report element");
89 wpa_printf(MSG_DEBUG, "Measurement report type %u", ie[4]);
92 case MEASURE_TYPE_LCI:
93 hostapd_handle_lci_report(hapd, token, ie + 2, ie[1]);
95 case MEASURE_TYPE_FTM_RANGE:
96 hostapd_handle_range_report(hapd, token, ie + 2, ie[1]);
100 "Measurement report type %u is not supported",
105 pos = ie + ie[1] + 2;
110 static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len)
114 /* Range Request element + Location Subject + Maximum Age subelement */
118 /* Subelements are arranged as IEs */
119 subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE);
120 if (subelem && subelem[1] == 2)
121 return *(u16 *) (subelem + 2);
127 static int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age)
129 struct os_time curr, diff;
130 unsigned long diff_l;
135 if (max_age == 0xffff)
138 if (os_get_time(&curr))
141 os_time_sub(&curr, &nr->lci_date, &diff);
144 if (diff.sec > 0xffff)
147 /* LCI age is calculated in 10th of a second units. */
148 diff_l = diff.sec * 10 + diff.usec / 100000;
150 return max_age > diff_l;
154 static size_t hostapd_neighbor_report_len(struct wpabuf *buf,
155 struct hostapd_neighbor_entry *nr,
156 int send_lci, int send_civic)
158 size_t len = 2 + wpabuf_len(nr->nr);
160 if (send_lci && nr->lci)
161 len += 2 + wpabuf_len(nr->lci);
163 if (send_civic && nr->civic)
164 len += 2 + wpabuf_len(nr->civic);
170 static void hostapd_send_nei_report_resp(struct hostapd_data *hapd,
171 const u8 *addr, u8 dialog_token,
172 struct wpa_ssid_value *ssid, u8 lci,
173 u8 civic, u16 lci_max_age)
175 struct hostapd_neighbor_entry *nr;
180 * The number and length of the Neighbor Report elements in a Neighbor
181 * Report frame is limited by the maximum allowed MMPDU size; + 3 bytes
184 buf = wpabuf_alloc(3 + IEEE80211_MAX_MMPDU_SIZE);
188 wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
189 wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_RESPONSE);
190 wpabuf_put_u8(buf, dialog_token);
192 dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
197 if (ssid->ssid_len != nr->ssid.ssid_len ||
198 os_memcmp(ssid->ssid, nr->ssid.ssid, ssid->ssid_len) != 0)
201 send_lci = (lci != 0) && hostapd_check_lci_age(nr, lci_max_age);
202 len = hostapd_neighbor_report_len(buf, nr, send_lci, civic);
204 if (len - 2 > 0xff) {
205 wpa_printf(MSG_DEBUG,
206 "NR entry for " MACSTR " exceeds 0xFF bytes",
211 if (len > wpabuf_tailroom(buf))
214 wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
215 wpabuf_put_u8(buf, len - 2);
216 wpabuf_put_buf(buf, nr->nr);
218 if (send_lci && nr->lci) {
219 wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
220 wpabuf_put_u8(buf, wpabuf_len(nr->lci));
222 * Override measurement token - the first byte of the
223 * Measurement Report element.
225 msmt_token = wpabuf_put(buf, 0);
226 wpabuf_put_buf(buf, nr->lci);
230 if (civic && nr->civic) {
231 wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
232 wpabuf_put_u8(buf, wpabuf_len(nr->civic));
234 * Override measurement token - the first byte of the
235 * Measurement Report element.
237 msmt_token = wpabuf_put(buf, 0);
238 wpabuf_put_buf(buf, nr->civic);
243 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
244 wpabuf_head(buf), wpabuf_len(buf));
249 static void hostapd_handle_nei_report_req(struct hostapd_data *hapd,
250 const u8 *buf, size_t len)
252 const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
253 const u8 *pos, *ie, *end;
254 struct wpa_ssid_value ssid = {
258 u8 lci = 0, civic = 0; /* Measurement tokens */
261 if (!(hapd->conf->radio_measurements[0] &
262 WLAN_RRM_CAPS_NEIGHBOR_REPORT))
267 token = mgmt->u.action.u.rrm.dialog_token;
268 pos = mgmt->u.action.u.rrm.variable;
271 ie = get_ie(pos, len, WLAN_EID_SSID);
272 if (ie && ie[1] && ie[1] <= SSID_MAX_LEN) {
273 ssid.ssid_len = ie[1];
274 os_memcpy(ssid.ssid, ie + 2, ssid.ssid_len);
276 ssid.ssid_len = hapd->conf->ssid.ssid_len;
277 os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
280 while ((ie = get_ie(pos, len, WLAN_EID_MEASURE_REQUEST))) {
284 wpa_printf(MSG_DEBUG,
285 "Neighbor report request, measure type %u",
288 switch (ie[4]) { /* Measurement Type */
289 case MEASURE_TYPE_LCI:
290 lci = ie[2]; /* Measurement Token */
291 lci_max_age = hostapd_parse_location_lci_req_age(ie + 2,
294 case MEASURE_TYPE_LOCATION_CIVIC:
295 civic = ie[2]; /* Measurement token */
299 pos = ie + ie[1] + 2;
303 hostapd_send_nei_report_resp(hapd, mgmt->sa, token, &ssid, lci, civic,
308 void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
309 const u8 *buf, size_t len)
311 const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
314 * Check for enough bytes: header + (1B)Category + (1B)Action +
317 if (len < IEEE80211_HDRLEN + 3)
320 wpa_printf(MSG_DEBUG, "Radio measurement frame, action %u from " MACSTR,
321 mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa));
323 switch (mgmt->u.action.u.rrm.action) {
324 case WLAN_RRM_RADIO_MEASUREMENT_REPORT:
325 hostapd_handle_radio_msmt_report(hapd, buf, len);
327 case WLAN_RRM_NEIGHBOR_REPORT_REQUEST:
328 hostapd_handle_nei_report_req(hapd, buf, len);
331 wpa_printf(MSG_DEBUG, "RRM action %u is not supported",
332 mgmt->u.action.u.rrm.action);
338 int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr)
341 struct sta_info *sta = ap_get_sta(hapd, addr);
346 "Request LCI: Destination address is not in station list");
350 if (!(sta->flags & WLAN_STA_AUTHORIZED)) {
352 "Request LCI: Destination address is not connected");
356 if (!(sta->rrm_enabled_capa[1] & WLAN_RRM_CAPS_LCI_MEASUREMENT)) {
358 "Request LCI: Station does not support LCI in RRM");
362 if (hapd->lci_req_active) {
363 wpa_printf(MSG_DEBUG,
364 "Request LCI: LCI request is already in process, overriding");
365 hapd->lci_req_active = 0;
366 eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd,
370 /* Measurement request (5) + Measurement element with LCI (10) */
371 buf = wpabuf_alloc(5 + 10);
375 hapd->lci_req_token++;
376 /* For wraparounds - the token must be nonzero */
377 if (!hapd->lci_req_token)
378 hapd->lci_req_token++;
380 wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
381 wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
382 wpabuf_put_u8(buf, hapd->lci_req_token);
383 wpabuf_put_le16(buf, 0); /* Number of repetitions */
385 wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
386 wpabuf_put_u8(buf, 3 + 1 + 4);
388 wpabuf_put_u8(buf, 1); /* Measurement Token */
390 * Parallel and Enable bits are 0, Duration, Request, and Report are
393 wpabuf_put_u8(buf, 0);
394 wpabuf_put_u8(buf, MEASURE_TYPE_LCI);
396 wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
398 wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE);
399 wpabuf_put_u8(buf, 2);
400 wpabuf_put_le16(buf, 0xffff);
402 ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
403 wpabuf_head(buf), wpabuf_len(buf));
408 hapd->lci_req_active = 1;
410 eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
411 hostapd_lci_rep_timeout_handler, hapd, NULL);
417 int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
418 u16 random_interval, u8 min_ap,
419 const u8 *responders, unsigned int n_responders)
422 struct sta_info *sta;
427 wpa_printf(MSG_DEBUG, "Request range: dest addr " MACSTR
428 " rand interval %u min AP %u n_responders %u", MAC2STR(addr),
429 random_interval, min_ap, n_responders);
431 if (min_ap == 0 || min_ap > n_responders) {
432 wpa_printf(MSG_INFO, "Request range: Wrong min AP count");
436 sta = ap_get_sta(hapd, addr);
437 if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
439 "Request range: Destination address is not connected");
443 if (!(sta->rrm_enabled_capa[4] & WLAN_RRM_CAPS_FTM_RANGE_REPORT)) {
444 wpa_printf(MSG_ERROR,
445 "Request range: Destination station does not support FTM range report in RRM");
449 if (hapd->range_req_active) {
450 wpa_printf(MSG_DEBUG,
451 "Request range: Range request is already in process; overriding");
452 hapd->range_req_active = 0;
453 eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
454 hostapd_range_rep_timeout_handler, hapd,
458 /* Action + measurement type + token + reps + EID + len = 7 */
459 buf = wpabuf_alloc(7 + 255);
463 hapd->range_req_token++;
464 if (!hapd->range_req_token) /* For wraparounds */
465 hapd->range_req_token++;
467 /* IEEE P802.11-REVmc/D5.0, 9.6.7.2 */
468 wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
469 wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
470 wpabuf_put_u8(buf, hapd->range_req_token); /* Dialog Token */
471 wpabuf_put_le16(buf, 0); /* Number of Repetitions */
473 /* IEEE P802.11-REVmc/D5.0, 9.4.2.21 */
474 wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
475 len = wpabuf_put(buf, 1); /* Length will be set later */
477 wpabuf_put_u8(buf, 1); /* Measurement Token */
479 * Parallel and Enable bits are 0; Duration, Request, and Report are
482 wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
483 wpabuf_put_u8(buf, MEASURE_TYPE_FTM_RANGE); /* Measurement Type */
485 /* IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 */
486 wpabuf_put_le16(buf, random_interval); /* Randomization Interval */
487 wpabuf_put_u8(buf, min_ap); /* Minimum AP Count */
489 /* FTM Range Subelements */
492 * Taking the neighbor report part of the range request from neighbor
493 * database instead of requesting the separate bits of data from the
496 for (i = 0; i < n_responders; i++) {
497 struct hostapd_neighbor_entry *nr;
499 nr = hostapd_neighbor_get(hapd, responders + ETH_ALEN * i,
502 wpa_printf(MSG_INFO, "Missing neighbor report for "
503 MACSTR, MAC2STR(responders + ETH_ALEN * i));
508 if (wpabuf_tailroom(buf) < 2 + wpabuf_len(nr->nr)) {
509 wpa_printf(MSG_ERROR, "Too long range request");
514 wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
515 wpabuf_put_u8(buf, wpabuf_len(nr->nr));
516 wpabuf_put_buf(buf, nr->nr);
519 /* Action + measurement type + token + reps + EID + len = 7 */
520 *len = wpabuf_len(buf) - 7;
522 ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
523 wpabuf_head(buf), wpabuf_len(buf));
528 hapd->range_req_active = 1;
530 eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
531 hostapd_range_rep_timeout_handler, hapd, NULL);
537 void hostapd_clean_rrm(struct hostapd_data *hapd)
539 hostpad_free_neighbor_db(hapd);
540 eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
541 hapd->lci_req_active = 0;
542 eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
543 hapd->range_req_active = 0;