xref: /freebsd/contrib/wpa/src/ap/rrm.c (revision 5f4c09dd85bff675e0ca63c55ea3c517e0fddfcc)
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  * Copyright (c) 2016-2017, Jouni Malinen <j@w1.fi>
6  *
7  * This software may be distributed under the terms of the BSD license.
8  * See README for more details.
9  */
10 
11 #include "utils/includes.h"
12 
13 #include "utils/common.h"
14 #include "common/wpa_ctrl.h"
15 #include "hostapd.h"
16 #include "ap_drv_ops.h"
17 #include "sta_info.h"
18 #include "eloop.h"
19 #include "neighbor_db.h"
20 #include "rrm.h"
21 
22 #define HOSTAPD_RRM_REQUEST_TIMEOUT 5
23 
24 
25 static void hostapd_lci_rep_timeout_handler(void *eloop_data, void *user_ctx)
26 {
27 	struct hostapd_data *hapd = eloop_data;
28 
29 	wpa_printf(MSG_DEBUG, "RRM: LCI request (token %u) timed out",
30 		   hapd->lci_req_token);
31 	hapd->lci_req_active = 0;
32 }
33 
34 
35 static void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token,
36 				      const u8 *pos, size_t len)
37 {
38 	if (!hapd->lci_req_active || hapd->lci_req_token != token) {
39 		wpa_printf(MSG_DEBUG, "Unexpected LCI report, token %u", token);
40 		return;
41 	}
42 
43 	hapd->lci_req_active = 0;
44 	eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
45 	wpa_printf(MSG_DEBUG, "LCI report token %u len %zu", token, len);
46 }
47 
48 
49 static void hostapd_range_rep_timeout_handler(void *eloop_data, void *user_ctx)
50 {
51 	struct hostapd_data *hapd = eloop_data;
52 
53 	wpa_printf(MSG_DEBUG, "RRM: Range request (token %u) timed out",
54 		   hapd->range_req_token);
55 	hapd->range_req_active = 0;
56 }
57 
58 
59 static void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token,
60 					const u8 *pos, size_t len)
61 {
62 	if (!hapd->range_req_active || hapd->range_req_token != token) {
63 		wpa_printf(MSG_DEBUG, "Unexpected range report, token %u",
64 			   token);
65 		return;
66 	}
67 
68 	hapd->range_req_active = 0;
69 	eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
70 	wpa_printf(MSG_DEBUG, "Range report token %u len %zu", token, len);
71 }
72 
73 
74 static void hostapd_handle_beacon_report(struct hostapd_data *hapd,
75 					 const u8 *addr, u8 token, u8 rep_mode,
76 					 const u8 *pos, size_t len)
77 {
78 	char report[2 * 255 + 1];
79 
80 	wpa_printf(MSG_DEBUG, "Beacon report token %u len %zu from " MACSTR,
81 		   token, len, MAC2STR(addr));
82 	/* Skip to the beginning of the Beacon report */
83 	if (len < 3)
84 		return;
85 	pos += 3;
86 	len -= 3;
87 	report[0] = '\0';
88 	if (wpa_snprintf_hex(report, sizeof(report), pos, len) < 0)
89 		return;
90 	wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s",
91 		MAC2STR(addr), token, rep_mode, report);
92 }
93 
94 
95 static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd,
96 					     const u8 *buf, size_t len)
97 {
98 	const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
99 	const u8 *pos, *ie, *end;
100 	u8 token, rep_mode;
101 
102 	end = buf + len;
103 	token = mgmt->u.action.u.rrm.dialog_token;
104 	pos = mgmt->u.action.u.rrm.variable;
105 
106 	while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) {
107 		if (ie[1] < 3) {
108 			wpa_printf(MSG_DEBUG, "Bad Measurement Report element");
109 			break;
110 		}
111 
112 		rep_mode = ie[3];
113 		wpa_printf(MSG_DEBUG, "Measurement report mode 0x%x type %u",
114 			   rep_mode, ie[4]);
115 
116 		switch (ie[4]) {
117 		case MEASURE_TYPE_LCI:
118 			hostapd_handle_lci_report(hapd, token, ie + 2, ie[1]);
119 			break;
120 		case MEASURE_TYPE_FTM_RANGE:
121 			hostapd_handle_range_report(hapd, token, ie + 2, ie[1]);
122 			break;
123 		case MEASURE_TYPE_BEACON:
124 			hostapd_handle_beacon_report(hapd, mgmt->sa, token,
125 						     rep_mode, ie + 2, ie[1]);
126 			break;
127 		default:
128 			wpa_printf(MSG_DEBUG,
129 				   "Measurement report type %u is not supported",
130 				   ie[4]);
131 			break;
132 		}
133 
134 		pos = ie + ie[1] + 2;
135 	}
136 }
137 
138 
139 static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len)
140 {
141 	const u8 *subelem;
142 
143 	/* Range Request element + Location Subject + Maximum Age subelement */
144 	if (len < 3 + 1 + 4)
145 		return 0;
146 
147 	/* Subelements are arranged as IEs */
148 	subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE);
149 	if (subelem && subelem[1] == 2)
150 		return WPA_GET_LE16(subelem + 2);
151 
152 	return 0;
153 }
154 
155 
156 static int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age)
157 {
158 	struct os_time curr, diff;
159 	unsigned long diff_l;
160 
161 	if (nr->stationary || max_age == 0xffff)
162 		return 1;
163 
164 	if (!max_age)
165 		return 0;
166 
167 	if (os_get_time(&curr))
168 		return 0;
169 
170 	os_time_sub(&curr, &nr->lci_date, &diff);
171 
172 	/* avoid overflow */
173 	if (diff.sec > 0xffff)
174 		return 0;
175 
176 	/* LCI age is calculated in 10th of a second units. */
177 	diff_l = diff.sec * 10 + diff.usec / 100000;
178 
179 	return max_age > diff_l;
180 }
181 
182 
183 static size_t hostapd_neighbor_report_len(struct wpabuf *buf,
184 					  struct hostapd_neighbor_entry *nr,
185 					  int send_lci, int send_civic)
186 {
187 	size_t len = 2 + wpabuf_len(nr->nr);
188 
189 	if (send_lci && nr->lci)
190 		len += 2 + wpabuf_len(nr->lci);
191 
192 	if (send_civic && nr->civic)
193 		len += 2 + wpabuf_len(nr->civic);
194 
195 	return len;
196 }
197 
198 
199 static void hostapd_send_nei_report_resp(struct hostapd_data *hapd,
200 					 const u8 *addr, u8 dialog_token,
201 					 struct wpa_ssid_value *ssid, u8 lci,
202 					 u8 civic, u16 lci_max_age)
203 {
204 	struct hostapd_neighbor_entry *nr;
205 	struct wpabuf *buf;
206 	u8 *msmt_token;
207 
208 	/*
209 	 * The number and length of the Neighbor Report elements in a Neighbor
210 	 * Report frame is limited by the maximum allowed MMPDU size; + 3 bytes
211 	 * of RRM header.
212 	 */
213 	buf = wpabuf_alloc(3 + IEEE80211_MAX_MMPDU_SIZE);
214 	if (!buf)
215 		return;
216 
217 	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
218 	wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_RESPONSE);
219 	wpabuf_put_u8(buf, dialog_token);
220 
221 	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
222 			 list) {
223 		int send_lci;
224 		size_t len;
225 
226 		if (ssid->ssid_len != nr->ssid.ssid_len ||
227 		    os_memcmp(ssid->ssid, nr->ssid.ssid, ssid->ssid_len) != 0)
228 			continue;
229 
230 		send_lci = (lci != 0) && hostapd_check_lci_age(nr, lci_max_age);
231 		len = hostapd_neighbor_report_len(buf, nr, send_lci, civic);
232 
233 		if (len - 2 > 0xff) {
234 			wpa_printf(MSG_DEBUG,
235 				   "NR entry for " MACSTR " exceeds 0xFF bytes",
236 				   MAC2STR(nr->bssid));
237 			continue;
238 		}
239 
240 		if (len > wpabuf_tailroom(buf))
241 			break;
242 
243 		wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
244 		wpabuf_put_u8(buf, len - 2);
245 		wpabuf_put_buf(buf, nr->nr);
246 
247 		if (send_lci && nr->lci) {
248 			wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
249 			wpabuf_put_u8(buf, wpabuf_len(nr->lci));
250 			/*
251 			 * Override measurement token - the first byte of the
252 			 * Measurement Report element.
253 			 */
254 			msmt_token = wpabuf_put(buf, 0);
255 			wpabuf_put_buf(buf, nr->lci);
256 			*msmt_token = lci;
257 		}
258 
259 		if (civic && nr->civic) {
260 			wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
261 			wpabuf_put_u8(buf, wpabuf_len(nr->civic));
262 			/*
263 			 * Override measurement token - the first byte of the
264 			 * Measurement Report element.
265 			 */
266 			msmt_token = wpabuf_put(buf, 0);
267 			wpabuf_put_buf(buf, nr->civic);
268 			*msmt_token = civic;
269 		}
270 	}
271 
272 	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
273 				wpabuf_head(buf), wpabuf_len(buf));
274 	wpabuf_free(buf);
275 }
276 
277 
278 static void hostapd_handle_nei_report_req(struct hostapd_data *hapd,
279 					  const u8 *buf, size_t len)
280 {
281 	const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
282 	const u8 *pos, *ie, *end;
283 	struct wpa_ssid_value ssid = {
284 		.ssid_len = 0
285 	};
286 	u8 token;
287 	u8 lci = 0, civic = 0; /* Measurement tokens */
288 	u16 lci_max_age = 0;
289 
290 	if (!(hapd->conf->radio_measurements[0] &
291 	      WLAN_RRM_CAPS_NEIGHBOR_REPORT))
292 		return;
293 
294 	end = buf + len;
295 
296 	token = mgmt->u.action.u.rrm.dialog_token;
297 	pos = mgmt->u.action.u.rrm.variable;
298 	len = end - pos;
299 
300 	ie = get_ie(pos, len, WLAN_EID_SSID);
301 	if (ie && ie[1] && ie[1] <= SSID_MAX_LEN) {
302 		ssid.ssid_len = ie[1];
303 		os_memcpy(ssid.ssid, ie + 2, ssid.ssid_len);
304 	} else {
305 		ssid.ssid_len = hapd->conf->ssid.ssid_len;
306 		os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
307 	}
308 
309 	while ((ie = get_ie(pos, len, WLAN_EID_MEASURE_REQUEST))) {
310 		if (ie[1] < 3)
311 			break;
312 
313 		wpa_printf(MSG_DEBUG,
314 			   "Neighbor report request, measure type %u",
315 			   ie[4]);
316 
317 		switch (ie[4]) { /* Measurement Type */
318 		case MEASURE_TYPE_LCI:
319 			lci = ie[2]; /* Measurement Token */
320 			lci_max_age = hostapd_parse_location_lci_req_age(ie + 2,
321 									 ie[1]);
322 			break;
323 		case MEASURE_TYPE_LOCATION_CIVIC:
324 			civic = ie[2]; /* Measurement token */
325 			break;
326 		}
327 
328 		pos = ie + ie[1] + 2;
329 		len = end - pos;
330 	}
331 
332 	hostapd_send_nei_report_resp(hapd, mgmt->sa, token, &ssid, lci, civic,
333 				     lci_max_age);
334 }
335 
336 
337 void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
338 				      const u8 *buf, size_t len)
339 {
340 	const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
341 
342 	/*
343 	 * Check for enough bytes: header + (1B)Category + (1B)Action +
344 	 * (1B)Dialog Token.
345 	 */
346 	if (len < IEEE80211_HDRLEN + 3)
347 		return;
348 
349 	wpa_printf(MSG_DEBUG, "Radio measurement frame, action %u from " MACSTR,
350 		   mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa));
351 
352 	switch (mgmt->u.action.u.rrm.action) {
353 	case WLAN_RRM_RADIO_MEASUREMENT_REPORT:
354 		hostapd_handle_radio_msmt_report(hapd, buf, len);
355 		break;
356 	case WLAN_RRM_NEIGHBOR_REPORT_REQUEST:
357 		hostapd_handle_nei_report_req(hapd, buf, len);
358 		break;
359 	default:
360 		wpa_printf(MSG_DEBUG, "RRM action %u is not supported",
361 			   mgmt->u.action.u.rrm.action);
362 		break;
363 	}
364 }
365 
366 
367 int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr)
368 {
369 	struct wpabuf *buf;
370 	struct sta_info *sta = ap_get_sta(hapd, addr);
371 	int ret;
372 
373 	if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
374 		wpa_printf(MSG_INFO,
375 			   "Request LCI: Destination address is not connected");
376 		return -1;
377 	}
378 
379 	if (!(sta->rrm_enabled_capa[1] & WLAN_RRM_CAPS_LCI_MEASUREMENT)) {
380 		wpa_printf(MSG_INFO,
381 			   "Request LCI: Station does not support LCI in RRM");
382 		return -1;
383 	}
384 
385 	if (hapd->lci_req_active) {
386 		wpa_printf(MSG_DEBUG,
387 			   "Request LCI: LCI request is already in process, overriding");
388 		hapd->lci_req_active = 0;
389 		eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd,
390 				     NULL);
391 	}
392 
393 	/* Measurement request (5) + Measurement element with LCI (10) */
394 	buf = wpabuf_alloc(5 + 10);
395 	if (!buf)
396 		return -1;
397 
398 	hapd->lci_req_token++;
399 	/* For wraparounds - the token must be nonzero */
400 	if (!hapd->lci_req_token)
401 		hapd->lci_req_token++;
402 
403 	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
404 	wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
405 	wpabuf_put_u8(buf, hapd->lci_req_token);
406 	wpabuf_put_le16(buf, 0); /* Number of repetitions */
407 
408 	wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
409 	wpabuf_put_u8(buf, 3 + 1 + 4);
410 
411 	wpabuf_put_u8(buf, 1); /* Measurement Token */
412 	/*
413 	 * Parallel and Enable bits are 0, Duration, Request, and Report are
414 	 * reserved.
415 	 */
416 	wpabuf_put_u8(buf, 0);
417 	wpabuf_put_u8(buf, MEASURE_TYPE_LCI);
418 
419 	wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
420 
421 	wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE);
422 	wpabuf_put_u8(buf, 2);
423 	wpabuf_put_le16(buf, 0xffff);
424 
425 	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
426 				      wpabuf_head(buf), wpabuf_len(buf));
427 	wpabuf_free(buf);
428 	if (ret)
429 		return ret;
430 
431 	hapd->lci_req_active = 1;
432 
433 	eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
434 			       hostapd_lci_rep_timeout_handler, hapd, NULL);
435 
436 	return 0;
437 }
438 
439 
440 int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
441 			   u16 random_interval, u8 min_ap,
442 			   const u8 *responders, unsigned int n_responders)
443 {
444 	struct wpabuf *buf;
445 	struct sta_info *sta;
446 	u8 *len;
447 	unsigned int i;
448 	int ret;
449 
450 	wpa_printf(MSG_DEBUG, "Request range: dest addr " MACSTR
451 		   " rand interval %u min AP %u n_responders %u", MAC2STR(addr),
452 		   random_interval, min_ap, n_responders);
453 
454 	if (min_ap == 0 || min_ap > n_responders) {
455 		wpa_printf(MSG_INFO, "Request range: Wrong min AP count");
456 		return -1;
457 	}
458 
459 	sta = ap_get_sta(hapd, addr);
460 	if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
461 		wpa_printf(MSG_INFO,
462 			   "Request range: Destination address is not connected");
463 		return -1;
464 	}
465 
466 	if (!(sta->rrm_enabled_capa[4] & WLAN_RRM_CAPS_FTM_RANGE_REPORT)) {
467 		wpa_printf(MSG_ERROR,
468 			   "Request range: Destination station does not support FTM range report in RRM");
469 		return -1;
470 	}
471 
472 	if (hapd->range_req_active) {
473 		wpa_printf(MSG_DEBUG,
474 			   "Request range: Range request is already in process; overriding");
475 		hapd->range_req_active = 0;
476 		eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd,
477 				     NULL);
478 	}
479 
480 	/* Action + measurement type + token + reps + EID + len = 7 */
481 	buf = wpabuf_alloc(7 + 255);
482 	if (!buf)
483 		return -1;
484 
485 	hapd->range_req_token++;
486 	if (!hapd->range_req_token) /* For wraparounds */
487 		hapd->range_req_token++;
488 
489 	/* IEEE P802.11-REVmc/D5.0, 9.6.7.2 */
490 	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
491 	wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
492 	wpabuf_put_u8(buf, hapd->range_req_token); /* Dialog Token */
493 	wpabuf_put_le16(buf, 0); /* Number of Repetitions */
494 
495 	/* IEEE P802.11-REVmc/D5.0, 9.4.2.21 */
496 	wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
497 	len = wpabuf_put(buf, 1); /* Length will be set later */
498 
499 	wpabuf_put_u8(buf, 1); /* Measurement Token */
500 	/*
501 	 * Parallel and Enable bits are 0; Duration, Request, and Report are
502 	 * reserved.
503 	 */
504 	wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
505 	wpabuf_put_u8(buf, MEASURE_TYPE_FTM_RANGE); /* Measurement Type */
506 
507 	/* IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 */
508 	wpabuf_put_le16(buf, random_interval); /* Randomization Interval */
509 	wpabuf_put_u8(buf, min_ap); /* Minimum AP Count */
510 
511 	/* FTM Range Subelements */
512 
513 	/*
514 	 * Taking the neighbor report part of the range request from neighbor
515 	 * database instead of requesting the separate bits of data from the
516 	 * user.
517 	 */
518 	for (i = 0; i < n_responders; i++) {
519 		struct hostapd_neighbor_entry *nr;
520 
521 		nr = hostapd_neighbor_get(hapd, responders + ETH_ALEN * i,
522 					  NULL);
523 		if (!nr) {
524 			wpa_printf(MSG_INFO, "Missing neighbor report for "
525 				   MACSTR, MAC2STR(responders + ETH_ALEN * i));
526 			wpabuf_free(buf);
527 			return -1;
528 		}
529 
530 		if (wpabuf_tailroom(buf) < 2 + wpabuf_len(nr->nr)) {
531 			wpa_printf(MSG_ERROR, "Too long range request");
532 			wpabuf_free(buf);
533 			return -1;
534 		}
535 
536 		wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
537 		wpabuf_put_u8(buf, wpabuf_len(nr->nr));
538 		wpabuf_put_buf(buf, nr->nr);
539 	}
540 
541 	/* Action + measurement type + token + reps + EID + len = 7 */
542 	*len = wpabuf_len(buf) - 7;
543 
544 	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
545 				      wpabuf_head(buf), wpabuf_len(buf));
546 	wpabuf_free(buf);
547 	if (ret)
548 		return ret;
549 
550 	hapd->range_req_active = 1;
551 
552 	eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
553 			       hostapd_range_rep_timeout_handler, hapd, NULL);
554 
555 	return 0;
556 }
557 
558 
559 void hostapd_clean_rrm(struct hostapd_data *hapd)
560 {
561 	hostapd_free_neighbor_db(hapd);
562 	eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
563 	hapd->lci_req_active = 0;
564 	eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
565 	hapd->range_req_active = 0;
566 }
567 
568 
569 int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr,
570 			    u8 req_mode, const struct wpabuf *req)
571 {
572 	struct wpabuf *buf;
573 	struct sta_info *sta = ap_get_sta(hapd, addr);
574 	int ret;
575 	enum beacon_report_mode mode;
576 	const u8 *pos;
577 
578 	/* Request data:
579 	 * Operating Class (1), Channel Number (1), Randomization Interval (2),
580 	 * Measurement Duration (2), Measurement Mode (1), BSSID (6),
581 	 * Optional Subelements (variable)
582 	 */
583 	if (wpabuf_len(req) < 13) {
584 		wpa_printf(MSG_INFO, "Beacon request: Too short request data");
585 		return -1;
586 	}
587 	pos = wpabuf_head(req);
588 	mode = pos[6];
589 
590 	if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
591 		wpa_printf(MSG_INFO,
592 			   "Beacon request: " MACSTR " is not connected",
593 			   MAC2STR(addr));
594 		return -1;
595 	}
596 
597 	switch (mode) {
598 	case BEACON_REPORT_MODE_PASSIVE:
599 		if (!(sta->rrm_enabled_capa[0] &
600 		      WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE)) {
601 			wpa_printf(MSG_INFO,
602 				   "Beacon request: " MACSTR
603 				   " does not support passive beacon report",
604 				   MAC2STR(addr));
605 			return -1;
606 		}
607 		break;
608 	case BEACON_REPORT_MODE_ACTIVE:
609 		if (!(sta->rrm_enabled_capa[0] &
610 		      WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE)) {
611 			wpa_printf(MSG_INFO,
612 				   "Beacon request: " MACSTR
613 				   " does not support active beacon report",
614 				   MAC2STR(addr));
615 			return -1;
616 		}
617 		break;
618 	case BEACON_REPORT_MODE_TABLE:
619 		if (!(sta->rrm_enabled_capa[0] &
620 		      WLAN_RRM_CAPS_BEACON_REPORT_TABLE)) {
621 			wpa_printf(MSG_INFO,
622 				   "Beacon request: " MACSTR
623 				   " does not support table beacon report",
624 				   MAC2STR(addr));
625 			return -1;
626 		}
627 		break;
628 	default:
629 		wpa_printf(MSG_INFO,
630 			   "Beacon request: Unknown measurement mode %d", mode);
631 		return -1;
632 	}
633 
634 	buf = wpabuf_alloc(5 + 2 + 3 + wpabuf_len(req));
635 	if (!buf)
636 		return -1;
637 
638 	hapd->beacon_req_token++;
639 	if (!hapd->beacon_req_token)
640 		hapd->beacon_req_token++;
641 
642 	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
643 	wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
644 	wpabuf_put_u8(buf, hapd->beacon_req_token);
645 	wpabuf_put_le16(buf, 0); /* Number of repetitions */
646 
647 	/* Measurement Request element */
648 	wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
649 	wpabuf_put_u8(buf, 3 + wpabuf_len(req));
650 	wpabuf_put_u8(buf, 1); /* Measurement Token */
651 	wpabuf_put_u8(buf, req_mode); /* Measurement Request Mode */
652 	wpabuf_put_u8(buf, MEASURE_TYPE_BEACON); /* Measurement Type */
653 	wpabuf_put_buf(buf, req);
654 
655 	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
656 				      wpabuf_head(buf), wpabuf_len(buf));
657 	wpabuf_free(buf);
658 	if (ret < 0)
659 		return ret;
660 
661 	return hapd->beacon_req_token;
662 }
663 
664 
665 void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
666 				      const struct ieee80211_mgmt *mgmt,
667 				      size_t len, int ok)
668 {
669 	if (len < 24 + 3)
670 		return;
671 	wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_REQ_TX_STATUS MACSTR
672 		" %u ack=%d", MAC2STR(mgmt->da),
673 		mgmt->u.action.u.rrm.dialog_token, ok);
674 }
675