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