2 * hostapd / Radio Measurement (RRM)
3 * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
4 * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
6 * This software may be distributed under the terms of the BSD license.
7 * See README for more details.
10 #include "utils/includes.h"
12 #include "utils/common.h"
14 #include "ap_drv_ops.h"
17 #include "neighbor_db.h"
20 #define HOSTAPD_RRM_REQUEST_TIMEOUT 5
23 static void hostapd_lci_rep_timeout_handler(void *eloop_data, void *user_ctx)
25 struct hostapd_data *hapd = eloop_data;
27 wpa_printf(MSG_DEBUG, "RRM: LCI request (token %u) timed out",
29 hapd->lci_req_active = 0;
33 static void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token,
34 const u8 *pos, size_t len)
36 if (!hapd->lci_req_active || hapd->lci_req_token != token) {
37 wpa_printf(MSG_DEBUG, "Unexpected LCI report, token %u", token);
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);
47 static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd,
48 const u8 *buf, size_t len)
50 const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
51 const u8 *pos, *ie, *end;
55 token = mgmt->u.action.u.rrm.dialog_token;
56 pos = mgmt->u.action.u.rrm.variable;
58 while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) {
60 wpa_printf(MSG_DEBUG, "Bad Measurement Report element");
64 wpa_printf(MSG_DEBUG, "Measurement report type %u", ie[4]);
67 case MEASURE_TYPE_LCI:
68 hostapd_handle_lci_report(hapd, token, ie + 2, ie[1]);
72 "Measurement report type %u is not supported",
82 static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len)
86 /* Range Request element + Location Subject + Maximum Age subelement */
90 /* Subelements are arranged as IEs */
91 subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE);
92 if (subelem && subelem[1] == 2)
93 return *(u16 *) (subelem + 2);
99 static int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age)
101 struct os_time curr, diff;
102 unsigned long diff_l;
107 if (max_age == 0xffff)
110 if (os_get_time(&curr))
113 os_time_sub(&curr, &nr->lci_date, &diff);
116 if (diff.sec > 0xffff)
119 /* LCI age is calculated in 10th of a second units. */
120 diff_l = diff.sec * 10 + diff.usec / 100000;
122 return max_age > diff_l;
126 static size_t hostapd_neighbor_report_len(struct wpabuf *buf,
127 struct hostapd_neighbor_entry *nr,
128 int send_lci, int send_civic)
130 size_t len = 2 + wpabuf_len(nr->nr);
132 if (send_lci && nr->lci)
133 len += 2 + wpabuf_len(nr->lci);
135 if (send_civic && nr->civic)
136 len += 2 + wpabuf_len(nr->civic);
142 static void hostapd_send_nei_report_resp(struct hostapd_data *hapd,
143 const u8 *addr, u8 dialog_token,
144 struct wpa_ssid_value *ssid, u8 lci,
145 u8 civic, u16 lci_max_age)
147 struct hostapd_neighbor_entry *nr;
152 * The number and length of the Neighbor Report elements in a Neighbor
153 * Report frame is limited by the maximum allowed MMPDU size; + 3 bytes
156 buf = wpabuf_alloc(3 + IEEE80211_MAX_MMPDU_SIZE);
160 wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
161 wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_RESPONSE);
162 wpabuf_put_u8(buf, dialog_token);
164 dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
169 if (ssid->ssid_len != nr->ssid.ssid_len ||
170 os_memcmp(ssid->ssid, nr->ssid.ssid, ssid->ssid_len) != 0)
173 send_lci = (lci != 0) && hostapd_check_lci_age(nr, lci_max_age);
174 len = hostapd_neighbor_report_len(buf, nr, send_lci, civic);
176 if (len - 2 > 0xff) {
177 wpa_printf(MSG_DEBUG,
178 "NR entry for " MACSTR " exceeds 0xFF bytes",
183 if (len > wpabuf_tailroom(buf))
186 wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
187 wpabuf_put_u8(buf, len - 2);
188 wpabuf_put_buf(buf, nr->nr);
190 if (send_lci && nr->lci) {
191 wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
192 wpabuf_put_u8(buf, wpabuf_len(nr->lci));
194 * Override measurement token - the first byte of the
195 * Measurement Report element.
197 msmt_token = wpabuf_put(buf, 0);
198 wpabuf_put_buf(buf, nr->lci);
202 if (civic && nr->civic) {
203 wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
204 wpabuf_put_u8(buf, wpabuf_len(nr->civic));
206 * Override measurement token - the first byte of the
207 * Measurement Report element.
209 msmt_token = wpabuf_put(buf, 0);
210 wpabuf_put_buf(buf, nr->civic);
215 hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
216 wpabuf_head(buf), wpabuf_len(buf));
221 static void hostapd_handle_nei_report_req(struct hostapd_data *hapd,
222 const u8 *buf, size_t len)
224 const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
225 const u8 *pos, *ie, *end;
226 struct wpa_ssid_value ssid = {
230 u8 lci = 0, civic = 0; /* Measurement tokens */
233 if (!(hapd->conf->radio_measurements[0] &
234 WLAN_RRM_CAPS_NEIGHBOR_REPORT))
239 token = mgmt->u.action.u.rrm.dialog_token;
240 pos = mgmt->u.action.u.rrm.variable;
243 ie = get_ie(pos, len, WLAN_EID_SSID);
244 if (ie && ie[1] && ie[1] <= SSID_MAX_LEN) {
245 ssid.ssid_len = ie[1];
246 os_memcpy(ssid.ssid, ie + 2, ssid.ssid_len);
248 ssid.ssid_len = hapd->conf->ssid.ssid_len;
249 os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
252 while ((ie = get_ie(pos, len, WLAN_EID_MEASURE_REQUEST))) {
256 wpa_printf(MSG_DEBUG,
257 "Neighbor report request, measure type %u",
260 switch (ie[4]) { /* Measurement Type */
261 case MEASURE_TYPE_LCI:
262 lci = ie[2]; /* Measurement Token */
263 lci_max_age = hostapd_parse_location_lci_req_age(ie + 2,
266 case MEASURE_TYPE_LOCATION_CIVIC:
267 civic = ie[2]; /* Measurement token */
271 pos = ie + ie[1] + 2;
275 hostapd_send_nei_report_resp(hapd, mgmt->sa, token, &ssid, lci, civic,
280 void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
281 const u8 *buf, size_t len)
283 const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
286 * Check for enough bytes: header + (1B)Category + (1B)Action +
289 if (len < IEEE80211_HDRLEN + 3)
292 wpa_printf(MSG_DEBUG, "Radio measurement frame, action %u from " MACSTR,
293 mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa));
295 switch (mgmt->u.action.u.rrm.action) {
296 case WLAN_RRM_RADIO_MEASUREMENT_REPORT:
297 hostapd_handle_radio_msmt_report(hapd, buf, len);
299 case WLAN_RRM_NEIGHBOR_REPORT_REQUEST:
300 hostapd_handle_nei_report_req(hapd, buf, len);
303 wpa_printf(MSG_DEBUG, "RRM action %u is not supported",
304 mgmt->u.action.u.rrm.action);
310 int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr)
313 struct sta_info *sta = ap_get_sta(hapd, addr);
318 "Request LCI: Destination address is not in station list");
322 if (!(sta->flags & WLAN_STA_AUTHORIZED)) {
324 "Request LCI: Destination address is not connected");
328 if (!(sta->rrm_enabled_capa[1] & WLAN_RRM_CAPS_LCI_MEASUREMENT)) {
330 "Request LCI: Station does not support LCI in RRM");
334 if (hapd->lci_req_active) {
335 wpa_printf(MSG_DEBUG,
336 "Request LCI: LCI request is already in process, overriding");
337 hapd->lci_req_active = 0;
338 eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd,
342 /* Measurement request (5) + Measurement element with LCI (10) */
343 buf = wpabuf_alloc(5 + 10);
347 hapd->lci_req_token++;
348 /* For wraparounds - the token must be nonzero */
349 if (!hapd->lci_req_token)
350 hapd->lci_req_token++;
352 wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
353 wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
354 wpabuf_put_u8(buf, hapd->lci_req_token);
355 wpabuf_put_le16(buf, 0); /* Number of repetitions */
357 wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
358 wpabuf_put_u8(buf, 3 + 1 + 4);
360 wpabuf_put_u8(buf, 1); /* Measurement Token */
362 * Parallel and Enable bits are 0, Duration, Request, and Report are
365 wpabuf_put_u8(buf, 0);
366 wpabuf_put_u8(buf, MEASURE_TYPE_LCI);
368 wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
370 wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE);
371 wpabuf_put_u8(buf, 2);
372 wpabuf_put_le16(buf, 0xffff);
374 ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
375 wpabuf_head(buf), wpabuf_len(buf));
380 hapd->lci_req_active = 1;
382 eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
383 hostapd_lci_rep_timeout_handler, hapd, NULL);
389 void hostapd_clean_rrm(struct hostapd_data *hapd)
391 hostpad_free_neighbor_db(hapd);
392 eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
393 hapd->lci_req_active = 0;