xref: /freebsd/contrib/wpa/src/ap/rrm.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
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