blob: 5ca87c0be942ddc7cab54bfd989f149b9d72fbf4 [file] [log] [blame]
Dmitry Shmidt849734c2016-05-27 09:59:01 -07001/*
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
23static void hostapd_lci_rep_timeout_handler(void *eloop_data, void *user_ctx)
24{
25 struct hostapd_data *hapd = eloop_data;
26
27 wpa_printf(MSG_DEBUG, "RRM: LCI request (token %u) timed out",
28 hapd->lci_req_token);
29 hapd->lci_req_active = 0;
30}
31
32
33static void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token,
34 const u8 *pos, size_t len)
35{
36 if (!hapd->lci_req_active || hapd->lci_req_token != token) {
37 wpa_printf(MSG_DEBUG, "Unexpected LCI report, token %u", token);
38 return;
39 }
40
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);
44}
45
46
47static void hostapd_range_rep_timeout_handler(void *eloop_data, void *user_ctx)
48{
49 struct hostapd_data *hapd = eloop_data;
50
51 wpa_printf(MSG_DEBUG, "RRM: Range request (token %u) timed out",
52 hapd->range_req_token);
53 hapd->range_req_active = 0;
54}
55
56
57static void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token,
58 const u8 *pos, size_t len)
59{
60 if (!hapd->range_req_active || hapd->range_req_token != token) {
61 wpa_printf(MSG_DEBUG, "Unexpected range report, token %u",
62 token);
63 return;
64 }
65
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);
69}
70
71
72static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd,
73 const u8 *buf, size_t len)
74{
75 const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
76 const u8 *pos, *ie, *end;
77 u8 token;
78
79 end = buf + len;
80 token = mgmt->u.action.u.rrm.dialog_token;
81 pos = mgmt->u.action.u.rrm.variable;
82
83 while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) {
84 if (ie[1] < 5) {
85 wpa_printf(MSG_DEBUG, "Bad Measurement Report element");
86 break;
87 }
88
89 wpa_printf(MSG_DEBUG, "Measurement report type %u", ie[4]);
90
91 switch (ie[4]) {
92 case MEASURE_TYPE_LCI:
93 hostapd_handle_lci_report(hapd, token, ie + 2, ie[1]);
94 break;
95 case MEASURE_TYPE_FTM_RANGE:
96 hostapd_handle_range_report(hapd, token, ie + 2, ie[1]);
97 break;
98 default:
99 wpa_printf(MSG_DEBUG,
100 "Measurement report type %u is not supported",
101 ie[4]);
102 break;
103 }
104
105 pos = ie + ie[1] + 2;
106 }
107}
108
109
110static 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 if (len < 3 + 1 + 4)
116 return 0;
117
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);
122
123 return 0;
124}
125
126
127static 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
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -0800132 if (nr->stationary || max_age == 0xffff)
133 return 1;
134
Dmitry Shmidt849734c2016-05-27 09:59:01 -0700135 if (!max_age)
136 return 0;
137
Dmitry Shmidt849734c2016-05-27 09:59:01 -0700138 if (os_get_time(&curr))
139 return 0;
140
141 os_time_sub(&curr, &nr->lci_date, &diff);
142
143 /* avoid overflow */
144 if (diff.sec > 0xffff)
145 return 0;
146
147 /* LCI age is calculated in 10th of a second units. */
148 diff_l = diff.sec * 10 + diff.usec / 100000;
149
150 return max_age > diff_l;
151}
152
153
154static size_t hostapd_neighbor_report_len(struct wpabuf *buf,
155 struct hostapd_neighbor_entry *nr,
156 int send_lci, int send_civic)
157{
158 size_t len = 2 + wpabuf_len(nr->nr);
159
160 if (send_lci && nr->lci)
161 len += 2 + wpabuf_len(nr->lci);
162
163 if (send_civic && nr->civic)
164 len += 2 + wpabuf_len(nr->civic);
165
166 return len;
167}
168
169
170static 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 buf = wpabuf_alloc(3 + IEEE80211_MAX_MMPDU_SIZE);
185 if (!buf)
186 return;
187
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);
191
192 dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
193 list) {
194 int send_lci;
195 size_t len;
196
197 if (ssid->ssid_len != nr->ssid.ssid_len ||
198 os_memcmp(ssid->ssid, nr->ssid.ssid, ssid->ssid_len) != 0)
199 continue;
200
201 send_lci = (lci != 0) && hostapd_check_lci_age(nr, lci_max_age);
202 len = hostapd_neighbor_report_len(buf, nr, send_lci, civic);
203
204 if (len - 2 > 0xff) {
205 wpa_printf(MSG_DEBUG,
206 "NR entry for " MACSTR " exceeds 0xFF bytes",
207 MAC2STR(nr->bssid));
208 continue;
209 }
210
211 if (len > wpabuf_tailroom(buf))
212 break;
213
214 wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
215 wpabuf_put_u8(buf, len - 2);
216 wpabuf_put_buf(buf, nr->nr);
217
218 if (send_lci && nr->lci) {
219 wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
220 wpabuf_put_u8(buf, wpabuf_len(nr->lci));
221 /*
222 * Override measurement token - the first byte of the
223 * Measurement Report element.
224 */
225 msmt_token = wpabuf_put(buf, 0);
226 wpabuf_put_buf(buf, nr->lci);
227 *msmt_token = lci;
228 }
229
230 if (civic && nr->civic) {
231 wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
232 wpabuf_put_u8(buf, wpabuf_len(nr->civic));
233 /*
234 * Override measurement token - the first byte of the
235 * Measurement Report element.
236 */
237 msmt_token = wpabuf_put(buf, 0);
238 wpabuf_put_buf(buf, nr->civic);
239 *msmt_token = civic;
240 }
241 }
242
243 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
244 wpabuf_head(buf), wpabuf_len(buf));
245 wpabuf_free(buf);
246}
247
248
249static void hostapd_handle_nei_report_req(struct hostapd_data *hapd,
250 const u8 *buf, size_t len)
251{
252 const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
253 const u8 *pos, *ie, *end;
254 struct wpa_ssid_value ssid = {
255 .ssid_len = 0
256 };
257 u8 token;
258 u8 lci = 0, civic = 0; /* Measurement tokens */
259 u16 lci_max_age = 0;
260
261 if (!(hapd->conf->radio_measurements[0] &
262 WLAN_RRM_CAPS_NEIGHBOR_REPORT))
263 return;
264
265 end = buf + len;
266
267 token = mgmt->u.action.u.rrm.dialog_token;
268 pos = mgmt->u.action.u.rrm.variable;
269 len = end - pos;
270
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);
275 } else {
276 ssid.ssid_len = hapd->conf->ssid.ssid_len;
277 os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
278 }
279
280 while ((ie = get_ie(pos, len, WLAN_EID_MEASURE_REQUEST))) {
281 if (ie[1] < 3)
282 break;
283
284 wpa_printf(MSG_DEBUG,
285 "Neighbor report request, measure type %u",
286 ie[4]);
287
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,
292 ie[1]);
293 break;
294 case MEASURE_TYPE_LOCATION_CIVIC:
295 civic = ie[2]; /* Measurement token */
296 break;
297 }
298
299 pos = ie + ie[1] + 2;
300 len = end - pos;
301 }
302
303 hostapd_send_nei_report_resp(hapd, mgmt->sa, token, &ssid, lci, civic,
304 lci_max_age);
305}
306
307
308void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
309 const u8 *buf, size_t len)
310{
311 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 if (len < IEEE80211_HDRLEN + 3)
318 return;
319
320 wpa_printf(MSG_DEBUG, "Radio measurement frame, action %u from " MACSTR,
321 mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa));
322
323 switch (mgmt->u.action.u.rrm.action) {
324 case WLAN_RRM_RADIO_MEASUREMENT_REPORT:
325 hostapd_handle_radio_msmt_report(hapd, buf, len);
326 break;
327 case WLAN_RRM_NEIGHBOR_REPORT_REQUEST:
328 hostapd_handle_nei_report_req(hapd, buf, len);
329 break;
330 default:
331 wpa_printf(MSG_DEBUG, "RRM action %u is not supported",
332 mgmt->u.action.u.rrm.action);
333 break;
334 }
335}
336
337
338int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr)
339{
340 struct wpabuf *buf;
341 struct sta_info *sta = ap_get_sta(hapd, addr);
342 int ret;
343
344 if (!sta) {
345 wpa_printf(MSG_INFO,
346 "Request LCI: Destination address is not in station list");
347 return -1;
348 }
349
350 if (!(sta->flags & WLAN_STA_AUTHORIZED)) {
351 wpa_printf(MSG_INFO,
352 "Request LCI: Destination address is not connected");
353 return -1;
354 }
355
356 if (!(sta->rrm_enabled_capa[1] & WLAN_RRM_CAPS_LCI_MEASUREMENT)) {
357 wpa_printf(MSG_INFO,
358 "Request LCI: Station does not support LCI in RRM");
359 return -1;
360 }
361
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,
367 NULL);
368 }
369
370 /* Measurement request (5) + Measurement element with LCI (10) */
371 buf = wpabuf_alloc(5 + 10);
372 if (!buf)
373 return -1;
374
375 hapd->lci_req_token++;
376 /* For wraparounds - the token must be nonzero */
377 if (!hapd->lci_req_token)
378 hapd->lci_req_token++;
379
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 */
384
385 wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
386 wpabuf_put_u8(buf, 3 + 1 + 4);
387
388 wpabuf_put_u8(buf, 1); /* Measurement Token */
389 /*
390 * Parallel and Enable bits are 0, Duration, Request, and Report are
391 * reserved.
392 */
393 wpabuf_put_u8(buf, 0);
394 wpabuf_put_u8(buf, MEASURE_TYPE_LCI);
395
396 wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
397
398 wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE);
399 wpabuf_put_u8(buf, 2);
400 wpabuf_put_le16(buf, 0xffff);
401
402 ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
403 wpabuf_head(buf), wpabuf_len(buf));
404 wpabuf_free(buf);
405 if (ret)
406 return ret;
407
408 hapd->lci_req_active = 1;
409
410 eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
411 hostapd_lci_rep_timeout_handler, hapd, NULL);
412
413 return 0;
414}
415
416
417int 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 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);
430
431 if (min_ap == 0 || min_ap > n_responders) {
432 wpa_printf(MSG_INFO, "Request range: Wrong min AP count");
433 return -1;
434 }
435
436 sta = ap_get_sta(hapd, addr);
437 if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
438 wpa_printf(MSG_INFO,
439 "Request range: Destination address is not connected");
440 return -1;
441 }
442
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");
446 return -1;
447 }
448
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,
455 NULL);
456 }
457
458 /* Action + measurement type + token + reps + EID + len = 7 */
459 buf = wpabuf_alloc(7 + 255);
460 if (!buf)
461 return -1;
462
463 hapd->range_req_token++;
464 if (!hapd->range_req_token) /* For wraparounds */
465 hapd->range_req_token++;
466
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 */
472
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 */
476
477 wpabuf_put_u8(buf, 1); /* Measurement Token */
478 /*
479 * Parallel and Enable bits are 0; Duration, Request, and Report are
480 * reserved.
481 */
482 wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
483 wpabuf_put_u8(buf, MEASURE_TYPE_FTM_RANGE); /* Measurement Type */
484
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 */
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 for (i = 0; i < n_responders; i++) {
497 struct hostapd_neighbor_entry *nr;
498
499 nr = hostapd_neighbor_get(hapd, responders + ETH_ALEN * i,
500 NULL);
501 if (!nr) {
502 wpa_printf(MSG_INFO, "Missing neighbor report for "
503 MACSTR, MAC2STR(responders + ETH_ALEN * i));
504 wpabuf_free(buf);
505 return -1;
506 }
507
508 if (wpabuf_tailroom(buf) < 2 + wpabuf_len(nr->nr)) {
509 wpa_printf(MSG_ERROR, "Too long range request");
510 wpabuf_free(buf);
511 return -1;
512 }
513
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);
517 }
518
519 /* Action + measurement type + token + reps + EID + len = 7 */
520 *len = wpabuf_len(buf) - 7;
521
522 ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
523 wpabuf_head(buf), wpabuf_len(buf));
524 wpabuf_free(buf);
525 if (ret)
526 return ret;
527
528 hapd->range_req_active = 1;
529
530 eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
531 hostapd_range_rep_timeout_handler, hapd, NULL);
532
533 return 0;
534}
535
536
537void hostapd_clean_rrm(struct hostapd_data *hapd)
538{
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;
544}