]> git.ipfire.org Git - thirdparty/hostap.git/blob - wpa_supplicant/wnm_sta.c
OCE: Add RSSI based association rejection support (STA)
[thirdparty/hostap.git] / wpa_supplicant / wnm_sta.c
1 /*
2 * wpa_supplicant - WNM
3 * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9 #include "utils/includes.h"
10
11 #include "utils/common.h"
12 #include "common/ieee802_11_defs.h"
13 #include "common/ieee802_11_common.h"
14 #include "common/wpa_ctrl.h"
15 #include "common/ocv.h"
16 #include "rsn_supp/wpa.h"
17 #include "config.h"
18 #include "wpa_supplicant_i.h"
19 #include "driver_i.h"
20 #include "scan.h"
21 #include "ctrl_iface.h"
22 #include "bss.h"
23 #include "wnm_sta.h"
24 #include "hs20_supplicant.h"
25
26 #define MAX_TFS_IE_LEN 1024
27 #define WNM_MAX_NEIGHBOR_REPORT 10
28
29 #define WNM_SCAN_RESULT_AGE 2 /* 2 seconds */
30
31 /* get the TFS IE from driver */
32 static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf,
33 u16 *buf_len, enum wnm_oper oper)
34 {
35 wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper);
36
37 return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len);
38 }
39
40
41 /* set the TFS IE to driver */
42 static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s,
43 const u8 *addr, const u8 *buf, u16 buf_len,
44 enum wnm_oper oper)
45 {
46 u16 len = buf_len;
47
48 wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper);
49
50 return wpa_drv_wnm_oper(wpa_s, oper, addr, (u8 *) buf, &len);
51 }
52
53
54 /* MLME-SLEEPMODE.request */
55 int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
56 u8 action, u16 intval, struct wpabuf *tfs_req)
57 {
58 struct ieee80211_mgmt *mgmt;
59 int res;
60 size_t len;
61 struct wnm_sleep_element *wnmsleep_ie;
62 u8 *wnmtfs_ie, *oci_ie;
63 u8 wnmsleep_ie_len, oci_ie_len;
64 u16 wnmtfs_ie_len; /* possibly multiple IE(s) */
65 enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD :
66 WNM_SLEEP_TFS_REQ_IE_NONE;
67
68 wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request "
69 "action=%s to " MACSTR,
70 action == 0 ? "enter" : "exit",
71 MAC2STR(wpa_s->bssid));
72
73 /* WNM-Sleep Mode IE */
74 wnmsleep_ie_len = sizeof(struct wnm_sleep_element);
75 wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element));
76 if (wnmsleep_ie == NULL)
77 return -1;
78 wnmsleep_ie->eid = WLAN_EID_WNMSLEEP;
79 wnmsleep_ie->len = wnmsleep_ie_len - 2;
80 wnmsleep_ie->action_type = action;
81 wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT;
82 wnmsleep_ie->intval = host_to_le16(intval);
83 wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element",
84 (u8 *) wnmsleep_ie, wnmsleep_ie_len);
85
86 /* TFS IE(s) */
87 if (tfs_req) {
88 wnmtfs_ie_len = wpabuf_len(tfs_req);
89 wnmtfs_ie = os_memdup(wpabuf_head(tfs_req), wnmtfs_ie_len);
90 if (wnmtfs_ie == NULL) {
91 os_free(wnmsleep_ie);
92 return -1;
93 }
94 } else {
95 wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
96 if (wnmtfs_ie == NULL) {
97 os_free(wnmsleep_ie);
98 return -1;
99 }
100 if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len,
101 tfs_oper)) {
102 wnmtfs_ie_len = 0;
103 os_free(wnmtfs_ie);
104 wnmtfs_ie = NULL;
105 }
106 }
107 wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element",
108 (u8 *) wnmtfs_ie, wnmtfs_ie_len);
109
110 oci_ie = NULL;
111 oci_ie_len = 0;
112 #ifdef CONFIG_OCV
113 if (action == WNM_SLEEP_MODE_EXIT && wpa_sm_ocv_enabled(wpa_s->wpa)) {
114 struct wpa_channel_info ci;
115
116 if (wpa_drv_channel_info(wpa_s, &ci) != 0) {
117 wpa_printf(MSG_WARNING,
118 "Failed to get channel info for OCI element in WNM-Sleep Mode frame");
119 os_free(wnmsleep_ie);
120 os_free(wnmtfs_ie);
121 return -1;
122 }
123
124 oci_ie_len = OCV_OCI_EXTENDED_LEN;
125 oci_ie = os_zalloc(oci_ie_len);
126 if (!oci_ie) {
127 wpa_printf(MSG_WARNING,
128 "Failed to allocate buffer for for OCI element in WNM-Sleep Mode frame");
129 os_free(wnmsleep_ie);
130 os_free(wnmtfs_ie);
131 return -1;
132 }
133
134 if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
135 os_free(wnmsleep_ie);
136 os_free(wnmtfs_ie);
137 os_free(oci_ie);
138 return -1;
139 }
140 }
141 #endif /* CONFIG_OCV */
142
143 mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len +
144 oci_ie_len);
145 if (mgmt == NULL) {
146 wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
147 "WNM-Sleep Request action frame");
148 os_free(wnmsleep_ie);
149 os_free(wnmtfs_ie);
150 return -1;
151 }
152
153 os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
154 os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
155 os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
156 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
157 WLAN_FC_STYPE_ACTION);
158 mgmt->u.action.category = WLAN_ACTION_WNM;
159 mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ;
160 mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1;
161 os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie,
162 wnmsleep_ie_len);
163 /* copy TFS IE here */
164 if (wnmtfs_ie_len > 0) {
165 os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable +
166 wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len);
167 }
168
169 #ifdef CONFIG_OCV
170 /* copy OCV OCI here */
171 if (oci_ie_len > 0) {
172 os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable +
173 wnmsleep_ie_len + wnmtfs_ie_len, oci_ie, oci_ie_len);
174 }
175 #endif /* CONFIG_OCV */
176
177 len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len +
178 wnmtfs_ie_len + oci_ie_len;
179
180 res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
181 wpa_s->own_addr, wpa_s->bssid,
182 &mgmt->u.action.category, len, 0);
183 if (res < 0)
184 wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request "
185 "(action=%d, intval=%d)", action, intval);
186 else
187 wpa_s->wnmsleep_used = 1;
188
189 os_free(wnmsleep_ie);
190 os_free(wnmtfs_ie);
191 os_free(oci_ie);
192 os_free(mgmt);
193
194 return res;
195 }
196
197
198 static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s,
199 const u8 *tfsresp_ie_start,
200 const u8 *tfsresp_ie_end)
201 {
202 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM,
203 wpa_s->bssid, NULL, NULL);
204 /* remove GTK/IGTK ?? */
205
206 /* set the TFS Resp IE(s) */
207 if (tfsresp_ie_start && tfsresp_ie_end &&
208 tfsresp_ie_end - tfsresp_ie_start >= 0) {
209 u16 tfsresp_ie_len;
210 tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) -
211 tfsresp_ie_start;
212 wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found");
213 /* pass the TFS Resp IE(s) to driver for processing */
214 if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid,
215 tfsresp_ie_start,
216 tfsresp_ie_len,
217 WNM_SLEEP_TFS_RESP_IE_SET))
218 wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE");
219 }
220 }
221
222
223 static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s,
224 const u8 *frm, u16 key_len_total)
225 {
226 u8 *ptr, *end;
227 u8 gtk_len;
228
229 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM, wpa_s->bssid,
230 NULL, NULL);
231
232 /* Install GTK/IGTK */
233
234 /* point to key data field */
235 ptr = (u8 *) frm + 1 + 2;
236 end = ptr + key_len_total;
237 wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total);
238
239 if (key_len_total && !wpa_sm_pmf_enabled(wpa_s->wpa)) {
240 wpa_msg(wpa_s, MSG_INFO,
241 "WNM: Ignore Key Data in WNM-Sleep Mode Response - PMF not enabled");
242 return;
243 }
244
245 while (end - ptr > 1) {
246 if (2 + ptr[1] > end - ptr) {
247 wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element "
248 "length");
249 if (end > ptr) {
250 wpa_hexdump(MSG_DEBUG, "WNM: Remaining data",
251 ptr, end - ptr);
252 }
253 break;
254 }
255 if (*ptr == WNM_SLEEP_SUBELEM_GTK) {
256 if (ptr[1] < 11 + 5) {
257 wpa_printf(MSG_DEBUG, "WNM: Too short GTK "
258 "subelem");
259 break;
260 }
261 gtk_len = *(ptr + 4);
262 if (ptr[1] < 11 + gtk_len ||
263 gtk_len < 5 || gtk_len > 32) {
264 wpa_printf(MSG_DEBUG, "WNM: Invalid GTK "
265 "subelem");
266 break;
267 }
268 wpa_wnmsleep_install_key(
269 wpa_s->wpa,
270 WNM_SLEEP_SUBELEM_GTK,
271 ptr);
272 ptr += 13 + gtk_len;
273 #ifdef CONFIG_IEEE80211W
274 } else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) {
275 if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) {
276 wpa_printf(MSG_DEBUG, "WNM: Too short IGTK "
277 "subelem");
278 break;
279 }
280 wpa_wnmsleep_install_key(wpa_s->wpa,
281 WNM_SLEEP_SUBELEM_IGTK, ptr);
282 ptr += 10 + WPA_IGTK_LEN;
283 #endif /* CONFIG_IEEE80211W */
284 } else
285 break; /* skip the loop */
286 }
287 }
288
289
290 static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
291 const u8 *frm, int len)
292 {
293 /*
294 * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data |
295 * WNM-Sleep Mode IE | TFS Response IE
296 */
297 const u8 *pos = frm; /* point to payload after the action field */
298 u16 key_len_total;
299 struct wnm_sleep_element *wnmsleep_ie = NULL;
300 /* multiple TFS Resp IE (assuming consecutive) */
301 const u8 *tfsresp_ie_start = NULL;
302 const u8 *tfsresp_ie_end = NULL;
303 #ifdef CONFIG_OCV
304 const u8 *oci_ie = NULL;
305 u8 oci_ie_len = 0;
306 #endif /* CONFIG_OCV */
307 size_t left;
308
309 if (!wpa_s->wnmsleep_used) {
310 wpa_printf(MSG_DEBUG,
311 "WNM: Ignore WNM-Sleep Mode Response frame since WNM-Sleep Mode operation has not been requested");
312 return;
313 }
314
315 if (len < 3)
316 return;
317 key_len_total = WPA_GET_LE16(frm + 1);
318
319 wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d",
320 frm[0], key_len_total);
321 left = len - 3;
322 if (key_len_total > left) {
323 wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field");
324 return;
325 }
326 pos += 3 + key_len_total;
327 while (pos - frm + 1 < len) {
328 u8 ie_len = *(pos + 1);
329 if (2 + ie_len > frm + len - pos) {
330 wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len);
331 break;
332 }
333 wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len);
334 if (*pos == WLAN_EID_WNMSLEEP && ie_len >= 4)
335 wnmsleep_ie = (struct wnm_sleep_element *) pos;
336 else if (*pos == WLAN_EID_TFS_RESP) {
337 if (!tfsresp_ie_start)
338 tfsresp_ie_start = pos;
339 tfsresp_ie_end = pos;
340 #ifdef CONFIG_OCV
341 } else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 &&
342 pos[2] == WLAN_EID_EXT_OCV_OCI) {
343 oci_ie = pos + 3;
344 oci_ie_len = ie_len - 1;
345 #endif /* CONFIG_OCV */
346 } else
347 wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos);
348 pos += ie_len + 2;
349 }
350
351 if (!wnmsleep_ie) {
352 wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found");
353 return;
354 }
355
356 #ifdef CONFIG_OCV
357 if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT &&
358 wpa_sm_ocv_enabled(wpa_s->wpa)) {
359 struct wpa_channel_info ci;
360
361 if (wpa_drv_channel_info(wpa_s, &ci) != 0) {
362 wpa_msg(wpa_s, MSG_WARNING,
363 "Failed to get channel info to validate received OCI in WNM-Sleep Mode frame");
364 return;
365 }
366
367 if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
368 channel_width_to_int(ci.chanwidth),
369 ci.seg1_idx) != 0) {
370 wpa_msg(wpa_s, MSG_WARNING, "WNM: %s", ocv_errorstr);
371 return;
372 }
373 }
374 #endif /* CONFIG_OCV */
375
376 wpa_s->wnmsleep_used = 0;
377
378 if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT ||
379 wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) {
380 wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response "
381 "frame (action=%d, intval=%d)",
382 wnmsleep_ie->action_type, wnmsleep_ie->intval);
383 if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) {
384 wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start,
385 tfsresp_ie_end);
386 } else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
387 wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total);
388 }
389 } else {
390 wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame "
391 "(action=%d, intval=%d)",
392 wnmsleep_ie->action_type, wnmsleep_ie->intval);
393 if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER)
394 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL,
395 wpa_s->bssid, NULL, NULL);
396 else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT)
397 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL,
398 wpa_s->bssid, NULL, NULL);
399 }
400 }
401
402
403 void wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
404 {
405 int i;
406
407 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
408 os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
409 os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
410 }
411
412 wpa_s->wnm_num_neighbor_report = 0;
413 os_free(wpa_s->wnm_neighbor_report_elements);
414 wpa_s->wnm_neighbor_report_elements = NULL;
415
416 wpabuf_free(wpa_s->coloc_intf_elems);
417 wpa_s->coloc_intf_elems = NULL;
418 }
419
420
421 static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
422 u8 id, u8 elen, const u8 *pos)
423 {
424 switch (id) {
425 case WNM_NEIGHBOR_TSF:
426 if (elen < 2 + 2) {
427 wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
428 break;
429 }
430 rep->tsf_offset = WPA_GET_LE16(pos);
431 rep->beacon_int = WPA_GET_LE16(pos + 2);
432 rep->tsf_present = 1;
433 break;
434 case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
435 if (elen < 2) {
436 wpa_printf(MSG_DEBUG, "WNM: Too short condensed "
437 "country string");
438 break;
439 }
440 os_memcpy(rep->country, pos, 2);
441 rep->country_present = 1;
442 break;
443 case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
444 if (elen < 1) {
445 wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition "
446 "candidate");
447 break;
448 }
449 rep->preference = pos[0];
450 rep->preference_present = 1;
451 break;
452 case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
453 rep->bss_term_tsf = WPA_GET_LE64(pos);
454 rep->bss_term_dur = WPA_GET_LE16(pos + 8);
455 rep->bss_term_present = 1;
456 break;
457 case WNM_NEIGHBOR_BEARING:
458 if (elen < 8) {
459 wpa_printf(MSG_DEBUG, "WNM: Too short neighbor "
460 "bearing");
461 break;
462 }
463 rep->bearing = WPA_GET_LE16(pos);
464 rep->distance = WPA_GET_LE32(pos + 2);
465 rep->rel_height = WPA_GET_LE16(pos + 2 + 4);
466 rep->bearing_present = 1;
467 break;
468 case WNM_NEIGHBOR_MEASUREMENT_PILOT:
469 if (elen < 1) {
470 wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
471 "pilot");
472 break;
473 }
474 os_free(rep->meas_pilot);
475 rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
476 if (rep->meas_pilot == NULL)
477 break;
478 rep->meas_pilot->measurement_pilot = pos[0];
479 rep->meas_pilot->subelem_len = elen - 1;
480 os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1);
481 break;
482 case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
483 if (elen < 5) {
484 wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
485 "capabilities");
486 break;
487 }
488 os_memcpy(rep->rm_capab, pos, 5);
489 rep->rm_capab_present = 1;
490 break;
491 case WNM_NEIGHBOR_MULTIPLE_BSSID:
492 if (elen < 1) {
493 wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
494 break;
495 }
496 os_free(rep->mul_bssid);
497 rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
498 if (rep->mul_bssid == NULL)
499 break;
500 rep->mul_bssid->max_bssid_indicator = pos[0];
501 rep->mul_bssid->subelem_len = elen - 1;
502 os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
503 break;
504 }
505 }
506
507
508 static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan)
509 {
510 struct wpa_bss *bss = wpa_s->current_bss;
511 const char *country = NULL;
512 int freq;
513
514 if (bss) {
515 const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY);
516
517 if (elem && elem[1] >= 2)
518 country = (const char *) (elem + 2);
519 }
520
521 freq = ieee80211_chan_to_freq(country, op_class, chan);
522 if (freq <= 0 && op_class == 0) {
523 /*
524 * Some APs do not advertise correct operating class
525 * information. Try to determine the most likely operating
526 * frequency based on the channel number.
527 */
528 if (chan >= 1 && chan <= 13)
529 freq = 2407 + chan * 5;
530 else if (chan == 14)
531 freq = 2484;
532 else if (chan >= 36 && chan <= 169)
533 freq = 5000 + chan * 5;
534 }
535 return freq;
536 }
537
538
539 static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
540 const u8 *pos, u8 len,
541 struct neighbor_report *rep)
542 {
543 u8 left = len;
544
545 if (left < 13) {
546 wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report");
547 return;
548 }
549
550 os_memcpy(rep->bssid, pos, ETH_ALEN);
551 rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN);
552 rep->regulatory_class = *(pos + 10);
553 rep->channel_number = *(pos + 11);
554 rep->phy_type = *(pos + 12);
555
556 pos += 13;
557 left -= 13;
558
559 while (left >= 2) {
560 u8 id, elen;
561
562 id = *pos++;
563 elen = *pos++;
564 wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen);
565 left -= 2;
566 if (elen > left) {
567 wpa_printf(MSG_DEBUG,
568 "WNM: Truncated neighbor report subelement");
569 break;
570 }
571 wnm_parse_neighbor_report_elem(rep, id, elen, pos);
572 left -= elen;
573 pos += elen;
574 }
575
576 rep->freq = wnm_nei_get_chan(wpa_s, rep->regulatory_class,
577 rep->channel_number);
578 }
579
580
581 static void wnm_clear_acceptable(struct wpa_supplicant *wpa_s)
582 {
583 unsigned int i;
584
585 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++)
586 wpa_s->wnm_neighbor_report_elements[i].acceptable = 0;
587 }
588
589
590 static struct wpa_bss * get_first_acceptable(struct wpa_supplicant *wpa_s)
591 {
592 unsigned int i;
593 struct neighbor_report *nei;
594
595 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
596 nei = &wpa_s->wnm_neighbor_report_elements[i];
597 if (nei->acceptable)
598 return wpa_bss_get_bssid(wpa_s, nei->bssid);
599 }
600
601 return NULL;
602 }
603
604
605 #ifdef CONFIG_MBO
606 static struct wpa_bss *
607 get_mbo_transition_candidate(struct wpa_supplicant *wpa_s,
608 enum mbo_transition_reject_reason *reason)
609 {
610 struct wpa_bss *target = NULL;
611 struct wpa_bss_trans_info params;
612 struct wpa_bss_candidate_info *info = NULL;
613 struct neighbor_report *nei = wpa_s->wnm_neighbor_report_elements;
614 u8 *first_candidate_bssid = NULL, *pos;
615 unsigned int i;
616
617 params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason;
618 params.n_candidates = 0;
619 params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN);
620 if (!params.bssid)
621 return NULL;
622
623 pos = params.bssid;
624 for (i = 0; i < wpa_s->wnm_num_neighbor_report; nei++, i++) {
625 if (nei->is_first)
626 first_candidate_bssid = nei->bssid;
627 if (!nei->acceptable)
628 continue;
629 os_memcpy(pos, nei->bssid, ETH_ALEN);
630 pos += ETH_ALEN;
631 params.n_candidates++;
632 }
633
634 if (!params.n_candidates)
635 goto end;
636
637 info = wpa_drv_get_bss_trans_status(wpa_s, &params);
638 if (!info) {
639 /* If failed to get candidate BSS transition status from driver,
640 * get the first acceptable candidate from wpa_supplicant.
641 */
642 target = wpa_bss_get_bssid(wpa_s, params.bssid);
643 goto end;
644 }
645
646 /* Get the first acceptable candidate from driver */
647 for (i = 0; i < info->num; i++) {
648 if (info->candidates[i].is_accept) {
649 target = wpa_bss_get_bssid(wpa_s,
650 info->candidates[i].bssid);
651 goto end;
652 }
653 }
654
655 /* If Disassociation Imminent is set and driver rejects all the
656 * candidate select first acceptable candidate which has
657 * rssi > disassoc_imminent_rssi_threshold
658 */
659 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
660 for (i = 0; i < info->num; i++) {
661 target = wpa_bss_get_bssid(wpa_s,
662 info->candidates[i].bssid);
663 if (target &&
664 (target->level <
665 wpa_s->conf->disassoc_imminent_rssi_threshold))
666 continue;
667 goto end;
668 }
669 }
670
671 /* While sending BTM reject use reason code of the first candidate
672 * received in BTM request frame
673 */
674 if (reason) {
675 for (i = 0; i < info->num; i++) {
676 if (first_candidate_bssid &&
677 os_memcmp(first_candidate_bssid,
678 info->candidates[i].bssid, ETH_ALEN) == 0)
679 {
680 *reason = info->candidates[i].reject_reason;
681 break;
682 }
683 }
684 }
685
686 target = NULL;
687
688 end:
689 os_free(params.bssid);
690 if (info) {
691 os_free(info->candidates);
692 os_free(info);
693 }
694 return target;
695 }
696 #endif /* CONFIG_MBO */
697
698
699 static struct wpa_bss *
700 compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs,
701 enum mbo_transition_reject_reason *reason)
702 {
703 u8 i;
704 struct wpa_bss *bss = wpa_s->current_bss;
705 struct wpa_bss *target;
706
707 if (!bss)
708 return NULL;
709
710 wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
711 MAC2STR(wpa_s->bssid), bss->level);
712
713 wnm_clear_acceptable(wpa_s);
714
715 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
716 struct neighbor_report *nei;
717
718 nei = &wpa_s->wnm_neighbor_report_elements[i];
719 if (nei->preference_present && nei->preference == 0) {
720 wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR,
721 MAC2STR(nei->bssid));
722 continue;
723 }
724
725 target = wpa_bss_get_bssid(wpa_s, nei->bssid);
726 if (!target) {
727 wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
728 " (pref %d) not found in scan results",
729 MAC2STR(nei->bssid),
730 nei->preference_present ? nei->preference :
731 -1);
732 continue;
733 }
734
735 if (age_secs) {
736 struct os_reltime now;
737
738 if (os_get_reltime(&now) == 0 &&
739 os_reltime_expired(&now, &target->last_update,
740 age_secs)) {
741 wpa_printf(MSG_DEBUG,
742 "Candidate BSS is more than %ld seconds old",
743 age_secs);
744 continue;
745 }
746 }
747
748 if (bss->ssid_len != target->ssid_len ||
749 os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
750 /*
751 * TODO: Could consider allowing transition to another
752 * ESS if PMF was enabled for the association.
753 */
754 wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
755 " (pref %d) in different ESS",
756 MAC2STR(nei->bssid),
757 nei->preference_present ? nei->preference :
758 -1);
759 continue;
760 }
761
762 if (wpa_s->current_ssid &&
763 !wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid,
764 1, 0)) {
765 wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
766 " (pref %d) does not match the current network profile",
767 MAC2STR(nei->bssid),
768 nei->preference_present ? nei->preference :
769 -1);
770 continue;
771 }
772
773 if (wpa_is_bss_tmp_disallowed(wpa_s, target)) {
774 wpa_printf(MSG_DEBUG,
775 "MBO: Candidate BSS " MACSTR
776 " retry delay is not over yet",
777 MAC2STR(nei->bssid));
778 continue;
779 }
780
781 if (target->level < bss->level && target->level < -80) {
782 wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
783 " (pref %d) does not have sufficient signal level (%d)",
784 MAC2STR(nei->bssid),
785 nei->preference_present ? nei->preference :
786 -1,
787 target->level);
788 continue;
789 }
790
791 nei->acceptable = 1;
792 }
793
794 #ifdef CONFIG_MBO
795 if (wpa_s->wnm_mbo_trans_reason_present)
796 target = get_mbo_transition_candidate(wpa_s, reason);
797 else
798 target = get_first_acceptable(wpa_s);
799 #else /* CONFIG_MBO */
800 target = get_first_acceptable(wpa_s);
801 #endif /* CONFIG_MBO */
802
803 if (target) {
804 wpa_printf(MSG_DEBUG,
805 "WNM: Found an acceptable preferred transition candidate BSS "
806 MACSTR " (RSSI %d)",
807 MAC2STR(target->bssid), target->level);
808 }
809
810 return target;
811 }
812
813
814 static int wpa_bss_ies_eq(struct wpa_bss *a, struct wpa_bss *b, u8 eid)
815 {
816 const u8 *ie_a, *ie_b;
817
818 if (!a || !b)
819 return 0;
820
821 ie_a = wpa_bss_get_ie(a, eid);
822 ie_b = wpa_bss_get_ie(b, eid);
823
824 if (!ie_a || !ie_b || ie_a[1] != ie_b[1])
825 return 0;
826
827 return os_memcmp(ie_a, ie_b, ie_a[1]) == 0;
828 }
829
830
831 static u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
832 {
833 u32 info = 0;
834
835 info |= NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH;
836
837 /*
838 * Leave the security and key scope bits unset to indicate that the
839 * security information is not available.
840 */
841
842 if (bss->caps & WLAN_CAPABILITY_SPECTRUM_MGMT)
843 info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
844 if (bss->caps & WLAN_CAPABILITY_QOS)
845 info |= NEI_REP_BSSID_INFO_QOS;
846 if (bss->caps & WLAN_CAPABILITY_APSD)
847 info |= NEI_REP_BSSID_INFO_APSD;
848 if (bss->caps & WLAN_CAPABILITY_RADIO_MEASUREMENT)
849 info |= NEI_REP_BSSID_INFO_RM;
850 if (bss->caps & WLAN_CAPABILITY_DELAYED_BLOCK_ACK)
851 info |= NEI_REP_BSSID_INFO_DELAYED_BA;
852 if (bss->caps & WLAN_CAPABILITY_IMM_BLOCK_ACK)
853 info |= NEI_REP_BSSID_INFO_IMM_BA;
854 if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_MOBILITY_DOMAIN))
855 info |= NEI_REP_BSSID_INFO_MOBILITY_DOMAIN;
856 if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_HT_CAP))
857 info |= NEI_REP_BSSID_INFO_HT;
858
859 return info;
860 }
861
862
863 static int wnm_add_nei_rep(struct wpabuf **buf, const u8 *bssid,
864 u32 bss_info, u8 op_class, u8 chan, u8 phy_type,
865 u8 pref)
866 {
867 if (wpabuf_len(*buf) + 18 >
868 IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN) {
869 wpa_printf(MSG_DEBUG,
870 "WNM: No room in frame for Neighbor Report element");
871 return -1;
872 }
873
874 if (wpabuf_resize(buf, 18) < 0) {
875 wpa_printf(MSG_DEBUG,
876 "WNM: Failed to allocate memory for Neighbor Report element");
877 return -1;
878 }
879
880 wpabuf_put_u8(*buf, WLAN_EID_NEIGHBOR_REPORT);
881 /* length: 13 for basic neighbor report + 3 for preference subelement */
882 wpabuf_put_u8(*buf, 16);
883 wpabuf_put_data(*buf, bssid, ETH_ALEN);
884 wpabuf_put_le32(*buf, bss_info);
885 wpabuf_put_u8(*buf, op_class);
886 wpabuf_put_u8(*buf, chan);
887 wpabuf_put_u8(*buf, phy_type);
888 wpabuf_put_u8(*buf, WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE);
889 wpabuf_put_u8(*buf, 1);
890 wpabuf_put_u8(*buf, pref);
891 return 0;
892 }
893
894
895 static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s,
896 struct wpa_bss *bss, struct wpabuf **buf,
897 u8 pref)
898 {
899 const u8 *ie;
900 u8 op_class, chan;
901 int sec_chan = 0, vht = 0;
902 enum phy_type phy_type;
903 u32 info;
904 struct ieee80211_ht_operation *ht_oper = NULL;
905 struct ieee80211_vht_operation *vht_oper = NULL;
906
907 ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
908 if (ie && ie[1] >= 2) {
909 ht_oper = (struct ieee80211_ht_operation *) (ie + 2);
910
911 if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
912 sec_chan = 1;
913 else if (ht_oper->ht_param &
914 HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
915 sec_chan = -1;
916 }
917
918 ie = wpa_bss_get_ie(bss, WLAN_EID_VHT_OPERATION);
919 if (ie && ie[1] >= 1) {
920 vht_oper = (struct ieee80211_vht_operation *) (ie + 2);
921
922 if (vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_80MHZ ||
923 vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_160MHZ ||
924 vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_80P80MHZ)
925 vht = vht_oper->vht_op_info_chwidth;
926 }
927
928 if (ieee80211_freq_to_channel_ext(bss->freq, sec_chan, vht, &op_class,
929 &chan) == NUM_HOSTAPD_MODES) {
930 wpa_printf(MSG_DEBUG,
931 "WNM: Cannot determine operating class and channel");
932 return -2;
933 }
934
935 phy_type = ieee80211_get_phy_type(bss->freq, (ht_oper != NULL),
936 (vht_oper != NULL));
937 if (phy_type == PHY_TYPE_UNSPECIFIED) {
938 wpa_printf(MSG_DEBUG,
939 "WNM: Cannot determine BSS phy type for Neighbor Report");
940 return -2;
941 }
942
943 info = wnm_get_bss_info(wpa_s, bss);
944
945 return wnm_add_nei_rep(buf, bss->bssid, info, op_class, chan, phy_type,
946 pref);
947 }
948
949
950 static void wnm_add_cand_list(struct wpa_supplicant *wpa_s, struct wpabuf **buf)
951 {
952 unsigned int i, pref = 255;
953 struct os_reltime now;
954 struct wpa_ssid *ssid = wpa_s->current_ssid;
955
956 if (!ssid)
957 return;
958
959 /*
960 * TODO: Define when scan results are no longer valid for the candidate
961 * list.
962 */
963 os_get_reltime(&now);
964 if (os_reltime_expired(&now, &wpa_s->last_scan, 10))
965 return;
966
967 wpa_printf(MSG_DEBUG,
968 "WNM: Add candidate list to BSS Transition Management Response frame");
969 for (i = 0; i < wpa_s->last_scan_res_used && pref; i++) {
970 struct wpa_bss *bss = wpa_s->last_scan_res[i];
971 int res;
972
973 if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0)) {
974 res = wnm_nei_rep_add_bss(wpa_s, bss, buf, pref--);
975 if (res == -2)
976 continue; /* could not build entry for BSS */
977 if (res < 0)
978 break; /* no more room for candidates */
979 if (pref == 1)
980 break;
981 }
982 }
983
984 wpa_hexdump_buf(MSG_DEBUG,
985 "WNM: BSS Transition Management Response candidate list",
986 *buf);
987 }
988
989
990 #define BTM_RESP_MIN_SIZE 5 + ETH_ALEN
991
992 static void wnm_send_bss_transition_mgmt_resp(
993 struct wpa_supplicant *wpa_s, u8 dialog_token,
994 enum bss_trans_mgmt_status_code status,
995 enum mbo_transition_reject_reason reason,
996 u8 delay, const u8 *target_bssid)
997 {
998 struct wpabuf *buf;
999 int res;
1000
1001 wpa_printf(MSG_DEBUG,
1002 "WNM: Send BSS Transition Management Response to " MACSTR
1003 " dialog_token=%u status=%u reason=%u delay=%d",
1004 MAC2STR(wpa_s->bssid), dialog_token, status, reason, delay);
1005 if (!wpa_s->current_bss) {
1006 wpa_printf(MSG_DEBUG,
1007 "WNM: Current BSS not known - drop response");
1008 return;
1009 }
1010
1011 buf = wpabuf_alloc(BTM_RESP_MIN_SIZE);
1012 if (!buf) {
1013 wpa_printf(MSG_DEBUG,
1014 "WNM: Failed to allocate memory for BTM response");
1015 return;
1016 }
1017
1018 wpabuf_put_u8(buf, WLAN_ACTION_WNM);
1019 wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP);
1020 wpabuf_put_u8(buf, dialog_token);
1021 wpabuf_put_u8(buf, status);
1022 wpabuf_put_u8(buf, delay);
1023 if (target_bssid) {
1024 wpabuf_put_data(buf, target_bssid, ETH_ALEN);
1025 } else if (status == WNM_BSS_TM_ACCEPT) {
1026 /*
1027 * P802.11-REVmc clarifies that the Target BSSID field is always
1028 * present when status code is zero, so use a fake value here if
1029 * no BSSID is yet known.
1030 */
1031 wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN);
1032 }
1033
1034 if (status == WNM_BSS_TM_ACCEPT)
1035 wnm_add_cand_list(wpa_s, &buf);
1036
1037 #ifdef CONFIG_MBO
1038 if (status != WNM_BSS_TM_ACCEPT &&
1039 wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) {
1040 u8 mbo[10];
1041 size_t ret;
1042
1043 ret = wpas_mbo_ie_bss_trans_reject(wpa_s, mbo, sizeof(mbo),
1044 reason);
1045 if (ret) {
1046 if (wpabuf_resize(&buf, ret) < 0) {
1047 wpabuf_free(buf);
1048 wpa_printf(MSG_DEBUG,
1049 "WNM: Failed to allocate memory for MBO IE");
1050 return;
1051 }
1052
1053 wpabuf_put_data(buf, mbo, ret);
1054 }
1055 }
1056 #endif /* CONFIG_MBO */
1057
1058 res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
1059 wpa_s->own_addr, wpa_s->bssid,
1060 wpabuf_head_u8(buf), wpabuf_len(buf), 0);
1061 if (res < 0) {
1062 wpa_printf(MSG_DEBUG,
1063 "WNM: Failed to send BSS Transition Management Response");
1064 }
1065
1066 wpabuf_free(buf);
1067 }
1068
1069
1070 static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s,
1071 struct wpa_bss *bss, struct wpa_ssid *ssid,
1072 int after_new_scan)
1073 {
1074 wpa_dbg(wpa_s, MSG_DEBUG,
1075 "WNM: Transition to BSS " MACSTR
1076 " based on BSS Transition Management Request (old BSSID "
1077 MACSTR " after_new_scan=%d)",
1078 MAC2STR(bss->bssid), MAC2STR(wpa_s->bssid), after_new_scan);
1079
1080 /* Send the BSS Management Response - Accept */
1081 if (wpa_s->wnm_reply) {
1082 wpa_s->wnm_reply = 0;
1083 wpa_printf(MSG_DEBUG,
1084 "WNM: Sending successful BSS Transition Management Response");
1085 wnm_send_bss_transition_mgmt_resp(
1086 wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT,
1087 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
1088 bss->bssid);
1089 }
1090
1091 if (bss == wpa_s->current_bss) {
1092 wpa_printf(MSG_DEBUG,
1093 "WNM: Already associated with the preferred candidate");
1094 wnm_deallocate_memory(wpa_s);
1095 return;
1096 }
1097
1098 wpa_s->reassociate = 1;
1099 wpa_printf(MSG_DEBUG, "WNM: Issuing connect");
1100 wpa_supplicant_connect(wpa_s, bss, ssid);
1101 wnm_deallocate_memory(wpa_s);
1102 }
1103
1104
1105 int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
1106 {
1107 struct wpa_bss *bss;
1108 struct wpa_ssid *ssid = wpa_s->current_ssid;
1109 enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
1110 enum mbo_transition_reject_reason reason =
1111 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
1112
1113 if (!wpa_s->wnm_neighbor_report_elements)
1114 return 0;
1115
1116 wpa_dbg(wpa_s, MSG_DEBUG,
1117 "WNM: Process scan results for BSS Transition Management");
1118 if (os_reltime_before(&wpa_s->wnm_cand_valid_until,
1119 &wpa_s->scan_trigger_time)) {
1120 wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
1121 wnm_deallocate_memory(wpa_s);
1122 return 0;
1123 }
1124
1125 if (!wpa_s->current_bss ||
1126 os_memcmp(wpa_s->wnm_cand_from_bss, wpa_s->current_bss->bssid,
1127 ETH_ALEN) != 0) {
1128 wpa_printf(MSG_DEBUG, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it");
1129 return 0;
1130 }
1131
1132 /* Compare the Neighbor Report and scan results */
1133 bss = compare_scan_neighbor_results(wpa_s, 0, &reason);
1134 if (!bss) {
1135 wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
1136 status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
1137 goto send_bss_resp_fail;
1138 }
1139
1140 /* Associate to the network */
1141 wnm_bss_tm_connect(wpa_s, bss, ssid, 1);
1142 return 1;
1143
1144 send_bss_resp_fail:
1145 if (!reply_on_fail)
1146 return 0;
1147
1148 /* Send reject response for all the failures */
1149
1150 if (wpa_s->wnm_reply) {
1151 wpa_s->wnm_reply = 0;
1152 wnm_send_bss_transition_mgmt_resp(wpa_s,
1153 wpa_s->wnm_dialog_token,
1154 status, reason, 0, NULL);
1155 }
1156 wnm_deallocate_memory(wpa_s);
1157
1158 return 0;
1159 }
1160
1161
1162 static int cand_pref_compar(const void *a, const void *b)
1163 {
1164 const struct neighbor_report *aa = a;
1165 const struct neighbor_report *bb = b;
1166
1167 if (!aa->preference_present && !bb->preference_present)
1168 return 0;
1169 if (!aa->preference_present)
1170 return 1;
1171 if (!bb->preference_present)
1172 return -1;
1173 if (bb->preference > aa->preference)
1174 return 1;
1175 if (bb->preference < aa->preference)
1176 return -1;
1177 return 0;
1178 }
1179
1180
1181 static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s)
1182 {
1183 if (!wpa_s->wnm_neighbor_report_elements)
1184 return;
1185 qsort(wpa_s->wnm_neighbor_report_elements,
1186 wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report),
1187 cand_pref_compar);
1188 }
1189
1190
1191 static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s)
1192 {
1193 unsigned int i;
1194
1195 wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List");
1196 if (!wpa_s->wnm_neighbor_report_elements)
1197 return;
1198 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
1199 struct neighbor_report *nei;
1200
1201 nei = &wpa_s->wnm_neighbor_report_elements[i];
1202 wpa_printf(MSG_DEBUG, "%u: " MACSTR
1203 " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d",
1204 i, MAC2STR(nei->bssid), nei->bssid_info,
1205 nei->regulatory_class,
1206 nei->channel_number, nei->phy_type,
1207 nei->preference_present ? nei->preference : -1,
1208 nei->freq);
1209 }
1210 }
1211
1212
1213 static int chan_supported(struct wpa_supplicant *wpa_s, int freq)
1214 {
1215 unsigned int i;
1216
1217 for (i = 0; i < wpa_s->hw.num_modes; i++) {
1218 struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i];
1219 int j;
1220
1221 for (j = 0; j < mode->num_channels; j++) {
1222 struct hostapd_channel_data *chan;
1223
1224 chan = &mode->channels[j];
1225 if (chan->freq == freq &&
1226 !(chan->flag & HOSTAPD_CHAN_DISABLED))
1227 return 1;
1228 }
1229 }
1230
1231 return 0;
1232 }
1233
1234
1235 static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s)
1236 {
1237 int *freqs;
1238 int num_freqs = 0;
1239 unsigned int i;
1240
1241 if (!wpa_s->wnm_neighbor_report_elements)
1242 return;
1243
1244 if (wpa_s->hw.modes == NULL)
1245 return;
1246
1247 os_free(wpa_s->next_scan_freqs);
1248 wpa_s->next_scan_freqs = NULL;
1249
1250 freqs = os_calloc(wpa_s->wnm_num_neighbor_report + 1, sizeof(int));
1251 if (freqs == NULL)
1252 return;
1253
1254 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
1255 struct neighbor_report *nei;
1256
1257 nei = &wpa_s->wnm_neighbor_report_elements[i];
1258 if (nei->freq <= 0) {
1259 wpa_printf(MSG_DEBUG,
1260 "WNM: Unknown neighbor operating frequency for "
1261 MACSTR " - scan all channels",
1262 MAC2STR(nei->bssid));
1263 os_free(freqs);
1264 return;
1265 }
1266 if (chan_supported(wpa_s, nei->freq))
1267 add_freq(freqs, &num_freqs, nei->freq);
1268 }
1269
1270 if (num_freqs == 0) {
1271 os_free(freqs);
1272 return;
1273 }
1274
1275 wpa_printf(MSG_DEBUG,
1276 "WNM: Scan %d frequencies based on transition candidate list",
1277 num_freqs);
1278 wpa_s->next_scan_freqs = freqs;
1279 }
1280
1281
1282 static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s)
1283 {
1284 struct wpa_scan_results *scan_res;
1285 struct wpa_bss *bss;
1286 struct wpa_ssid *ssid = wpa_s->current_ssid;
1287 u8 i, found = 0;
1288 size_t j;
1289
1290 wpa_dbg(wpa_s, MSG_DEBUG,
1291 "WNM: Fetch current scan results from the driver for checking transition candidates");
1292 scan_res = wpa_drv_get_scan_results2(wpa_s);
1293 if (!scan_res) {
1294 wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Failed to get scan results");
1295 return 0;
1296 }
1297
1298 if (scan_res->fetch_time.sec == 0)
1299 os_get_reltime(&scan_res->fetch_time);
1300
1301 filter_scan_res(wpa_s, scan_res);
1302
1303 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
1304 struct neighbor_report *nei;
1305
1306 nei = &wpa_s->wnm_neighbor_report_elements[i];
1307 if (nei->preference_present && nei->preference == 0)
1308 continue;
1309
1310 for (j = 0; j < scan_res->num; j++) {
1311 struct wpa_scan_res *res;
1312 const u8 *ssid_ie;
1313
1314 res = scan_res->res[j];
1315 if (os_memcmp(nei->bssid, res->bssid, ETH_ALEN) != 0 ||
1316 res->age > WNM_SCAN_RESULT_AGE * 1000)
1317 continue;
1318 bss = wpa_s->current_bss;
1319 ssid_ie = wpa_scan_get_ie(res, WLAN_EID_SSID);
1320 if (bss && ssid_ie &&
1321 (bss->ssid_len != ssid_ie[1] ||
1322 os_memcmp(bss->ssid, ssid_ie + 2,
1323 bss->ssid_len) != 0))
1324 continue;
1325
1326 /* Potential candidate found */
1327 found = 1;
1328 scan_snr(res);
1329 scan_est_throughput(wpa_s, res);
1330 wpa_bss_update_scan_res(wpa_s, res,
1331 &scan_res->fetch_time);
1332 }
1333 }
1334
1335 wpa_scan_results_free(scan_res);
1336 if (!found) {
1337 wpa_dbg(wpa_s, MSG_DEBUG,
1338 "WNM: No transition candidate matches existing scan results");
1339 return 0;
1340 }
1341
1342 bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE, NULL);
1343 if (!bss) {
1344 wpa_dbg(wpa_s, MSG_DEBUG,
1345 "WNM: Comparison of scan results against transition candidates did not find matches");
1346 return 0;
1347 }
1348
1349 /* Associate to the network */
1350 wnm_bss_tm_connect(wpa_s, bss, ssid, 0);
1351 return 1;
1352 }
1353
1354
1355 static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
1356 const u8 *pos, const u8 *end,
1357 int reply)
1358 {
1359 unsigned int beacon_int;
1360 u8 valid_int;
1361 #ifdef CONFIG_MBO
1362 const u8 *vendor;
1363 #endif /* CONFIG_MBO */
1364
1365 if (end - pos < 5)
1366 return;
1367
1368 #ifdef CONFIG_MBO
1369 wpa_s->wnm_mbo_trans_reason_present = 0;
1370 wpa_s->wnm_mbo_transition_reason = 0;
1371 #endif /* CONFIG_MBO */
1372
1373 if (wpa_s->current_bss)
1374 beacon_int = wpa_s->current_bss->beacon_int;
1375 else
1376 beacon_int = 100; /* best guess */
1377
1378 wpa_s->wnm_dialog_token = pos[0];
1379 wpa_s->wnm_mode = pos[1];
1380 wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
1381 valid_int = pos[4];
1382 wpa_s->wnm_reply = reply;
1383
1384 wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
1385 "dialog_token=%u request_mode=0x%x "
1386 "disassoc_timer=%u validity_interval=%u",
1387 wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
1388 wpa_s->wnm_dissoc_timer, valid_int);
1389
1390 #if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS)
1391 if (wpa_s->reject_btm_req_reason) {
1392 wpa_printf(MSG_INFO,
1393 "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d",
1394 wpa_s->reject_btm_req_reason);
1395 wnm_send_bss_transition_mgmt_resp(
1396 wpa_s, wpa_s->wnm_dialog_token,
1397 wpa_s->reject_btm_req_reason,
1398 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
1399 return;
1400 }
1401 #endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */
1402
1403 pos += 5;
1404
1405 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
1406 if (end - pos < 12) {
1407 wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
1408 return;
1409 }
1410 os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
1411 pos += 12; /* BSS Termination Duration */
1412 }
1413
1414 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
1415 char url[256];
1416
1417 if (end - pos < 1 || 1 + pos[0] > end - pos) {
1418 wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
1419 "Management Request (URL)");
1420 return;
1421 }
1422 os_memcpy(url, pos + 1, pos[0]);
1423 url[pos[0]] = '\0';
1424 pos += 1 + pos[0];
1425
1426 wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s",
1427 wpa_sm_pmf_enabled(wpa_s->wpa),
1428 wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url);
1429 }
1430
1431 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
1432 wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
1433 "Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
1434 if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) {
1435 /* TODO: mark current BSS less preferred for
1436 * selection */
1437 wpa_printf(MSG_DEBUG, "Trying to find another BSS");
1438 wpa_supplicant_req_scan(wpa_s, 0, 0);
1439 }
1440 }
1441
1442 #ifdef CONFIG_MBO
1443 vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC);
1444 if (vendor)
1445 wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]);
1446 #endif /* CONFIG_MBO */
1447
1448 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
1449 unsigned int valid_ms;
1450
1451 wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
1452 wnm_deallocate_memory(wpa_s);
1453 wpa_s->wnm_neighbor_report_elements = os_calloc(
1454 WNM_MAX_NEIGHBOR_REPORT,
1455 sizeof(struct neighbor_report));
1456 if (wpa_s->wnm_neighbor_report_elements == NULL)
1457 return;
1458
1459 while (end - pos >= 2 &&
1460 wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT)
1461 {
1462 u8 tag = *pos++;
1463 u8 len = *pos++;
1464
1465 wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u",
1466 tag);
1467 if (len > end - pos) {
1468 wpa_printf(MSG_DEBUG, "WNM: Truncated request");
1469 return;
1470 }
1471 if (tag == WLAN_EID_NEIGHBOR_REPORT) {
1472 struct neighbor_report *rep;
1473 rep = &wpa_s->wnm_neighbor_report_elements[
1474 wpa_s->wnm_num_neighbor_report];
1475 wnm_parse_neighbor_report(wpa_s, pos, len, rep);
1476 wpa_s->wnm_num_neighbor_report++;
1477 #ifdef CONFIG_MBO
1478 if (wpa_s->wnm_mbo_trans_reason_present &&
1479 wpa_s->wnm_num_neighbor_report == 1) {
1480 rep->is_first = 1;
1481 wpa_printf(MSG_DEBUG,
1482 "WNM: First transition candidate is "
1483 MACSTR, MAC2STR(rep->bssid));
1484 }
1485 #endif /* CONFIG_MBO */
1486 }
1487
1488 pos += len;
1489 }
1490
1491 if (!wpa_s->wnm_num_neighbor_report) {
1492 wpa_printf(MSG_DEBUG,
1493 "WNM: Candidate list included bit is set, but no candidates found");
1494 wnm_send_bss_transition_mgmt_resp(
1495 wpa_s, wpa_s->wnm_dialog_token,
1496 WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
1497 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
1498 NULL);
1499 return;
1500 }
1501
1502 wnm_sort_cand_list(wpa_s);
1503 wnm_dump_cand_list(wpa_s);
1504 valid_ms = valid_int * beacon_int * 128 / 125;
1505 wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms",
1506 valid_ms);
1507 os_get_reltime(&wpa_s->wnm_cand_valid_until);
1508 wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000;
1509 wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000;
1510 wpa_s->wnm_cand_valid_until.sec +=
1511 wpa_s->wnm_cand_valid_until.usec / 1000000;
1512 wpa_s->wnm_cand_valid_until.usec %= 1000000;
1513 os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN);
1514
1515 /*
1516 * Fetch the latest scan results from the kernel and check for
1517 * candidates based on those results first. This can help in
1518 * finding more up-to-date information should the driver has
1519 * done some internal scanning operations after the last scan
1520 * result update in wpa_supplicant.
1521 */
1522 if (wnm_fetch_scan_results(wpa_s) > 0)
1523 return;
1524
1525 /*
1526 * Try to use previously received scan results, if they are
1527 * recent enough to use for a connection.
1528 */
1529 if (wpa_s->last_scan_res_used > 0) {
1530 struct os_reltime now;
1531
1532 os_get_reltime(&now);
1533 if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) {
1534 wpa_printf(MSG_DEBUG,
1535 "WNM: Try to use recent scan results");
1536 if (wnm_scan_process(wpa_s, 0) > 0)
1537 return;
1538 wpa_printf(MSG_DEBUG,
1539 "WNM: No match in previous scan results - try a new scan");
1540 }
1541 }
1542
1543 wnm_set_scan_freqs(wpa_s);
1544 if (wpa_s->wnm_num_neighbor_report == 1) {
1545 os_memcpy(wpa_s->next_scan_bssid,
1546 wpa_s->wnm_neighbor_report_elements[0].bssid,
1547 ETH_ALEN);
1548 wpa_printf(MSG_DEBUG,
1549 "WNM: Scan only for a specific BSSID since there is only a single candidate "
1550 MACSTR, MAC2STR(wpa_s->next_scan_bssid));
1551 }
1552 wpa_supplicant_req_scan(wpa_s, 0, 0);
1553 } else if (reply) {
1554 enum bss_trans_mgmt_status_code status;
1555 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)
1556 status = WNM_BSS_TM_ACCEPT;
1557 else {
1558 wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates");
1559 status = WNM_BSS_TM_REJECT_UNSPECIFIED;
1560 }
1561 wnm_send_bss_transition_mgmt_resp(
1562 wpa_s, wpa_s->wnm_dialog_token, status,
1563 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
1564 }
1565 }
1566
1567
1568 #define BTM_QUERY_MIN_SIZE 4
1569
1570 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
1571 u8 query_reason,
1572 const char *btm_candidates,
1573 int cand_list)
1574 {
1575 struct wpabuf *buf;
1576 int ret;
1577
1578 wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
1579 MACSTR " query_reason=%u%s",
1580 MAC2STR(wpa_s->bssid), query_reason,
1581 cand_list ? " candidate list" : "");
1582
1583 buf = wpabuf_alloc(BTM_QUERY_MIN_SIZE);
1584 if (!buf)
1585 return -1;
1586
1587 wpabuf_put_u8(buf, WLAN_ACTION_WNM);
1588 wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_QUERY);
1589 wpabuf_put_u8(buf, 1);
1590 wpabuf_put_u8(buf, query_reason);
1591
1592 if (cand_list)
1593 wnm_add_cand_list(wpa_s, &buf);
1594
1595 if (btm_candidates) {
1596 const size_t max_len = 1000;
1597
1598 ret = wpabuf_resize(&buf, max_len);
1599 if (ret < 0) {
1600 wpabuf_free(buf);
1601 return ret;
1602 }
1603
1604 ret = ieee802_11_parse_candidate_list(btm_candidates,
1605 wpabuf_put(buf, 0),
1606 max_len);
1607 if (ret < 0) {
1608 wpabuf_free(buf);
1609 return ret;
1610 }
1611
1612 wpabuf_put(buf, ret);
1613 }
1614
1615 ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
1616 wpa_s->own_addr, wpa_s->bssid,
1617 wpabuf_head_u8(buf), wpabuf_len(buf), 0);
1618
1619 wpabuf_free(buf);
1620 return ret;
1621 }
1622
1623
1624 static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s,
1625 const u8 *sa, const u8 *data,
1626 int len)
1627 {
1628 const u8 *pos, *end, *next;
1629 u8 ie, ie_len;
1630
1631 pos = data;
1632 end = data + len;
1633
1634 while (end - pos > 1) {
1635 ie = *pos++;
1636 ie_len = *pos++;
1637 wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u",
1638 ie, ie_len);
1639 if (ie_len > end - pos) {
1640 wpa_printf(MSG_DEBUG, "WNM: Not enough room for "
1641 "subelement");
1642 break;
1643 }
1644 next = pos + ie_len;
1645 if (ie_len < 4) {
1646 pos = next;
1647 continue;
1648 }
1649 wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u",
1650 WPA_GET_BE24(pos), pos[3]);
1651
1652 #ifdef CONFIG_HS20
1653 if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 &&
1654 WPA_GET_BE24(pos) == OUI_WFA &&
1655 pos[3] == HS20_WNM_SUB_REM_NEEDED) {
1656 /* Subscription Remediation subelement */
1657 const u8 *ie_end;
1658 u8 url_len;
1659 char *url;
1660 u8 osu_method;
1661
1662 wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation "
1663 "subelement");
1664 ie_end = pos + ie_len;
1665 pos += 4;
1666 url_len = *pos++;
1667 if (url_len == 0) {
1668 wpa_printf(MSG_DEBUG, "WNM: No Server URL included");
1669 url = NULL;
1670 osu_method = 1;
1671 } else {
1672 if (url_len + 1 > ie_end - pos) {
1673 wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)",
1674 url_len,
1675 (int) (ie_end - pos));
1676 break;
1677 }
1678 url = os_malloc(url_len + 1);
1679 if (url == NULL)
1680 break;
1681 os_memcpy(url, pos, url_len);
1682 url[url_len] = '\0';
1683 osu_method = pos[url_len];
1684 }
1685 hs20_rx_subscription_remediation(wpa_s, url,
1686 osu_method);
1687 os_free(url);
1688 pos = next;
1689 continue;
1690 }
1691
1692 if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 &&
1693 WPA_GET_BE24(pos) == OUI_WFA &&
1694 pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) {
1695 const u8 *ie_end;
1696 u8 url_len;
1697 char *url;
1698 u8 code;
1699 u16 reauth_delay;
1700
1701 ie_end = pos + ie_len;
1702 pos += 4;
1703 code = *pos++;
1704 reauth_delay = WPA_GET_LE16(pos);
1705 pos += 2;
1706 url_len = *pos++;
1707 wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication "
1708 "Imminent - Reason Code %u "
1709 "Re-Auth Delay %u URL Length %u",
1710 code, reauth_delay, url_len);
1711 if (url_len > ie_end - pos)
1712 break;
1713 url = os_malloc(url_len + 1);
1714 if (url == NULL)
1715 break;
1716 os_memcpy(url, pos, url_len);
1717 url[url_len] = '\0';
1718 hs20_rx_deauth_imminent_notice(wpa_s, code,
1719 reauth_delay, url);
1720 os_free(url);
1721 pos = next;
1722 continue;
1723 }
1724
1725 if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 &&
1726 WPA_GET_BE24(pos) == OUI_WFA &&
1727 pos[3] == HS20_WNM_T_C_ACCEPTANCE) {
1728 const u8 *ie_end;
1729 u8 url_len;
1730 char *url;
1731
1732 ie_end = pos + ie_len;
1733 pos += 4;
1734 url_len = *pos++;
1735 wpa_printf(MSG_DEBUG,
1736 "WNM: HS 2.0 Terms and Conditions Acceptance (URL Length %u)",
1737 url_len);
1738 if (url_len > ie_end - pos)
1739 break;
1740 url = os_malloc(url_len + 1);
1741 if (!url)
1742 break;
1743 os_memcpy(url, pos, url_len);
1744 url[url_len] = '\0';
1745 hs20_rx_t_c_acceptance(wpa_s, url);
1746 os_free(url);
1747 pos = next;
1748 continue;
1749 }
1750 #endif /* CONFIG_HS20 */
1751
1752 pos = next;
1753 }
1754 }
1755
1756
1757 static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s,
1758 const u8 *sa, const u8 *frm, int len)
1759 {
1760 const u8 *pos, *end;
1761 u8 dialog_token, type;
1762
1763 /* Dialog Token [1] | Type [1] | Subelements */
1764
1765 if (len < 2 || sa == NULL)
1766 return;
1767 end = frm + len;
1768 pos = frm;
1769 dialog_token = *pos++;
1770 type = *pos++;
1771
1772 wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request "
1773 "(dialog_token %u type %u sa " MACSTR ")",
1774 dialog_token, type, MAC2STR(sa));
1775 wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements",
1776 pos, end - pos);
1777
1778 if (wpa_s->wpa_state != WPA_COMPLETED ||
1779 os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
1780 wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not "
1781 "from our AP - ignore it");
1782 return;
1783 }
1784
1785 switch (type) {
1786 case 1:
1787 ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos);
1788 break;
1789 default:
1790 wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown "
1791 "WNM-Notification type %u", type);
1792 break;
1793 }
1794 }
1795
1796
1797 static void ieee802_11_rx_wnm_coloc_intf_req(struct wpa_supplicant *wpa_s,
1798 const u8 *sa, const u8 *frm,
1799 int len)
1800 {
1801 u8 dialog_token, req_info, auto_report, timeout;
1802
1803 if (!wpa_s->conf->coloc_intf_reporting)
1804 return;
1805
1806 /* Dialog Token [1] | Request Info [1] */
1807
1808 if (len < 2)
1809 return;
1810 dialog_token = frm[0];
1811 req_info = frm[1];
1812 auto_report = req_info & 0x03;
1813 timeout = req_info >> 2;
1814
1815 wpa_dbg(wpa_s, MSG_DEBUG,
1816 "WNM: Received Collocated Interference Request (dialog_token %u auto_report %u timeout %u sa " MACSTR ")",
1817 dialog_token, auto_report, timeout, MAC2STR(sa));
1818
1819 if (dialog_token == 0)
1820 return; /* only nonzero values are used for request */
1821
1822 if (wpa_s->wpa_state != WPA_COMPLETED ||
1823 os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
1824 wpa_dbg(wpa_s, MSG_DEBUG,
1825 "WNM: Collocated Interference Request frame not from current AP - ignore it");
1826 return;
1827 }
1828
1829 wpa_msg(wpa_s, MSG_INFO, COLOC_INTF_REQ "%u %u %u",
1830 dialog_token, auto_report, timeout);
1831 wpa_s->coloc_intf_dialog_token = dialog_token;
1832 wpa_s->coloc_intf_auto_report = auto_report;
1833 wpa_s->coloc_intf_timeout = timeout;
1834 }
1835
1836
1837 void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
1838 const struct ieee80211_mgmt *mgmt, size_t len)
1839 {
1840 const u8 *pos, *end;
1841 u8 act;
1842
1843 if (len < IEEE80211_HDRLEN + 2)
1844 return;
1845
1846 pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
1847 act = *pos++;
1848 end = ((const u8 *) mgmt) + len;
1849
1850 wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
1851 act, MAC2STR(mgmt->sa));
1852 if (wpa_s->wpa_state < WPA_ASSOCIATED ||
1853 os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) {
1854 wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action "
1855 "frame");
1856 return;
1857 }
1858
1859 switch (act) {
1860 case WNM_BSS_TRANS_MGMT_REQ:
1861 ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
1862 !(mgmt->da[0] & 0x01));
1863 break;
1864 case WNM_SLEEP_MODE_RESP:
1865 ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos);
1866 break;
1867 case WNM_NOTIFICATION_REQ:
1868 ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos);
1869 break;
1870 case WNM_COLLOCATED_INTERFERENCE_REQ:
1871 ieee802_11_rx_wnm_coloc_intf_req(wpa_s, mgmt->sa, pos,
1872 end - pos);
1873 break;
1874 default:
1875 wpa_printf(MSG_ERROR, "WNM: Unknown request");
1876 break;
1877 }
1878 }
1879
1880
1881 int wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token,
1882 const struct wpabuf *elems)
1883 {
1884 struct wpabuf *buf;
1885 int ret;
1886
1887 if (wpa_s->wpa_state < WPA_ASSOCIATED || !elems)
1888 return -1;
1889
1890 wpa_printf(MSG_DEBUG, "WNM: Send Collocated Interference Report to "
1891 MACSTR " (dialog token %u)",
1892 MAC2STR(wpa_s->bssid), dialog_token);
1893
1894 buf = wpabuf_alloc(3 + wpabuf_len(elems));
1895 if (!buf)
1896 return -1;
1897
1898 wpabuf_put_u8(buf, WLAN_ACTION_WNM);
1899 wpabuf_put_u8(buf, WNM_COLLOCATED_INTERFERENCE_REPORT);
1900 wpabuf_put_u8(buf, dialog_token);
1901 wpabuf_put_buf(buf, elems);
1902
1903 ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
1904 wpa_s->own_addr, wpa_s->bssid,
1905 wpabuf_head_u8(buf), wpabuf_len(buf), 0);
1906 wpabuf_free(buf);
1907 return ret;
1908 }
1909
1910
1911 void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s,
1912 struct wpabuf *elems)
1913 {
1914 wpabuf_free(wpa_s->coloc_intf_elems);
1915 if (elems && wpabuf_len(elems) == 0) {
1916 wpabuf_free(elems);
1917 elems = NULL;
1918 }
1919 wpa_s->coloc_intf_elems = elems;
1920
1921 if (wpa_s->conf->coloc_intf_reporting && wpa_s->coloc_intf_elems &&
1922 wpa_s->coloc_intf_dialog_token &&
1923 (wpa_s->coloc_intf_auto_report == 1 ||
1924 wpa_s->coloc_intf_auto_report == 3)) {
1925 /* TODO: Check that there has not been less than
1926 * wpa_s->coloc_intf_timeout * 200 TU from the last report.
1927 */
1928 wnm_send_coloc_intf_report(wpa_s,
1929 wpa_s->coloc_intf_dialog_token,
1930 wpa_s->coloc_intf_elems);
1931 }
1932 }
1933
1934
1935 void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s)
1936 {
1937 #ifdef CONFIG_WNM
1938 wpa_s->coloc_intf_dialog_token = 0;
1939 wpa_s->coloc_intf_auto_report = 0;
1940 #endif /* CONFIG_WNM */
1941 }