]>
Commit | Line | Data |
---|---|---|
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 | ||
19 | static 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 | */ | |
41 | void 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 | */ | |
59 | void 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 | */ | |
129 | int 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 | ||
282 | static 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 |
342 | static 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 | ||
367 | static 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 |
391 | static int |
392 | wpas_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 |
416 | static struct wpabuf * |
417 | wpas_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 | ||
459 | out: | |
460 | wpabuf_free(buf); | |
461 | return NULL; | |
462 | } | |
463 | ||
464 | ||
465 | void 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 | ||
502 | void 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 | } |