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