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;