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