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