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