1780fb4a2SCy Schubert /*
2780fb4a2SCy Schubert * hostapd / Radio Measurement (RRM)
3780fb4a2SCy Schubert * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
4780fb4a2SCy Schubert * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
585732ac8SCy Schubert * Copyright (c) 2016-2017, Jouni Malinen <j@w1.fi>
6780fb4a2SCy Schubert *
7780fb4a2SCy Schubert * This software may be distributed under the terms of the BSD license.
8780fb4a2SCy Schubert * See README for more details.
9780fb4a2SCy Schubert */
10780fb4a2SCy Schubert
11780fb4a2SCy Schubert #include "utils/includes.h"
12780fb4a2SCy Schubert
13780fb4a2SCy Schubert #include "utils/common.h"
1485732ac8SCy Schubert #include "common/wpa_ctrl.h"
15780fb4a2SCy Schubert #include "hostapd.h"
16780fb4a2SCy Schubert #include "ap_drv_ops.h"
17780fb4a2SCy Schubert #include "sta_info.h"
18780fb4a2SCy Schubert #include "eloop.h"
19780fb4a2SCy Schubert #include "neighbor_db.h"
20780fb4a2SCy Schubert #include "rrm.h"
21780fb4a2SCy Schubert
22780fb4a2SCy Schubert #define HOSTAPD_RRM_REQUEST_TIMEOUT 5
23780fb4a2SCy Schubert
24780fb4a2SCy Schubert
hostapd_lci_rep_timeout_handler(void * eloop_data,void * user_ctx)25780fb4a2SCy Schubert static void hostapd_lci_rep_timeout_handler(void *eloop_data, void *user_ctx)
26780fb4a2SCy Schubert {
27780fb4a2SCy Schubert struct hostapd_data *hapd = eloop_data;
28780fb4a2SCy Schubert
29780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "RRM: LCI request (token %u) timed out",
30780fb4a2SCy Schubert hapd->lci_req_token);
31780fb4a2SCy Schubert hapd->lci_req_active = 0;
32780fb4a2SCy Schubert }
33780fb4a2SCy Schubert
34780fb4a2SCy Schubert
hostapd_handle_lci_report(struct hostapd_data * hapd,u8 token,const u8 * pos,size_t len)35780fb4a2SCy Schubert static void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token,
36780fb4a2SCy Schubert const u8 *pos, size_t len)
37780fb4a2SCy Schubert {
38780fb4a2SCy Schubert if (!hapd->lci_req_active || hapd->lci_req_token != token) {
39780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "Unexpected LCI report, token %u", token);
40780fb4a2SCy Schubert return;
41780fb4a2SCy Schubert }
42780fb4a2SCy Schubert
43780fb4a2SCy Schubert hapd->lci_req_active = 0;
44780fb4a2SCy Schubert eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
45780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "LCI report token %u len %zu", token, len);
46780fb4a2SCy Schubert }
47780fb4a2SCy Schubert
48780fb4a2SCy Schubert
hostapd_range_rep_timeout_handler(void * eloop_data,void * user_ctx)49780fb4a2SCy Schubert static void hostapd_range_rep_timeout_handler(void *eloop_data, void *user_ctx)
50780fb4a2SCy Schubert {
51780fb4a2SCy Schubert struct hostapd_data *hapd = eloop_data;
52780fb4a2SCy Schubert
53780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "RRM: Range request (token %u) timed out",
54780fb4a2SCy Schubert hapd->range_req_token);
55780fb4a2SCy Schubert hapd->range_req_active = 0;
56780fb4a2SCy Schubert }
57780fb4a2SCy Schubert
58780fb4a2SCy Schubert
hostapd_handle_range_report(struct hostapd_data * hapd,u8 token,const u8 * pos,size_t len)59780fb4a2SCy Schubert static void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token,
60780fb4a2SCy Schubert const u8 *pos, size_t len)
61780fb4a2SCy Schubert {
62780fb4a2SCy Schubert if (!hapd->range_req_active || hapd->range_req_token != token) {
63780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "Unexpected range report, token %u",
64780fb4a2SCy Schubert token);
65780fb4a2SCy Schubert return;
66780fb4a2SCy Schubert }
67780fb4a2SCy Schubert
68780fb4a2SCy Schubert hapd->range_req_active = 0;
69780fb4a2SCy Schubert eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
70780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "Range report token %u len %zu", token, len);
71780fb4a2SCy Schubert }
72780fb4a2SCy Schubert
73780fb4a2SCy Schubert
hostapd_handle_beacon_report(struct hostapd_data * hapd,const u8 * addr,u8 token,u8 rep_mode,const u8 * pos,size_t len)7485732ac8SCy Schubert static void hostapd_handle_beacon_report(struct hostapd_data *hapd,
7585732ac8SCy Schubert const u8 *addr, u8 token, u8 rep_mode,
7685732ac8SCy Schubert const u8 *pos, size_t len)
7785732ac8SCy Schubert {
7885732ac8SCy Schubert char report[2 * 255 + 1];
7985732ac8SCy Schubert
8085732ac8SCy Schubert wpa_printf(MSG_DEBUG, "Beacon report token %u len %zu from " MACSTR,
8185732ac8SCy Schubert token, len, MAC2STR(addr));
8285732ac8SCy Schubert /* Skip to the beginning of the Beacon report */
8385732ac8SCy Schubert if (len < 3)
8485732ac8SCy Schubert return;
8585732ac8SCy Schubert pos += 3;
8685732ac8SCy Schubert len -= 3;
8785732ac8SCy Schubert report[0] = '\0';
8885732ac8SCy Schubert if (wpa_snprintf_hex(report, sizeof(report), pos, len) < 0)
8985732ac8SCy Schubert return;
9085732ac8SCy Schubert wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s",
9185732ac8SCy Schubert MAC2STR(addr), token, rep_mode, report);
9285732ac8SCy Schubert }
9385732ac8SCy Schubert
9485732ac8SCy Schubert
hostapd_handle_radio_msmt_report(struct hostapd_data * hapd,const u8 * buf,size_t len)95780fb4a2SCy Schubert static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd,
96780fb4a2SCy Schubert const u8 *buf, size_t len)
97780fb4a2SCy Schubert {
98780fb4a2SCy Schubert const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
99780fb4a2SCy Schubert const u8 *pos, *ie, *end;
10085732ac8SCy Schubert u8 token, rep_mode;
101780fb4a2SCy Schubert
102780fb4a2SCy Schubert end = buf + len;
103780fb4a2SCy Schubert token = mgmt->u.action.u.rrm.dialog_token;
104780fb4a2SCy Schubert pos = mgmt->u.action.u.rrm.variable;
105780fb4a2SCy Schubert
106780fb4a2SCy Schubert while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) {
10785732ac8SCy Schubert if (ie[1] < 3) {
108780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "Bad Measurement Report element");
109780fb4a2SCy Schubert break;
110780fb4a2SCy Schubert }
111780fb4a2SCy Schubert
11285732ac8SCy Schubert rep_mode = ie[3];
11385732ac8SCy Schubert wpa_printf(MSG_DEBUG, "Measurement report mode 0x%x type %u",
11485732ac8SCy Schubert rep_mode, ie[4]);
115780fb4a2SCy Schubert
116780fb4a2SCy Schubert switch (ie[4]) {
117780fb4a2SCy Schubert case MEASURE_TYPE_LCI:
118780fb4a2SCy Schubert hostapd_handle_lci_report(hapd, token, ie + 2, ie[1]);
119780fb4a2SCy Schubert break;
120780fb4a2SCy Schubert case MEASURE_TYPE_FTM_RANGE:
121780fb4a2SCy Schubert hostapd_handle_range_report(hapd, token, ie + 2, ie[1]);
122780fb4a2SCy Schubert break;
12385732ac8SCy Schubert case MEASURE_TYPE_BEACON:
12485732ac8SCy Schubert hostapd_handle_beacon_report(hapd, mgmt->sa, token,
12585732ac8SCy Schubert rep_mode, ie + 2, ie[1]);
12685732ac8SCy Schubert break;
127780fb4a2SCy Schubert default:
128780fb4a2SCy Schubert wpa_printf(MSG_DEBUG,
129780fb4a2SCy Schubert "Measurement report type %u is not supported",
130780fb4a2SCy Schubert ie[4]);
131780fb4a2SCy Schubert break;
132780fb4a2SCy Schubert }
133780fb4a2SCy Schubert
134780fb4a2SCy Schubert pos = ie + ie[1] + 2;
135780fb4a2SCy Schubert }
136780fb4a2SCy Schubert }
137780fb4a2SCy Schubert
138780fb4a2SCy Schubert
hostapd_parse_location_lci_req_age(const u8 * buf,size_t len)139780fb4a2SCy Schubert static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len)
140780fb4a2SCy Schubert {
141780fb4a2SCy Schubert const u8 *subelem;
142780fb4a2SCy Schubert
143780fb4a2SCy Schubert /* Range Request element + Location Subject + Maximum Age subelement */
144780fb4a2SCy Schubert if (len < 3 + 1 + 4)
145780fb4a2SCy Schubert return 0;
146780fb4a2SCy Schubert
147780fb4a2SCy Schubert /* Subelements are arranged as IEs */
148780fb4a2SCy Schubert subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE);
149780fb4a2SCy Schubert if (subelem && subelem[1] == 2)
15085732ac8SCy Schubert return WPA_GET_LE16(subelem + 2);
151780fb4a2SCy Schubert
152780fb4a2SCy Schubert return 0;
153780fb4a2SCy Schubert }
154780fb4a2SCy Schubert
155780fb4a2SCy Schubert
hostapd_check_lci_age(struct hostapd_neighbor_entry * nr,u16 max_age)156780fb4a2SCy Schubert static int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age)
157780fb4a2SCy Schubert {
158780fb4a2SCy Schubert struct os_time curr, diff;
159780fb4a2SCy Schubert unsigned long diff_l;
160780fb4a2SCy Schubert
16185732ac8SCy Schubert if (nr->stationary || max_age == 0xffff)
16285732ac8SCy Schubert return 1;
16385732ac8SCy Schubert
164780fb4a2SCy Schubert if (!max_age)
165780fb4a2SCy Schubert return 0;
166780fb4a2SCy Schubert
167780fb4a2SCy Schubert if (os_get_time(&curr))
168780fb4a2SCy Schubert return 0;
169780fb4a2SCy Schubert
170780fb4a2SCy Schubert os_time_sub(&curr, &nr->lci_date, &diff);
171780fb4a2SCy Schubert
172780fb4a2SCy Schubert /* avoid overflow */
173780fb4a2SCy Schubert if (diff.sec > 0xffff)
174780fb4a2SCy Schubert return 0;
175780fb4a2SCy Schubert
176780fb4a2SCy Schubert /* LCI age is calculated in 10th of a second units. */
177780fb4a2SCy Schubert diff_l = diff.sec * 10 + diff.usec / 100000;
178780fb4a2SCy Schubert
179780fb4a2SCy Schubert return max_age > diff_l;
180780fb4a2SCy Schubert }
181780fb4a2SCy Schubert
182780fb4a2SCy Schubert
hostapd_neighbor_report_len(struct wpabuf * buf,struct hostapd_neighbor_entry * nr,int send_lci,int send_civic)183780fb4a2SCy Schubert static size_t hostapd_neighbor_report_len(struct wpabuf *buf,
184780fb4a2SCy Schubert struct hostapd_neighbor_entry *nr,
185780fb4a2SCy Schubert int send_lci, int send_civic)
186780fb4a2SCy Schubert {
187780fb4a2SCy Schubert size_t len = 2 + wpabuf_len(nr->nr);
188780fb4a2SCy Schubert
189780fb4a2SCy Schubert if (send_lci && nr->lci)
190780fb4a2SCy Schubert len += 2 + wpabuf_len(nr->lci);
191780fb4a2SCy Schubert
192780fb4a2SCy Schubert if (send_civic && nr->civic)
193780fb4a2SCy Schubert len += 2 + wpabuf_len(nr->civic);
194780fb4a2SCy Schubert
195780fb4a2SCy Schubert return len;
196780fb4a2SCy Schubert }
197780fb4a2SCy Schubert
198780fb4a2SCy Schubert
hostapd_send_nei_report_resp(struct hostapd_data * hapd,const u8 * addr,u8 dialog_token,struct wpa_ssid_value * ssid,u8 lci,u8 civic,u16 lci_max_age)199780fb4a2SCy Schubert static void hostapd_send_nei_report_resp(struct hostapd_data *hapd,
200780fb4a2SCy Schubert const u8 *addr, u8 dialog_token,
201780fb4a2SCy Schubert struct wpa_ssid_value *ssid, u8 lci,
202780fb4a2SCy Schubert u8 civic, u16 lci_max_age)
203780fb4a2SCy Schubert {
204780fb4a2SCy Schubert struct hostapd_neighbor_entry *nr;
205780fb4a2SCy Schubert struct wpabuf *buf;
206780fb4a2SCy Schubert u8 *msmt_token;
207780fb4a2SCy Schubert
208780fb4a2SCy Schubert /*
209780fb4a2SCy Schubert * The number and length of the Neighbor Report elements in a Neighbor
210780fb4a2SCy Schubert * Report frame is limited by the maximum allowed MMPDU size; + 3 bytes
211780fb4a2SCy Schubert * of RRM header.
212780fb4a2SCy Schubert */
213780fb4a2SCy Schubert buf = wpabuf_alloc(3 + IEEE80211_MAX_MMPDU_SIZE);
214780fb4a2SCy Schubert if (!buf)
215780fb4a2SCy Schubert return;
216780fb4a2SCy Schubert
217780fb4a2SCy Schubert wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
218780fb4a2SCy Schubert wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_RESPONSE);
219780fb4a2SCy Schubert wpabuf_put_u8(buf, dialog_token);
220780fb4a2SCy Schubert
221780fb4a2SCy Schubert dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
222780fb4a2SCy Schubert list) {
223780fb4a2SCy Schubert int send_lci;
224780fb4a2SCy Schubert size_t len;
225780fb4a2SCy Schubert
226780fb4a2SCy Schubert if (ssid->ssid_len != nr->ssid.ssid_len ||
227780fb4a2SCy Schubert os_memcmp(ssid->ssid, nr->ssid.ssid, ssid->ssid_len) != 0)
228780fb4a2SCy Schubert continue;
229780fb4a2SCy Schubert
230780fb4a2SCy Schubert send_lci = (lci != 0) && hostapd_check_lci_age(nr, lci_max_age);
231780fb4a2SCy Schubert len = hostapd_neighbor_report_len(buf, nr, send_lci, civic);
232780fb4a2SCy Schubert
233780fb4a2SCy Schubert if (len - 2 > 0xff) {
234780fb4a2SCy Schubert wpa_printf(MSG_DEBUG,
235780fb4a2SCy Schubert "NR entry for " MACSTR " exceeds 0xFF bytes",
236780fb4a2SCy Schubert MAC2STR(nr->bssid));
237780fb4a2SCy Schubert continue;
238780fb4a2SCy Schubert }
239780fb4a2SCy Schubert
240780fb4a2SCy Schubert if (len > wpabuf_tailroom(buf))
241780fb4a2SCy Schubert break;
242780fb4a2SCy Schubert
243780fb4a2SCy Schubert wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
244780fb4a2SCy Schubert wpabuf_put_u8(buf, len - 2);
245780fb4a2SCy Schubert wpabuf_put_buf(buf, nr->nr);
246780fb4a2SCy Schubert
247780fb4a2SCy Schubert if (send_lci && nr->lci) {
248780fb4a2SCy Schubert wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
249780fb4a2SCy Schubert wpabuf_put_u8(buf, wpabuf_len(nr->lci));
250780fb4a2SCy Schubert /*
251780fb4a2SCy Schubert * Override measurement token - the first byte of the
252780fb4a2SCy Schubert * Measurement Report element.
253780fb4a2SCy Schubert */
254780fb4a2SCy Schubert msmt_token = wpabuf_put(buf, 0);
255780fb4a2SCy Schubert wpabuf_put_buf(buf, nr->lci);
256780fb4a2SCy Schubert *msmt_token = lci;
257780fb4a2SCy Schubert }
258780fb4a2SCy Schubert
259780fb4a2SCy Schubert if (civic && nr->civic) {
260780fb4a2SCy Schubert wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
261780fb4a2SCy Schubert wpabuf_put_u8(buf, wpabuf_len(nr->civic));
262780fb4a2SCy Schubert /*
263780fb4a2SCy Schubert * Override measurement token - the first byte of the
264780fb4a2SCy Schubert * Measurement Report element.
265780fb4a2SCy Schubert */
266780fb4a2SCy Schubert msmt_token = wpabuf_put(buf, 0);
267780fb4a2SCy Schubert wpabuf_put_buf(buf, nr->civic);
268780fb4a2SCy Schubert *msmt_token = civic;
269780fb4a2SCy Schubert }
270780fb4a2SCy Schubert }
271780fb4a2SCy Schubert
272780fb4a2SCy Schubert hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
273780fb4a2SCy Schubert wpabuf_head(buf), wpabuf_len(buf));
274780fb4a2SCy Schubert wpabuf_free(buf);
275780fb4a2SCy Schubert }
276780fb4a2SCy Schubert
277780fb4a2SCy Schubert
hostapd_handle_nei_report_req(struct hostapd_data * hapd,const u8 * buf,size_t len)278780fb4a2SCy Schubert static void hostapd_handle_nei_report_req(struct hostapd_data *hapd,
279780fb4a2SCy Schubert const u8 *buf, size_t len)
280780fb4a2SCy Schubert {
281780fb4a2SCy Schubert const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
282780fb4a2SCy Schubert const u8 *pos, *ie, *end;
283780fb4a2SCy Schubert struct wpa_ssid_value ssid = {
284780fb4a2SCy Schubert .ssid_len = 0
285780fb4a2SCy Schubert };
286780fb4a2SCy Schubert u8 token;
287780fb4a2SCy Schubert u8 lci = 0, civic = 0; /* Measurement tokens */
288780fb4a2SCy Schubert u16 lci_max_age = 0;
289780fb4a2SCy Schubert
290780fb4a2SCy Schubert if (!(hapd->conf->radio_measurements[0] &
291780fb4a2SCy Schubert WLAN_RRM_CAPS_NEIGHBOR_REPORT))
292780fb4a2SCy Schubert return;
293780fb4a2SCy Schubert
294780fb4a2SCy Schubert end = buf + len;
295780fb4a2SCy Schubert
296780fb4a2SCy Schubert token = mgmt->u.action.u.rrm.dialog_token;
297780fb4a2SCy Schubert pos = mgmt->u.action.u.rrm.variable;
298780fb4a2SCy Schubert len = end - pos;
299780fb4a2SCy Schubert
300780fb4a2SCy Schubert ie = get_ie(pos, len, WLAN_EID_SSID);
301780fb4a2SCy Schubert if (ie && ie[1] && ie[1] <= SSID_MAX_LEN) {
302780fb4a2SCy Schubert ssid.ssid_len = ie[1];
303780fb4a2SCy Schubert os_memcpy(ssid.ssid, ie + 2, ssid.ssid_len);
304780fb4a2SCy Schubert } else {
305780fb4a2SCy Schubert ssid.ssid_len = hapd->conf->ssid.ssid_len;
306780fb4a2SCy Schubert os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
307780fb4a2SCy Schubert }
308780fb4a2SCy Schubert
309780fb4a2SCy Schubert while ((ie = get_ie(pos, len, WLAN_EID_MEASURE_REQUEST))) {
310780fb4a2SCy Schubert if (ie[1] < 3)
311780fb4a2SCy Schubert break;
312780fb4a2SCy Schubert
313780fb4a2SCy Schubert wpa_printf(MSG_DEBUG,
314780fb4a2SCy Schubert "Neighbor report request, measure type %u",
315780fb4a2SCy Schubert ie[4]);
316780fb4a2SCy Schubert
317780fb4a2SCy Schubert switch (ie[4]) { /* Measurement Type */
318780fb4a2SCy Schubert case MEASURE_TYPE_LCI:
319780fb4a2SCy Schubert lci = ie[2]; /* Measurement Token */
320780fb4a2SCy Schubert lci_max_age = hostapd_parse_location_lci_req_age(ie + 2,
321780fb4a2SCy Schubert ie[1]);
322780fb4a2SCy Schubert break;
323780fb4a2SCy Schubert case MEASURE_TYPE_LOCATION_CIVIC:
324780fb4a2SCy Schubert civic = ie[2]; /* Measurement token */
325780fb4a2SCy Schubert break;
326780fb4a2SCy Schubert }
327780fb4a2SCy Schubert
328780fb4a2SCy Schubert pos = ie + ie[1] + 2;
329780fb4a2SCy Schubert len = end - pos;
330780fb4a2SCy Schubert }
331780fb4a2SCy Schubert
332780fb4a2SCy Schubert hostapd_send_nei_report_resp(hapd, mgmt->sa, token, &ssid, lci, civic,
333780fb4a2SCy Schubert lci_max_age);
334780fb4a2SCy Schubert }
335780fb4a2SCy Schubert
336780fb4a2SCy Schubert
hostapd_link_mesr_rep_timeout_handler(void * eloop_data,void * user_ctx)337*a90b9d01SCy Schubert static void hostapd_link_mesr_rep_timeout_handler(void *eloop_data,
338*a90b9d01SCy Schubert void *user_ctx)
339*a90b9d01SCy Schubert {
340*a90b9d01SCy Schubert struct hostapd_data *hapd = eloop_data;
341*a90b9d01SCy Schubert
342*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
343*a90b9d01SCy Schubert "RRM: Link measurement request (token %u) timed out",
344*a90b9d01SCy Schubert hapd->link_measurement_req_token);
345*a90b9d01SCy Schubert hapd->link_mesr_req_active = 0;
346*a90b9d01SCy Schubert }
347*a90b9d01SCy Schubert
348*a90b9d01SCy Schubert
hostapd_handle_link_mesr_report(struct hostapd_data * hapd,const u8 * buf,size_t len)349*a90b9d01SCy Schubert static void hostapd_handle_link_mesr_report(struct hostapd_data *hapd,
350*a90b9d01SCy Schubert const u8 *buf, size_t len)
351*a90b9d01SCy Schubert {
352*a90b9d01SCy Schubert const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
353*a90b9d01SCy Schubert const struct rrm_link_measurement_report *report;
354*a90b9d01SCy Schubert const u8 *pos, *end;
355*a90b9d01SCy Schubert char report_msg[2 * 8 + 1];
356*a90b9d01SCy Schubert
357*a90b9d01SCy Schubert end = buf + len;
358*a90b9d01SCy Schubert pos = mgmt->u.action.u.rrm.variable;
359*a90b9d01SCy Schubert report = (const struct rrm_link_measurement_report *) (pos - 1);
360*a90b9d01SCy Schubert if (end - (const u8 *) report < (int) sizeof(*report))
361*a90b9d01SCy Schubert return;
362*a90b9d01SCy Schubert
363*a90b9d01SCy Schubert if (!hapd->link_mesr_req_active ||
364*a90b9d01SCy Schubert (hapd->link_measurement_req_token != report->dialog_token)) {
365*a90b9d01SCy Schubert wpa_printf(MSG_INFO,
366*a90b9d01SCy Schubert "Unexpected Link measurement report, token %u",
367*a90b9d01SCy Schubert report->dialog_token);
368*a90b9d01SCy Schubert return;
369*a90b9d01SCy Schubert }
370*a90b9d01SCy Schubert
371*a90b9d01SCy Schubert hapd->link_mesr_req_active = 0;
372*a90b9d01SCy Schubert eloop_cancel_timeout(hostapd_link_mesr_rep_timeout_handler, hapd, NULL);
373*a90b9d01SCy Schubert
374*a90b9d01SCy Schubert report_msg[0] = '\0';
375*a90b9d01SCy Schubert if (wpa_snprintf_hex(report_msg, sizeof(report_msg),
376*a90b9d01SCy Schubert pos, end - pos) < 0)
377*a90b9d01SCy Schubert return;
378*a90b9d01SCy Schubert
379*a90b9d01SCy Schubert wpa_msg(hapd->msg_ctx, MSG_INFO, LINK_MSR_RESP_RX MACSTR " %u %s",
380*a90b9d01SCy Schubert MAC2STR(mgmt->sa), report->dialog_token, report_msg);
381*a90b9d01SCy Schubert }
382*a90b9d01SCy Schubert
383*a90b9d01SCy Schubert
hostapd_handle_radio_measurement(struct hostapd_data * hapd,const u8 * buf,size_t len)384780fb4a2SCy Schubert void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
385780fb4a2SCy Schubert const u8 *buf, size_t len)
386780fb4a2SCy Schubert {
387780fb4a2SCy Schubert const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
388780fb4a2SCy Schubert
389780fb4a2SCy Schubert /*
390780fb4a2SCy Schubert * Check for enough bytes: header + (1B)Category + (1B)Action +
391780fb4a2SCy Schubert * (1B)Dialog Token.
392780fb4a2SCy Schubert */
393780fb4a2SCy Schubert if (len < IEEE80211_HDRLEN + 3)
394780fb4a2SCy Schubert return;
395780fb4a2SCy Schubert
396780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "Radio measurement frame, action %u from " MACSTR,
397780fb4a2SCy Schubert mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa));
398780fb4a2SCy Schubert
399780fb4a2SCy Schubert switch (mgmt->u.action.u.rrm.action) {
400780fb4a2SCy Schubert case WLAN_RRM_RADIO_MEASUREMENT_REPORT:
401780fb4a2SCy Schubert hostapd_handle_radio_msmt_report(hapd, buf, len);
402780fb4a2SCy Schubert break;
403780fb4a2SCy Schubert case WLAN_RRM_NEIGHBOR_REPORT_REQUEST:
404780fb4a2SCy Schubert hostapd_handle_nei_report_req(hapd, buf, len);
405780fb4a2SCy Schubert break;
406*a90b9d01SCy Schubert case WLAN_RRM_LINK_MEASUREMENT_REPORT:
407*a90b9d01SCy Schubert hostapd_handle_link_mesr_report(hapd, buf, len);
408*a90b9d01SCy Schubert break;
409780fb4a2SCy Schubert default:
410780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "RRM action %u is not supported",
411780fb4a2SCy Schubert mgmt->u.action.u.rrm.action);
412780fb4a2SCy Schubert break;
413780fb4a2SCy Schubert }
414780fb4a2SCy Schubert }
415780fb4a2SCy Schubert
416780fb4a2SCy Schubert
hostapd_send_lci_req(struct hostapd_data * hapd,const u8 * addr)417780fb4a2SCy Schubert int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr)
418780fb4a2SCy Schubert {
419780fb4a2SCy Schubert struct wpabuf *buf;
420780fb4a2SCy Schubert struct sta_info *sta = ap_get_sta(hapd, addr);
421780fb4a2SCy Schubert int ret;
422780fb4a2SCy Schubert
42385732ac8SCy Schubert if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
424780fb4a2SCy Schubert wpa_printf(MSG_INFO,
425780fb4a2SCy Schubert "Request LCI: Destination address is not connected");
426780fb4a2SCy Schubert return -1;
427780fb4a2SCy Schubert }
428780fb4a2SCy Schubert
429780fb4a2SCy Schubert if (!(sta->rrm_enabled_capa[1] & WLAN_RRM_CAPS_LCI_MEASUREMENT)) {
430780fb4a2SCy Schubert wpa_printf(MSG_INFO,
431780fb4a2SCy Schubert "Request LCI: Station does not support LCI in RRM");
432780fb4a2SCy Schubert return -1;
433780fb4a2SCy Schubert }
434780fb4a2SCy Schubert
435780fb4a2SCy Schubert if (hapd->lci_req_active) {
436780fb4a2SCy Schubert wpa_printf(MSG_DEBUG,
437780fb4a2SCy Schubert "Request LCI: LCI request is already in process, overriding");
438780fb4a2SCy Schubert hapd->lci_req_active = 0;
439780fb4a2SCy Schubert eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd,
440780fb4a2SCy Schubert NULL);
441780fb4a2SCy Schubert }
442780fb4a2SCy Schubert
443780fb4a2SCy Schubert /* Measurement request (5) + Measurement element with LCI (10) */
444780fb4a2SCy Schubert buf = wpabuf_alloc(5 + 10);
445780fb4a2SCy Schubert if (!buf)
446780fb4a2SCy Schubert return -1;
447780fb4a2SCy Schubert
448780fb4a2SCy Schubert hapd->lci_req_token++;
449780fb4a2SCy Schubert /* For wraparounds - the token must be nonzero */
450780fb4a2SCy Schubert if (!hapd->lci_req_token)
451780fb4a2SCy Schubert hapd->lci_req_token++;
452780fb4a2SCy Schubert
453780fb4a2SCy Schubert wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
454780fb4a2SCy Schubert wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
455780fb4a2SCy Schubert wpabuf_put_u8(buf, hapd->lci_req_token);
456780fb4a2SCy Schubert wpabuf_put_le16(buf, 0); /* Number of repetitions */
457780fb4a2SCy Schubert
458780fb4a2SCy Schubert wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
459780fb4a2SCy Schubert wpabuf_put_u8(buf, 3 + 1 + 4);
460780fb4a2SCy Schubert
461780fb4a2SCy Schubert wpabuf_put_u8(buf, 1); /* Measurement Token */
462780fb4a2SCy Schubert /*
463780fb4a2SCy Schubert * Parallel and Enable bits are 0, Duration, Request, and Report are
464780fb4a2SCy Schubert * reserved.
465780fb4a2SCy Schubert */
466780fb4a2SCy Schubert wpabuf_put_u8(buf, 0);
467780fb4a2SCy Schubert wpabuf_put_u8(buf, MEASURE_TYPE_LCI);
468780fb4a2SCy Schubert
469780fb4a2SCy Schubert wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
470780fb4a2SCy Schubert
471780fb4a2SCy Schubert wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE);
472780fb4a2SCy Schubert wpabuf_put_u8(buf, 2);
473780fb4a2SCy Schubert wpabuf_put_le16(buf, 0xffff);
474780fb4a2SCy Schubert
475780fb4a2SCy Schubert ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
476780fb4a2SCy Schubert wpabuf_head(buf), wpabuf_len(buf));
477780fb4a2SCy Schubert wpabuf_free(buf);
478780fb4a2SCy Schubert if (ret)
479780fb4a2SCy Schubert return ret;
480780fb4a2SCy Schubert
481780fb4a2SCy Schubert hapd->lci_req_active = 1;
482780fb4a2SCy Schubert
483780fb4a2SCy Schubert eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
484780fb4a2SCy Schubert hostapd_lci_rep_timeout_handler, hapd, NULL);
485780fb4a2SCy Schubert
486780fb4a2SCy Schubert return 0;
487780fb4a2SCy Schubert }
488780fb4a2SCy Schubert
489780fb4a2SCy Schubert
hostapd_send_range_req(struct hostapd_data * hapd,const u8 * addr,u16 random_interval,u8 min_ap,const u8 * responders,unsigned int n_responders)490780fb4a2SCy Schubert int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
491780fb4a2SCy Schubert u16 random_interval, u8 min_ap,
492780fb4a2SCy Schubert const u8 *responders, unsigned int n_responders)
493780fb4a2SCy Schubert {
494780fb4a2SCy Schubert struct wpabuf *buf;
495780fb4a2SCy Schubert struct sta_info *sta;
496780fb4a2SCy Schubert u8 *len;
497780fb4a2SCy Schubert unsigned int i;
498780fb4a2SCy Schubert int ret;
499780fb4a2SCy Schubert
500780fb4a2SCy Schubert wpa_printf(MSG_DEBUG, "Request range: dest addr " MACSTR
501780fb4a2SCy Schubert " rand interval %u min AP %u n_responders %u", MAC2STR(addr),
502780fb4a2SCy Schubert random_interval, min_ap, n_responders);
503780fb4a2SCy Schubert
504780fb4a2SCy Schubert if (min_ap == 0 || min_ap > n_responders) {
505780fb4a2SCy Schubert wpa_printf(MSG_INFO, "Request range: Wrong min AP count");
506780fb4a2SCy Schubert return -1;
507780fb4a2SCy Schubert }
508780fb4a2SCy Schubert
509780fb4a2SCy Schubert sta = ap_get_sta(hapd, addr);
510780fb4a2SCy Schubert if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
511780fb4a2SCy Schubert wpa_printf(MSG_INFO,
512780fb4a2SCy Schubert "Request range: Destination address is not connected");
513780fb4a2SCy Schubert return -1;
514780fb4a2SCy Schubert }
515780fb4a2SCy Schubert
516780fb4a2SCy Schubert if (!(sta->rrm_enabled_capa[4] & WLAN_RRM_CAPS_FTM_RANGE_REPORT)) {
517780fb4a2SCy Schubert wpa_printf(MSG_ERROR,
518780fb4a2SCy Schubert "Request range: Destination station does not support FTM range report in RRM");
519780fb4a2SCy Schubert return -1;
520780fb4a2SCy Schubert }
521780fb4a2SCy Schubert
522780fb4a2SCy Schubert if (hapd->range_req_active) {
523780fb4a2SCy Schubert wpa_printf(MSG_DEBUG,
524780fb4a2SCy Schubert "Request range: Range request is already in process; overriding");
525780fb4a2SCy Schubert hapd->range_req_active = 0;
52685732ac8SCy Schubert eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd,
527780fb4a2SCy Schubert NULL);
528780fb4a2SCy Schubert }
529780fb4a2SCy Schubert
530780fb4a2SCy Schubert /* Action + measurement type + token + reps + EID + len = 7 */
531780fb4a2SCy Schubert buf = wpabuf_alloc(7 + 255);
532780fb4a2SCy Schubert if (!buf)
533780fb4a2SCy Schubert return -1;
534780fb4a2SCy Schubert
535780fb4a2SCy Schubert hapd->range_req_token++;
536780fb4a2SCy Schubert if (!hapd->range_req_token) /* For wraparounds */
537780fb4a2SCy Schubert hapd->range_req_token++;
538780fb4a2SCy Schubert
539780fb4a2SCy Schubert /* IEEE P802.11-REVmc/D5.0, 9.6.7.2 */
540780fb4a2SCy Schubert wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
541780fb4a2SCy Schubert wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
542780fb4a2SCy Schubert wpabuf_put_u8(buf, hapd->range_req_token); /* Dialog Token */
543780fb4a2SCy Schubert wpabuf_put_le16(buf, 0); /* Number of Repetitions */
544780fb4a2SCy Schubert
545780fb4a2SCy Schubert /* IEEE P802.11-REVmc/D5.0, 9.4.2.21 */
546780fb4a2SCy Schubert wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
547780fb4a2SCy Schubert len = wpabuf_put(buf, 1); /* Length will be set later */
548780fb4a2SCy Schubert
549780fb4a2SCy Schubert wpabuf_put_u8(buf, 1); /* Measurement Token */
550780fb4a2SCy Schubert /*
551780fb4a2SCy Schubert * Parallel and Enable bits are 0; Duration, Request, and Report are
552780fb4a2SCy Schubert * reserved.
553780fb4a2SCy Schubert */
554780fb4a2SCy Schubert wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
555780fb4a2SCy Schubert wpabuf_put_u8(buf, MEASURE_TYPE_FTM_RANGE); /* Measurement Type */
556780fb4a2SCy Schubert
557780fb4a2SCy Schubert /* IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 */
558780fb4a2SCy Schubert wpabuf_put_le16(buf, random_interval); /* Randomization Interval */
559780fb4a2SCy Schubert wpabuf_put_u8(buf, min_ap); /* Minimum AP Count */
560780fb4a2SCy Schubert
561780fb4a2SCy Schubert /* FTM Range Subelements */
562780fb4a2SCy Schubert
563780fb4a2SCy Schubert /*
564780fb4a2SCy Schubert * Taking the neighbor report part of the range request from neighbor
565780fb4a2SCy Schubert * database instead of requesting the separate bits of data from the
566780fb4a2SCy Schubert * user.
567780fb4a2SCy Schubert */
568780fb4a2SCy Schubert for (i = 0; i < n_responders; i++) {
569780fb4a2SCy Schubert struct hostapd_neighbor_entry *nr;
570780fb4a2SCy Schubert
571780fb4a2SCy Schubert nr = hostapd_neighbor_get(hapd, responders + ETH_ALEN * i,
572780fb4a2SCy Schubert NULL);
573780fb4a2SCy Schubert if (!nr) {
574780fb4a2SCy Schubert wpa_printf(MSG_INFO, "Missing neighbor report for "
575780fb4a2SCy Schubert MACSTR, MAC2STR(responders + ETH_ALEN * i));
576780fb4a2SCy Schubert wpabuf_free(buf);
577780fb4a2SCy Schubert return -1;
578780fb4a2SCy Schubert }
579780fb4a2SCy Schubert
580780fb4a2SCy Schubert if (wpabuf_tailroom(buf) < 2 + wpabuf_len(nr->nr)) {
581780fb4a2SCy Schubert wpa_printf(MSG_ERROR, "Too long range request");
582780fb4a2SCy Schubert wpabuf_free(buf);
583780fb4a2SCy Schubert return -1;
584780fb4a2SCy Schubert }
585780fb4a2SCy Schubert
586780fb4a2SCy Schubert wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
587780fb4a2SCy Schubert wpabuf_put_u8(buf, wpabuf_len(nr->nr));
588780fb4a2SCy Schubert wpabuf_put_buf(buf, nr->nr);
589780fb4a2SCy Schubert }
590780fb4a2SCy Schubert
591780fb4a2SCy Schubert /* Action + measurement type + token + reps + EID + len = 7 */
592780fb4a2SCy Schubert *len = wpabuf_len(buf) - 7;
593780fb4a2SCy Schubert
594780fb4a2SCy Schubert ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
595780fb4a2SCy Schubert wpabuf_head(buf), wpabuf_len(buf));
596780fb4a2SCy Schubert wpabuf_free(buf);
597780fb4a2SCy Schubert if (ret)
598780fb4a2SCy Schubert return ret;
599780fb4a2SCy Schubert
600780fb4a2SCy Schubert hapd->range_req_active = 1;
601780fb4a2SCy Schubert
602780fb4a2SCy Schubert eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
603780fb4a2SCy Schubert hostapd_range_rep_timeout_handler, hapd, NULL);
604780fb4a2SCy Schubert
605780fb4a2SCy Schubert return 0;
606780fb4a2SCy Schubert }
607780fb4a2SCy Schubert
608780fb4a2SCy Schubert
hostapd_clean_rrm(struct hostapd_data * hapd)609780fb4a2SCy Schubert void hostapd_clean_rrm(struct hostapd_data *hapd)
610780fb4a2SCy Schubert {
6114bc52338SCy Schubert hostapd_free_neighbor_db(hapd);
612780fb4a2SCy Schubert eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
613780fb4a2SCy Schubert hapd->lci_req_active = 0;
614780fb4a2SCy Schubert eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
615780fb4a2SCy Schubert hapd->range_req_active = 0;
616*a90b9d01SCy Schubert eloop_cancel_timeout(hostapd_link_mesr_rep_timeout_handler, hapd, NULL);
617780fb4a2SCy Schubert }
61885732ac8SCy Schubert
61985732ac8SCy Schubert
hostapd_send_beacon_req(struct hostapd_data * hapd,const u8 * addr,u8 req_mode,const struct wpabuf * req)62085732ac8SCy Schubert int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr,
62185732ac8SCy Schubert u8 req_mode, const struct wpabuf *req)
62285732ac8SCy Schubert {
62385732ac8SCy Schubert struct wpabuf *buf;
62485732ac8SCy Schubert struct sta_info *sta = ap_get_sta(hapd, addr);
62585732ac8SCy Schubert int ret;
62685732ac8SCy Schubert enum beacon_report_mode mode;
62785732ac8SCy Schubert const u8 *pos;
62885732ac8SCy Schubert
62985732ac8SCy Schubert /* Request data:
63085732ac8SCy Schubert * Operating Class (1), Channel Number (1), Randomization Interval (2),
63185732ac8SCy Schubert * Measurement Duration (2), Measurement Mode (1), BSSID (6),
63285732ac8SCy Schubert * Optional Subelements (variable)
63385732ac8SCy Schubert */
63485732ac8SCy Schubert if (wpabuf_len(req) < 13) {
63585732ac8SCy Schubert wpa_printf(MSG_INFO, "Beacon request: Too short request data");
63685732ac8SCy Schubert return -1;
63785732ac8SCy Schubert }
63885732ac8SCy Schubert pos = wpabuf_head(req);
63985732ac8SCy Schubert mode = pos[6];
64085732ac8SCy Schubert
64185732ac8SCy Schubert if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
64285732ac8SCy Schubert wpa_printf(MSG_INFO,
64385732ac8SCy Schubert "Beacon request: " MACSTR " is not connected",
64485732ac8SCy Schubert MAC2STR(addr));
64585732ac8SCy Schubert return -1;
64685732ac8SCy Schubert }
64785732ac8SCy Schubert
64885732ac8SCy Schubert switch (mode) {
64985732ac8SCy Schubert case BEACON_REPORT_MODE_PASSIVE:
65085732ac8SCy Schubert if (!(sta->rrm_enabled_capa[0] &
65185732ac8SCy Schubert WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE)) {
65285732ac8SCy Schubert wpa_printf(MSG_INFO,
65385732ac8SCy Schubert "Beacon request: " MACSTR
65485732ac8SCy Schubert " does not support passive beacon report",
65585732ac8SCy Schubert MAC2STR(addr));
65685732ac8SCy Schubert return -1;
65785732ac8SCy Schubert }
65885732ac8SCy Schubert break;
65985732ac8SCy Schubert case BEACON_REPORT_MODE_ACTIVE:
66085732ac8SCy Schubert if (!(sta->rrm_enabled_capa[0] &
66185732ac8SCy Schubert WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE)) {
66285732ac8SCy Schubert wpa_printf(MSG_INFO,
66385732ac8SCy Schubert "Beacon request: " MACSTR
66485732ac8SCy Schubert " does not support active beacon report",
66585732ac8SCy Schubert MAC2STR(addr));
66685732ac8SCy Schubert return -1;
66785732ac8SCy Schubert }
66885732ac8SCy Schubert break;
66985732ac8SCy Schubert case BEACON_REPORT_MODE_TABLE:
67085732ac8SCy Schubert if (!(sta->rrm_enabled_capa[0] &
67185732ac8SCy Schubert WLAN_RRM_CAPS_BEACON_REPORT_TABLE)) {
67285732ac8SCy Schubert wpa_printf(MSG_INFO,
67385732ac8SCy Schubert "Beacon request: " MACSTR
67485732ac8SCy Schubert " does not support table beacon report",
67585732ac8SCy Schubert MAC2STR(addr));
67685732ac8SCy Schubert return -1;
67785732ac8SCy Schubert }
67885732ac8SCy Schubert break;
67985732ac8SCy Schubert default:
68085732ac8SCy Schubert wpa_printf(MSG_INFO,
68185732ac8SCy Schubert "Beacon request: Unknown measurement mode %d", mode);
68285732ac8SCy Schubert return -1;
68385732ac8SCy Schubert }
68485732ac8SCy Schubert
68585732ac8SCy Schubert buf = wpabuf_alloc(5 + 2 + 3 + wpabuf_len(req));
68685732ac8SCy Schubert if (!buf)
68785732ac8SCy Schubert return -1;
68885732ac8SCy Schubert
68985732ac8SCy Schubert hapd->beacon_req_token++;
69085732ac8SCy Schubert if (!hapd->beacon_req_token)
69185732ac8SCy Schubert hapd->beacon_req_token++;
69285732ac8SCy Schubert
69385732ac8SCy Schubert wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
69485732ac8SCy Schubert wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
69585732ac8SCy Schubert wpabuf_put_u8(buf, hapd->beacon_req_token);
69685732ac8SCy Schubert wpabuf_put_le16(buf, 0); /* Number of repetitions */
69785732ac8SCy Schubert
69885732ac8SCy Schubert /* Measurement Request element */
69985732ac8SCy Schubert wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
70085732ac8SCy Schubert wpabuf_put_u8(buf, 3 + wpabuf_len(req));
70185732ac8SCy Schubert wpabuf_put_u8(buf, 1); /* Measurement Token */
70285732ac8SCy Schubert wpabuf_put_u8(buf, req_mode); /* Measurement Request Mode */
70385732ac8SCy Schubert wpabuf_put_u8(buf, MEASURE_TYPE_BEACON); /* Measurement Type */
70485732ac8SCy Schubert wpabuf_put_buf(buf, req);
70585732ac8SCy Schubert
70685732ac8SCy Schubert ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
70785732ac8SCy Schubert wpabuf_head(buf), wpabuf_len(buf));
70885732ac8SCy Schubert wpabuf_free(buf);
70985732ac8SCy Schubert if (ret < 0)
71085732ac8SCy Schubert return ret;
71185732ac8SCy Schubert
71285732ac8SCy Schubert return hapd->beacon_req_token;
71385732ac8SCy Schubert }
71485732ac8SCy Schubert
71585732ac8SCy Schubert
hostapd_rrm_beacon_req_tx_status(struct hostapd_data * hapd,const struct ieee80211_mgmt * mgmt,size_t len,int ok)71685732ac8SCy Schubert void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
71785732ac8SCy Schubert const struct ieee80211_mgmt *mgmt,
71885732ac8SCy Schubert size_t len, int ok)
71985732ac8SCy Schubert {
72085732ac8SCy Schubert if (len < 24 + 3)
72185732ac8SCy Schubert return;
72285732ac8SCy Schubert wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_REQ_TX_STATUS MACSTR
72385732ac8SCy Schubert " %u ack=%d", MAC2STR(mgmt->da),
72485732ac8SCy Schubert mgmt->u.action.u.rrm.dialog_token, ok);
72585732ac8SCy Schubert }
726*a90b9d01SCy Schubert
727*a90b9d01SCy Schubert
hostapd_send_link_measurement_req(struct hostapd_data * hapd,const u8 * addr)728*a90b9d01SCy Schubert int hostapd_send_link_measurement_req(struct hostapd_data *hapd, const u8 *addr)
729*a90b9d01SCy Schubert {
730*a90b9d01SCy Schubert struct wpabuf *buf;
731*a90b9d01SCy Schubert struct sta_info *sta;
732*a90b9d01SCy Schubert int ret;
733*a90b9d01SCy Schubert
734*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG, "Request Link Measurement: dest addr " MACSTR,
735*a90b9d01SCy Schubert MAC2STR(addr));
736*a90b9d01SCy Schubert
737*a90b9d01SCy Schubert if (!(hapd->iface->drv_rrm_flags &
738*a90b9d01SCy Schubert WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) {
739*a90b9d01SCy Schubert wpa_printf(MSG_INFO,
740*a90b9d01SCy Schubert "Request Link Measurement: the driver does not support TX power insertion");
741*a90b9d01SCy Schubert return -1;
742*a90b9d01SCy Schubert }
743*a90b9d01SCy Schubert
744*a90b9d01SCy Schubert sta = ap_get_sta(hapd, addr);
745*a90b9d01SCy Schubert if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
746*a90b9d01SCy Schubert wpa_printf(MSG_INFO,
747*a90b9d01SCy Schubert "Request Link Measurement: specied STA is not connected");
748*a90b9d01SCy Schubert return -1;
749*a90b9d01SCy Schubert }
750*a90b9d01SCy Schubert
751*a90b9d01SCy Schubert if (!(sta->rrm_enabled_capa[0] & WLAN_RRM_CAPS_LINK_MEASUREMENT)) {
752*a90b9d01SCy Schubert wpa_printf(MSG_INFO,
753*a90b9d01SCy Schubert "Request Link Measurement: destination STA does not support link measurement");
754*a90b9d01SCy Schubert return -1;
755*a90b9d01SCy Schubert }
756*a90b9d01SCy Schubert
757*a90b9d01SCy Schubert if (hapd->link_mesr_req_active) {
758*a90b9d01SCy Schubert wpa_printf(MSG_DEBUG,
759*a90b9d01SCy Schubert "Request Link Measurement: request already in process - overriding");
760*a90b9d01SCy Schubert hapd->link_mesr_req_active = 0;
761*a90b9d01SCy Schubert eloop_cancel_timeout(hostapd_link_mesr_rep_timeout_handler,
762*a90b9d01SCy Schubert hapd, NULL);
763*a90b9d01SCy Schubert }
764*a90b9d01SCy Schubert
765*a90b9d01SCy Schubert /* Action + Action type + token + Tx Power used + Max Tx Power = 5 */
766*a90b9d01SCy Schubert buf = wpabuf_alloc(5);
767*a90b9d01SCy Schubert if (!buf)
768*a90b9d01SCy Schubert return -1;
769*a90b9d01SCy Schubert
770*a90b9d01SCy Schubert hapd->link_measurement_req_token++;
771*a90b9d01SCy Schubert if (!hapd->link_measurement_req_token)
772*a90b9d01SCy Schubert hapd->link_measurement_req_token++;
773*a90b9d01SCy Schubert
774*a90b9d01SCy Schubert wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
775*a90b9d01SCy Schubert wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REQUEST);
776*a90b9d01SCy Schubert wpabuf_put_u8(buf, hapd->link_measurement_req_token);
777*a90b9d01SCy Schubert /* NOTE: The driver is expected to fill the Tx Power Used and Max Tx
778*a90b9d01SCy Schubert * Power */
779*a90b9d01SCy Schubert wpabuf_put_u8(buf, 0);
780*a90b9d01SCy Schubert wpabuf_put_u8(buf, 0);
781*a90b9d01SCy Schubert
782*a90b9d01SCy Schubert ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
783*a90b9d01SCy Schubert wpabuf_head(buf), wpabuf_len(buf));
784*a90b9d01SCy Schubert wpabuf_free(buf);
785*a90b9d01SCy Schubert if (ret < 0)
786*a90b9d01SCy Schubert return ret;
787*a90b9d01SCy Schubert
788*a90b9d01SCy Schubert hapd->link_mesr_req_active = 1;
789*a90b9d01SCy Schubert
790*a90b9d01SCy Schubert eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
791*a90b9d01SCy Schubert hostapd_link_mesr_rep_timeout_handler, hapd,
792*a90b9d01SCy Schubert NULL);
793*a90b9d01SCy Schubert
794*a90b9d01SCy Schubert return hapd->link_measurement_req_token;
795*a90b9d01SCy Schubert }
796