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