]> git.ipfire.org Git - thirdparty/hostap.git/blame - wpa_supplicant/rrm.c
wpa_supplicant: Limit RRM response size to MMPDU size
[thirdparty/hostap.git] / wpa_supplicant / rrm.c
CommitLineData
ec493469
AS
1/*
2 * wpa_supplicant - Radio Measurements
3 * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi>
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "includes.h"
10
11#include "utils/common.h"
12#include "utils/eloop.h"
13#include "common/ieee802_11_common.h"
14#include "wpa_supplicant_i.h"
15#include "driver_i.h"
16#include "bss.h"
17
18
19static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx)
20{
21 struct rrm_data *rrm = data;
22
23 if (!rrm->notify_neighbor_rep) {
24 wpa_printf(MSG_ERROR,
25 "RRM: Unexpected neighbor report timeout");
26 return;
27 }
28
29 wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE");
30 rrm->notify_neighbor_rep(rrm->neighbor_rep_cb_ctx, NULL);
31
32 rrm->notify_neighbor_rep = NULL;
33 rrm->neighbor_rep_cb_ctx = NULL;
34}
35
36
37/*
38 * wpas_rrm_reset - Clear and reset all RRM data in wpa_supplicant
39 * @wpa_s: Pointer to wpa_supplicant
40 */
41void wpas_rrm_reset(struct wpa_supplicant *wpa_s)
42{
43 wpa_s->rrm.rrm_used = 0;
44
45 eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
46 NULL);
47 if (wpa_s->rrm.notify_neighbor_rep)
48 wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL);
49 wpa_s->rrm.next_neighbor_rep_token = 1;
50}
51
52
53/*
54 * wpas_rrm_process_neighbor_rep - Handle incoming neighbor report
55 * @wpa_s: Pointer to wpa_supplicant
56 * @report: Neighbor report buffer, prefixed by a 1-byte dialog token
57 * @report_len: Length of neighbor report buffer
58 */
59void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
60 const u8 *report, size_t report_len)
61{
62 struct wpabuf *neighbor_rep;
63
64 wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len);
65 if (report_len < 1)
66 return;
67
68 if (report[0] != wpa_s->rrm.next_neighbor_rep_token - 1) {
69 wpa_printf(MSG_DEBUG,
70 "RRM: Discarding neighbor report with token %d (expected %d)",
71 report[0], wpa_s->rrm.next_neighbor_rep_token - 1);
72 return;
73 }
74
75 eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
76 NULL);
77
78 if (!wpa_s->rrm.notify_neighbor_rep) {
79 wpa_printf(MSG_ERROR, "RRM: Unexpected neighbor report");
80 return;
81 }
82
83 /* skipping the first byte, which is only an id (dialog token) */
84 neighbor_rep = wpabuf_alloc(report_len - 1);
85 if (neighbor_rep == NULL)
86 return;
87 wpabuf_put_data(neighbor_rep, report + 1, report_len - 1);
88 wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)",
89 report[0]);
90 wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx,
91 neighbor_rep);
92 wpa_s->rrm.notify_neighbor_rep = NULL;
93 wpa_s->rrm.neighbor_rep_cb_ctx = NULL;
94}
95
96
97#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
98/* Workaround different, undefined for Windows, error codes used here */
99#define ENOTCONN -1
100#define EOPNOTSUPP -1
101#define ECANCELED -1
102#endif
103
104/* Measurement Request element + Location Subject + Maximum Age subelement */
105#define MEASURE_REQUEST_LCI_LEN (3 + 1 + 4)
106/* Measurement Request element + Location Civic Request */
107#define MEASURE_REQUEST_CIVIC_LEN (3 + 5)
108
109
110/**
111 * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP
112 * @wpa_s: Pointer to wpa_supplicant
113 * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE
114 * is sent in the request.
115 * @lci: if set, neighbor request will include LCI request
116 * @civic: if set, neighbor request will include civic location request
117 * @cb: Callback function to be called once the requested report arrives, or
118 * timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds.
119 * In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's
120 * the requester's responsibility to free it.
121 * In the latter case NULL will be sent in 'neighbor_rep'.
122 * @cb_ctx: Context value to send the callback function
123 * Returns: 0 in case of success, negative error code otherwise
124 *
125 * In case there is a previous request which has not been answered yet, the
126 * new request fails. The caller may retry after RRM_NEIGHBOR_REPORT_TIMEOUT.
127 * Request must contain a callback function.
128 */
129int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
130 const struct wpa_ssid_value *ssid,
131 int lci, int civic,
132 void (*cb)(void *ctx,
133 struct wpabuf *neighbor_rep),
134 void *cb_ctx)
135{
136 struct wpabuf *buf;
137 const u8 *rrm_ie;
138
139 if (wpa_s->wpa_state != WPA_COMPLETED || wpa_s->current_ssid == NULL) {
140 wpa_printf(MSG_DEBUG, "RRM: No connection, no RRM.");
141 return -ENOTCONN;
142 }
143
144 if (!wpa_s->rrm.rrm_used) {
145 wpa_printf(MSG_DEBUG, "RRM: No RRM in current connection.");
146 return -EOPNOTSUPP;
147 }
148
149 rrm_ie = wpa_bss_get_ie(wpa_s->current_bss,
150 WLAN_EID_RRM_ENABLED_CAPABILITIES);
151 if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) ||
152 !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
153 wpa_printf(MSG_DEBUG,
154 "RRM: No network support for Neighbor Report.");
155 return -EOPNOTSUPP;
156 }
157
158 if (!cb) {
159 wpa_printf(MSG_DEBUG,
160 "RRM: Neighbor Report request must provide a callback.");
161 return -EINVAL;
162 }
163
164 /* Refuse if there's a live request */
165 if (wpa_s->rrm.notify_neighbor_rep) {
166 wpa_printf(MSG_DEBUG,
167 "RRM: Currently handling previous Neighbor Report.");
168 return -EBUSY;
169 }
170
171 /* 3 = action category + action code + dialog token */
172 buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0) +
173 (lci ? 2 + MEASURE_REQUEST_LCI_LEN : 0) +
174 (civic ? 2 + MEASURE_REQUEST_CIVIC_LEN : 0));
175 if (buf == NULL) {
176 wpa_printf(MSG_DEBUG,
177 "RRM: Failed to allocate Neighbor Report Request");
178 return -ENOMEM;
179 }
180
181 wpa_printf(MSG_DEBUG, "RRM: Neighbor report request (for %s), token=%d",
182 (ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""),
183 wpa_s->rrm.next_neighbor_rep_token);
184
185 wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
186 wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST);
187 wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token);
188 if (ssid) {
189 wpabuf_put_u8(buf, WLAN_EID_SSID);
190 wpabuf_put_u8(buf, ssid->ssid_len);
191 wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len);
192 }
193
194 if (lci) {
195 /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */
196 wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
197 wpabuf_put_u8(buf, MEASURE_REQUEST_LCI_LEN);
198
199 /*
200 * Measurement token; nonzero number that is unique among the
201 * Measurement Request elements in a particular frame.
202 */
203 wpabuf_put_u8(buf, 1); /* Measurement Token */
204
205 /*
206 * Parallel, Enable, Request, and Report bits are 0, Duration is
207 * reserved.
208 */
209 wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
210 wpabuf_put_u8(buf, MEASURE_TYPE_LCI); /* Measurement Type */
211
212 /* IEEE P802.11-REVmc/D5.0 9.4.2.21.10 - LCI request */
213 /* Location Subject */
214 wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
215
216 /* Optional Subelements */
217 /*
218 * IEEE P802.11-REVmc/D5.0 Figure 9-170
219 * The Maximum Age subelement is required, otherwise the AP can
220 * send only data that was determined after receiving the
221 * request. Setting it here to unlimited age.
222 */
223 wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE);
224 wpabuf_put_u8(buf, 2);
225 wpabuf_put_le16(buf, 0xffff);
226 }
227
228 if (civic) {
229 /* IEEE P802.11-REVmc/D5.0 9.4.2.21 */
230 wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
231 wpabuf_put_u8(buf, MEASURE_REQUEST_CIVIC_LEN);
232
233 /*
234 * Measurement token; nonzero number that is unique among the
235 * Measurement Request elements in a particular frame.
236 */
237 wpabuf_put_u8(buf, 2); /* Measurement Token */
238
239 /*
240 * Parallel, Enable, Request, and Report bits are 0, Duration is
241 * reserved.
242 */
243 wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
244 /* Measurement Type */
245 wpabuf_put_u8(buf, MEASURE_TYPE_LOCATION_CIVIC);
246
247 /* IEEE P802.11-REVmc/D5.0 9.4.2.21.14:
248 * Location Civic request */
249 /* Location Subject */
250 wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
251 wpabuf_put_u8(buf, 0); /* Civic Location Type: IETF RFC 4776 */
252 /* Location Service Interval Units: Seconds */
253 wpabuf_put_u8(buf, 0);
254 /* Location Service Interval: 0 - Only one report is requested
255 */
256 wpabuf_put_le16(buf, 0);
257 /* No optional subelements */
258 }
259
260 wpa_s->rrm.next_neighbor_rep_token++;
261
262 if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
263 wpa_s->own_addr, wpa_s->bssid,
264 wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
265 wpa_printf(MSG_DEBUG,
266 "RRM: Failed to send Neighbor Report Request");
267 wpabuf_free(buf);
268 return -ECANCELED;
269 }
270
271 wpa_s->rrm.neighbor_rep_cb_ctx = cb_ctx;
272 wpa_s->rrm.notify_neighbor_rep = cb;
273 eloop_register_timeout(RRM_NEIGHBOR_REPORT_TIMEOUT, 0,
274 wpas_rrm_neighbor_rep_timeout_handler,
275 &wpa_s->rrm, NULL);
276
277 wpabuf_free(buf);
278 return 0;
279}
280
281
282static struct wpabuf * wpas_rrm_build_lci_report(struct wpa_supplicant *wpa_s,
283 const u8 *request, size_t len,
284 struct wpabuf *report)
285{
286 u8 token, type, subject;
287 u16 max_age = 0;
288 struct os_reltime t, diff;
289 unsigned long diff_l;
290 u8 *ptoken;
291 const u8 *subelem;
292
293 if (!wpa_s->lci || len < 3 + 4)
294 return report;
295
296 token = *request++;
297 /* Measurement request mode isn't used */
298 request++;
299 type = *request++;
300 subject = *request++;
301 len -= 4;
302
303 wpa_printf(MSG_DEBUG,
304 "Measurement request token %u type %u location subject %u",
305 token, type, subject);
306
307 if (type != MEASURE_TYPE_LCI || subject != LOCATION_SUBJECT_REMOTE) {
308 wpa_printf(MSG_INFO,
309 "Not building LCI report - bad type or location subject");
310 return report;
311 }
312
313 /* Subelements are formatted exactly like elements */
314 subelem = get_ie(request, len, LCI_REQ_SUBELEM_MAX_AGE);
315 if (subelem && subelem[1] == 2)
316 max_age = WPA_GET_LE16(subelem + 2);
317
318 if (os_get_reltime(&t))
319 return report;
320
321 os_reltime_sub(&t, &wpa_s->lci_time, &diff);
322 /* LCI age is calculated in 10th of a second units. */
323 diff_l = diff.sec * 10 + diff.usec / 100000;
324
325 if (max_age != 0xffff && max_age < diff_l)
326 return report;
327
328 if (wpabuf_resize(&report, 2 + wpabuf_len(wpa_s->lci)))
329 return report;
330
331 wpabuf_put_u8(report, WLAN_EID_MEASURE_REPORT);
332 wpabuf_put_u8(report, wpabuf_len(wpa_s->lci));
333 /* We'll override user's measurement token */
334 ptoken = wpabuf_put(report, 0);
335 wpabuf_put_buf(report, wpa_s->lci);
336 *ptoken = token;
337
338 return report;
339}
340
341
0c73e410
AS
342static void wpas_rrm_send_msr_report_mpdu(struct wpa_supplicant *wpa_s,
343 const u8 *data, size_t len)
344{
345 struct wpabuf *report = wpabuf_alloc(len + 3);
346
347 if (!report)
348 return;
349
350 wpabuf_put_u8(report, WLAN_ACTION_RADIO_MEASUREMENT);
351 wpabuf_put_u8(report, WLAN_RRM_RADIO_MEASUREMENT_REPORT);
352 wpabuf_put_u8(report, wpa_s->rrm.token);
353
354 wpabuf_put_data(report, data, len);
355
356 if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
357 wpa_s->own_addr, wpa_s->bssid,
358 wpabuf_head(report), wpabuf_len(report), 0)) {
359 wpa_printf(MSG_ERROR,
360 "RRM: Radio measurement report failed: Sending Action frame failed");
361 }
362
363 wpabuf_free(report);
364}
365
366
367static void wpas_rrm_send_msr_report(struct wpa_supplicant *wpa_s,
368 struct wpabuf *buf)
369{
370 int len = wpabuf_len(buf);
371 const u8 *pos = wpabuf_head_u8(buf), *next = pos;
372
373#define MPDU_REPORT_LEN (int) (IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN - 3)
374
375 while (len) {
376 int send_len = (len > MPDU_REPORT_LEN) ? next - pos : len;
377
378 if (send_len == len ||
379 (send_len + next[1] + 2) > MPDU_REPORT_LEN) {
380 wpas_rrm_send_msr_report_mpdu(wpa_s, pos, send_len);
381 len -= send_len;
382 pos = next;
383 }
384
385 next += next[1] + 2;
386 }
387#undef MPDU_REPORT_LEN
388}
389
390
9664ab8b
AS
391static int
392wpas_rrm_handle_msr_req_element(
393 struct wpa_supplicant *wpa_s,
394 const struct rrm_measurement_request_element *req,
395 struct wpabuf **buf)
ec493469 396{
9664ab8b
AS
397 wpa_printf(MSG_DEBUG, "Measurement request type %d token %d",
398 req->type, req->token);
399
400 switch (req->type) {
401 case MEASURE_TYPE_LCI:
402 *buf = wpas_rrm_build_lci_report(wpa_s, &req->token, req->len,
403 *buf);
404 break;
405 default:
ec493469 406 wpa_printf(MSG_INFO,
9664ab8b
AS
407 "RRM: Unsupported radio measurement type %u",
408 req->type);
409 break;
ec493469
AS
410 }
411
9664ab8b
AS
412 return 0;
413}
ec493469 414
ec493469 415
9664ab8b
AS
416static struct wpabuf *
417wpas_rrm_process_msr_req_elems(struct wpa_supplicant *wpa_s, const u8 *pos,
418 size_t len)
419{
420 struct wpabuf *buf = NULL;
ec493469 421
9664ab8b 422 while (len) {
332bf5d3 423 const struct rrm_measurement_request_element *req;
9664ab8b 424 int res;
ec493469 425
9664ab8b 426 if (len < 2) {
332bf5d3
AS
427 wpa_printf(MSG_DEBUG, "RRM: Truncated element");
428 goto out;
429 }
430
9664ab8b 431 req = (const struct rrm_measurement_request_element *) pos;
332bf5d3
AS
432 if (req->eid != WLAN_EID_MEASURE_REQUEST) {
433 wpa_printf(MSG_DEBUG,
434 "RRM: Expected Measurement Request element, but EID is %u",
435 req->eid);
436 goto out;
437 }
438
439 if (req->len < 3) {
440 wpa_printf(MSG_DEBUG, "RRM: Element length too short");
441 goto out;
442 }
332bf5d3 443
9664ab8b 444 if (req->len > len - 2) {
332bf5d3
AS
445 wpa_printf(MSG_DEBUG, "RRM: Element length too long");
446 goto out;
447 }
ec493469 448
9664ab8b
AS
449 res = wpas_rrm_handle_msr_req_element(wpa_s, req, &buf);
450 if (res < 0)
451 goto out;
ec493469 452
9664ab8b
AS
453 pos += req->len + 2;
454 len -= req->len + 2;
ec493469
AS
455 }
456
9664ab8b
AS
457 return buf;
458
459out:
460 wpabuf_free(buf);
461 return NULL;
462}
463
464
465void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s,
466 const u8 *src,
467 const u8 *frame, size_t len)
468{
0c73e410 469 struct wpabuf *report;
9664ab8b
AS
470
471 if (wpa_s->wpa_state != WPA_COMPLETED) {
472 wpa_printf(MSG_INFO,
473 "RRM: Ignoring radio measurement request: Not associated");
474 return;
475 }
476
477 if (!wpa_s->rrm.rrm_used) {
478 wpa_printf(MSG_INFO,
479 "RRM: Ignoring radio measurement request: Not RRM network");
480 return;
481 }
482
483 if (len < 3) {
484 wpa_printf(MSG_INFO,
485 "RRM: Ignoring too short radio measurement request");
486 return;
487 }
488
0c73e410 489 wpa_s->rrm.token = *frame;
9664ab8b
AS
490
491 /* Number of repetitions is not supported */
492
493 report = wpas_rrm_process_msr_req_elems(wpa_s, frame + 3, len - 3);
ec493469
AS
494 if (!report)
495 return;
496
0c73e410 497 wpas_rrm_send_msr_report(wpa_s, report);
332bf5d3 498 wpabuf_free(report);
ec493469
AS
499}
500
501
502void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
503 const u8 *src,
504 const u8 *frame, size_t len,
505 int rssi)
506{
507 struct wpabuf *buf;
508 const struct rrm_link_measurement_request *req;
509 struct rrm_link_measurement_report report;
510
511 if (wpa_s->wpa_state != WPA_COMPLETED) {
512 wpa_printf(MSG_INFO,
513 "RRM: Ignoring link measurement request. Not associated");
514 return;
515 }
516
517 if (!wpa_s->rrm.rrm_used) {
518 wpa_printf(MSG_INFO,
519 "RRM: Ignoring link measurement request. Not RRM network");
520 return;
521 }
522
523 if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) {
524 wpa_printf(MSG_INFO,
525 "RRM: Measurement report failed. TX power insertion not supported");
526 return;
527 }
528
529 req = (const struct rrm_link_measurement_request *) frame;
530 if (len < sizeof(*req)) {
531 wpa_printf(MSG_INFO,
532 "RRM: Link measurement report failed. Request too short");
533 return;
534 }
535
536 os_memset(&report, 0, sizeof(report));
537 report.tpc.eid = WLAN_EID_TPC_REPORT;
538 report.tpc.len = 2;
539 report.rsni = 255; /* 255 indicates that RSNI is not available */
540 report.dialog_token = req->dialog_token;
541
542 /*
543 * It's possible to estimate RCPI based on RSSI in dBm. This
544 * calculation will not reflect the correct value for high rates,
545 * but it's good enough for Action frames which are transmitted
546 * with up to 24 Mbps rates.
547 */
548 if (!rssi)
549 report.rcpi = 255; /* not available */
550 else if (rssi < -110)
551 report.rcpi = 0;
552 else if (rssi > 0)
553 report.rcpi = 220;
554 else
555 report.rcpi = (rssi + 110) * 2;
556
557 /* action_category + action_code */
558 buf = wpabuf_alloc(2 + sizeof(report));
559 if (buf == NULL) {
560 wpa_printf(MSG_ERROR,
561 "RRM: Link measurement report failed. Buffer allocation failed");
562 return;
563 }
564
565 wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
566 wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REPORT);
567 wpabuf_put_data(buf, &report, sizeof(report));
568 wpa_hexdump(MSG_DEBUG, "RRM: Link measurement report:",
569 wpabuf_head(buf), wpabuf_len(buf));
570
571 if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src,
572 wpa_s->own_addr, wpa_s->bssid,
573 wpabuf_head(buf), wpabuf_len(buf), 0)) {
574 wpa_printf(MSG_ERROR,
575 "RRM: Link measurement report failed. Send action failed");
576 }
577 wpabuf_free(buf);
578}