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 "rsn_supp/wpa.h"
15 #include "wpa_supplicant_i.h"
16 #include "drivers/driver.h"
17 #include "scan.h"
18 #include "bss.h"
19 #include "wnm_sta.h"
20
21 #define MAX_TFS_IE_LEN 1024
22 #define WNM_MAX_NEIGHBOR_REPORT 10
23
24 #define WNM_SCAN_RESULT_AGE 2 /* 2 seconds */
25
wnm_deallocate_memory(struct wpa_supplicant * wpa_s)26 void wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
27 {
28 int i;
29
30 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
31 os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
32 os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
33 }
34
35 wpa_s->wnm_num_neighbor_report = 0;
36 os_free(wpa_s->wnm_neighbor_report_elements);
37 wpa_s->wnm_neighbor_report_elements = NULL;
38 }
39
40
wnm_parse_neighbor_report_elem(struct neighbor_report * rep,u8 id,u8 elen,const u8 * pos)41 static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
42 u8 id, u8 elen, const u8 *pos)
43 {
44 switch (id) {
45 case WNM_NEIGHBOR_TSF:
46 if (elen < 2 + 2) {
47 wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
48 break;
49 }
50 rep->tsf_offset = WPA_GET_LE16(pos);
51 rep->beacon_int = WPA_GET_LE16(pos + 2);
52 rep->tsf_present = 1;
53 break;
54 case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
55 if (elen < 2) {
56 wpa_printf(MSG_DEBUG, "WNM: Too short condensed "
57 "country string");
58 break;
59 }
60 os_memcpy(rep->country, pos, 2);
61 rep->country_present = 1;
62 break;
63 case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
64 if (elen < 1) {
65 wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition "
66 "candidate");
67 break;
68 }
69 rep->preference = pos[0];
70 rep->preference_present = 1;
71 break;
72 case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
73 if (elen < 10) {
74 wpa_printf(MSG_DEBUG,
75 "WNM: Too short BSS termination duration");
76 break;
77 }
78 rep->bss_term_tsf = WPA_GET_LE64(pos);
79 rep->bss_term_dur = WPA_GET_LE16(pos + 8);
80 rep->bss_term_present = 1;
81 break;
82 case WNM_NEIGHBOR_BEARING:
83 if (elen < 8) {
84 wpa_printf(MSG_DEBUG, "WNM: Too short neighbor "
85 "bearing");
86 break;
87 }
88 rep->bearing = WPA_GET_LE16(pos);
89 rep->distance = WPA_GET_LE32(pos + 2);
90 rep->rel_height = WPA_GET_LE16(pos + 2 + 4);
91 rep->bearing_present = 1;
92 break;
93 case WNM_NEIGHBOR_MEASUREMENT_PILOT:
94 if (elen < 1) {
95 wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
96 "pilot");
97 break;
98 }
99 os_free(rep->meas_pilot);
100 rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
101 if (rep->meas_pilot == NULL)
102 break;
103 rep->meas_pilot->measurement_pilot = pos[0];
104 rep->meas_pilot->subelem_len = elen - 1;
105 os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1);
106 break;
107 case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
108 if (elen < 5) {
109 wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
110 "capabilities");
111 break;
112 }
113 os_memcpy(rep->rm_capab, pos, 5);
114 rep->rm_capab_present = 1;
115 break;
116 case WNM_NEIGHBOR_MULTIPLE_BSSID:
117 if (elen < 1) {
118 wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
119 break;
120 }
121 os_free(rep->mul_bssid);
122 rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
123 if (rep->mul_bssid == NULL)
124 break;
125 rep->mul_bssid->max_bssid_indicator = pos[0];
126 rep->mul_bssid->subelem_len = elen - 1;
127 os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
128 break;
129 }
130 }
131
wnm_parse_neighbor_report(struct wpa_supplicant * wpa_s,const u8 * pos,u8 len,struct neighbor_report * rep)132 static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
133 const u8 *pos, u8 len,
134 struct neighbor_report *rep)
135 {
136 u8 left = len;
137
138 if (left < 13) {
139 wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report");
140 return;
141 }
142
143 os_memcpy(rep->bssid, pos, ETH_ALEN);
144 rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN);
145 rep->regulatory_class = *(pos + 10);
146 rep->channel_number = *(pos + 11);
147 rep->phy_type = *(pos + 12);
148
149 pos += 13;
150 left -= 13;
151
152 while (left >= 2) {
153 u8 id, elen;
154
155 id = *pos++;
156 elen = *pos++;
157 wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen);
158 left -= 2;
159 if (elen > left) {
160 wpa_printf(MSG_DEBUG,
161 "WNM: Truncated neighbor report subelement");
162 break;
163 }
164 wnm_parse_neighbor_report_elem(rep, id, elen, pos);
165 left -= elen;
166 pos += elen;
167 }
168 }
169
170
wnm_clear_acceptable(struct wpa_supplicant * wpa_s)171 static void wnm_clear_acceptable(struct wpa_supplicant *wpa_s)
172 {
173 unsigned int i;
174
175 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++)
176 wpa_s->wnm_neighbor_report_elements[i].acceptable = 0;
177 }
178
179
get_first_acceptable(struct wpa_supplicant * wpa_s)180 static struct wpa_bss * get_first_acceptable(struct wpa_supplicant *wpa_s)
181 {
182 unsigned int i;
183 struct neighbor_report *nei;
184
185 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
186 nei = &wpa_s->wnm_neighbor_report_elements[i];
187 if (nei->acceptable)
188 return wpa_bss_get_bssid(wpa_s, nei->bssid);
189 }
190
191 return NULL;
192 }
193
194
195 #ifdef CONFIG_MBO
196 static struct wpa_bss *
get_mbo_transition_candidate(struct wpa_supplicant * wpa_s,enum mbo_transition_reject_reason * reason)197 get_mbo_transition_candidate(struct wpa_supplicant *wpa_s,
198 enum mbo_transition_reject_reason *reason)
199 {
200 struct wpa_bss *target = NULL;
201 struct wpa_bss_trans_info params;
202 struct wpa_bss_candidate_info *info = NULL;
203 struct neighbor_report *nei = wpa_s->wnm_neighbor_report_elements;
204 u8 *first_candidate_bssid = NULL, *pos;
205 unsigned int i;
206
207 params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason;
208 params.n_candidates = 0;
209 params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN);
210 if (!params.bssid)
211 return NULL;
212
213 pos = params.bssid;
214 for (i = 0; i < wpa_s->wnm_num_neighbor_report; nei++, i++) {
215 if (nei->is_first)
216 first_candidate_bssid = nei->bssid;
217 if (!nei->acceptable)
218 continue;
219 os_memcpy(pos, nei->bssid, ETH_ALEN);
220 pos += ETH_ALEN;
221 params.n_candidates++;
222 }
223
224 if (!params.n_candidates)
225 goto end;
226
227 #ifndef ESP_SUPPLICANT
228 info = wpa_drv_get_bss_trans_status(wpa_s, ¶ms);
229 #endif
230 if (!info) {
231 /* If failed to get candidate BSS transition status from driver,
232 * get the first acceptable candidate from wpa_supplicant.
233 */
234 target = wpa_bss_get_bssid(wpa_s, params.bssid);
235 goto end;
236 }
237
238 /* Get the first acceptable candidate from driver */
239 for (i = 0; i < info->num; i++) {
240 if (info->candidates[i].is_accept) {
241 target = wpa_bss_get_bssid(wpa_s,
242 info->candidates[i].bssid);
243 goto end;
244 }
245 }
246
247 /* If Disassociation Imminent is set and driver rejects all the
248 * candidate select first acceptable candidate which has
249 * rssi > disassoc_imminent_rssi_threshold
250 */
251 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
252 for (i = 0; i < info->num; i++) {
253 target = wpa_bss_get_bssid(wpa_s,
254 info->candidates[i].bssid);
255 #ifndef ESP_SUPPLICANT
256 if (target &&
257 (target->level <
258 wpa_s->conf->disassoc_imminent_rssi_threshold))
259 continue;
260 #else
261 if (target)
262 continue;
263 #endif
264 goto end;
265 }
266 }
267
268 /* While sending BTM reject use reason code of the first candidate
269 * received in BTM request frame
270 */
271 if (reason) {
272 for (i = 0; i < info->num; i++) {
273 if (first_candidate_bssid &&
274 os_memcmp(first_candidate_bssid,
275 info->candidates[i].bssid, ETH_ALEN) == 0)
276 {
277 *reason = info->candidates[i].reject_reason;
278 break;
279 }
280 }
281 }
282
283 target = NULL;
284
285 end:
286 os_free(params.bssid);
287 if (info) {
288 os_free(info->candidates);
289 os_free(info);
290 }
291 return target;
292 }
293 #endif /* CONFIG_MBO */
294
295
296
297 /* basic function to match candidate profile with current bss */
wpa_scan_res_match(struct wpa_supplicant * wpa_s,struct wpa_bss * current_bss,struct wpa_bss * target_bss)298 bool wpa_scan_res_match(struct wpa_supplicant *wpa_s,
299 struct wpa_bss *current_bss,
300 struct wpa_bss *target_bss)
301 {
302 if (current_bss->ssid_len != target_bss->ssid_len) {
303 wpa_printf(MSG_DEBUG, "WNM: ssid didn't match");
304 return false;
305 }
306 if (os_memcmp(current_bss->ssid, target_bss->ssid, current_bss->ssid_len) != 0) {
307 wpa_printf(MSG_DEBUG, "WNM: ssid didn't match");
308 return false;
309 }
310
311 /* Just check for Open/secure mode */
312 if ((current_bss->caps & WLAN_CAPABILITY_PRIVACY) != (target_bss->caps & WLAN_CAPABILITY_PRIVACY)) {
313 wpa_printf(MSG_DEBUG, "WNM: Security didn't match");
314 return false;
315 }
316
317 return true;
318 }
319
320 static struct wpa_bss *
compare_scan_neighbor_results(struct wpa_supplicant * wpa_s,os_time_t age_secs,enum mbo_transition_reject_reason * reason)321 compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs,
322 enum mbo_transition_reject_reason *reason)
323 {
324 u8 i;
325 struct wpa_bss *bss = wpa_s->current_bss;
326 struct wpa_bss *target;
327
328 if (!bss)
329 return NULL;
330
331 wnm_clear_acceptable(wpa_s);
332
333 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
334 struct neighbor_report *nei;
335
336 nei = &wpa_s->wnm_neighbor_report_elements[i];
337 if (nei->preference_present && nei->preference == 0) {
338 wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR,
339 MAC2STR(nei->bssid));
340 continue;
341 }
342
343 target = wpa_bss_get_bssid(wpa_s, nei->bssid);
344 if (!target) {
345 wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
346 " (pref %d) not found in scan results",
347 MAC2STR(nei->bssid),
348 nei->preference_present ? nei->preference :
349 -1);
350 continue;
351 }
352
353 if (age_secs) {
354 struct os_reltime now;
355
356 if (os_get_reltime(&now) == 0 &&
357 os_reltime_expired(&now, &target->last_update,
358 age_secs)) {
359 wpa_printf(MSG_DEBUG,
360 "Candidate BSS is more than %jd seconds old",
361 (intmax_t)age_secs);
362 continue;
363 }
364 }
365
366 if (bss->ssid_len != target->ssid_len ||
367 os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
368 /*
369 * TODO: Could consider allowing transition to another
370 * ESS if PMF was enabled for the association.
371 */
372 wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
373 " (pref %d) in different ESS",
374 MAC2STR(nei->bssid),
375 nei->preference_present ? nei->preference :
376 -1);
377 continue;
378 }
379
380 if (wpa_s->current_bss &&
381 !wpa_scan_res_match(wpa_s, wpa_s->current_bss, target)) {
382 wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
383 " (pref %d) does not match the current network profile",
384 MAC2STR(nei->bssid),
385 nei->preference_present ? nei->preference :
386 -1);
387 continue;
388 }
389
390 if (target->level < bss->level && target->level < -80) {
391 wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
392 " (pref %d) does not have sufficient signal level (%d)",
393 MAC2STR(nei->bssid),
394 nei->preference_present ? nei->preference :
395 -1,
396 target->level);
397 continue;
398 }
399
400 nei->acceptable = 1;
401 }
402
403 #ifdef CONFIG_MBO
404 if (wpa_s->wnm_mbo_trans_reason_present)
405 target = get_mbo_transition_candidate(wpa_s, reason);
406 else
407 target = get_first_acceptable(wpa_s);
408 #else /* CONFIG_MBO */
409 target = get_first_acceptable(wpa_s);
410 #endif /* CONFIG_MBO */
411
412 if (target) {
413 wpa_printf(MSG_DEBUG,
414 "WNM: Found an acceptable preferred transition candidate BSS "
415 MACSTR " (RSSI %d)",
416 MAC2STR(target->bssid), target->level);
417 }
418
419 return target;
420 }
421
422
wpa_bss_ies_eq(struct wpa_bss * a,struct wpa_bss * b,u8 eid)423 static int wpa_bss_ies_eq(struct wpa_bss *a, struct wpa_bss *b, u8 eid)
424 {
425 const u8 *ie_a, *ie_b;
426
427 if (!a || !b)
428 return 0;
429
430 ie_a = wpa_bss_get_ie(a, eid);
431 ie_b = wpa_bss_get_ie(b, eid);
432
433 if (!ie_a || !ie_b || ie_a[1] != ie_b[1])
434 return 0;
435
436 return os_memcmp(ie_a, ie_b, ie_a[1]) == 0;
437 }
438
439
wnm_get_bss_info(struct wpa_supplicant * wpa_s,struct wpa_bss * bss)440 static u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
441 {
442 u32 info = 0;
443
444 info |= NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH;
445
446 /*
447 * Leave the security and key scope bits unset to indicate that the
448 * security information is not available.
449 */
450
451 if (bss->caps & WLAN_CAPABILITY_SPECTRUM_MGMT)
452 info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
453 if (bss->caps & WLAN_CAPABILITY_QOS)
454 info |= NEI_REP_BSSID_INFO_QOS;
455 if (bss->caps & WLAN_CAPABILITY_APSD)
456 info |= NEI_REP_BSSID_INFO_APSD;
457 if (bss->caps & WLAN_CAPABILITY_RADIO_MEASUREMENT)
458 info |= NEI_REP_BSSID_INFO_RM;
459 if (bss->caps & WLAN_CAPABILITY_DELAYED_BLOCK_ACK)
460 info |= NEI_REP_BSSID_INFO_DELAYED_BA;
461 if (bss->caps & WLAN_CAPABILITY_IMM_BLOCK_ACK)
462 info |= NEI_REP_BSSID_INFO_IMM_BA;
463 if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_MOBILITY_DOMAIN))
464 info |= NEI_REP_BSSID_INFO_MOBILITY_DOMAIN;
465 if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_HT_CAP))
466 info |= NEI_REP_BSSID_INFO_HT;
467
468 return info;
469 }
470
471
wnm_add_nei_rep(struct wpabuf ** buf,const u8 * bssid,u32 bss_info,u8 op_class,u8 chan,u8 phy_type,u8 pref)472 static int wnm_add_nei_rep(struct wpabuf **buf, const u8 *bssid,
473 u32 bss_info, u8 op_class, u8 chan, u8 phy_type,
474 u8 pref)
475 {
476 if (wpabuf_len(*buf) + 18 >
477 IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN) {
478 wpa_printf(MSG_DEBUG,
479 "WNM: No room in frame for Neighbor Report element");
480 return -1;
481 }
482
483 if (wpabuf_resize(buf, 18) < 0) {
484 wpa_printf(MSG_DEBUG,
485 "WNM: Failed to allocate memory for Neighbor Report element");
486 return -1;
487 }
488
489 wpabuf_put_u8(*buf, WLAN_EID_NEIGHBOR_REPORT);
490 /* length: 13 for basic neighbor report + 3 for preference subelement */
491 wpabuf_put_u8(*buf, 16);
492 wpabuf_put_data(*buf, bssid, ETH_ALEN);
493 wpabuf_put_le32(*buf, bss_info);
494 wpabuf_put_u8(*buf, op_class);
495 wpabuf_put_u8(*buf, chan);
496 wpabuf_put_u8(*buf, phy_type);
497 wpabuf_put_u8(*buf, WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE);
498 wpabuf_put_u8(*buf, 1);
499 wpabuf_put_u8(*buf, pref);
500 return 0;
501 }
502
wnm_nei_rep_add_bss(struct wpa_supplicant * wpa_s,struct wpa_bss * bss,struct wpabuf ** buf,u8 pref)503 static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s,
504 struct wpa_bss *bss, struct wpabuf **buf,
505 u8 pref)
506 {
507 const u8 *ie;
508 u8 op_class;
509 int sec_chan = 0;
510 enum phy_type phy_type;
511 u32 info;
512 struct ieee80211_ht_operation *ht_oper = NULL;
513
514 ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
515 if (ie && ie[1] >= 2) {
516 ht_oper = (struct ieee80211_ht_operation *) (ie + 2);
517
518 if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
519 sec_chan = 1;
520 else if (ht_oper->ht_param &
521 HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
522 sec_chan = -1;
523 }
524
525 op_class = get_operating_class(bss->channel, sec_chan);
526
527 phy_type = (sec_chan != 0) ? PHY_TYPE_HT : PHY_TYPE_ERP;
528
529 info = wnm_get_bss_info(wpa_s, bss);
530
531 return wnm_add_nei_rep(buf, bss->bssid, info, op_class, bss->channel, phy_type,
532 pref);
533 }
534
535
wnm_add_cand_list(struct wpa_supplicant * wpa_s,struct wpabuf ** buf)536 static void wnm_add_cand_list(struct wpa_supplicant *wpa_s, struct wpabuf **buf)
537 {
538 unsigned int i, pref = 255;
539 struct os_reltime now;
540
541 if (!wpa_s->current_bss)
542 return;
543
544 /*
545 * TODO: Define when scan results are no longer valid for the candidate
546 * list.
547 */
548 os_get_reltime(&now);
549 if (os_reltime_expired(&now, &wpa_s->last_scan, 10))
550 return;
551
552 wpa_printf(MSG_DEBUG,
553 "WNM: Add candidate list to BSS Transition Management Response frame");
554 for (i = 0; i < wpa_s->last_scan_res_used && pref; i++) {
555 struct wpa_bss *bss = wpa_s->last_scan_res[i];
556 int res;
557
558 if (wpa_scan_res_match(wpa_s, wpa_s->current_bss, bss)) {
559 res = wnm_nei_rep_add_bss(wpa_s, bss, buf, pref--);
560 if (res == -2)
561 continue; /* could not build entry for BSS */
562 if (res < 0)
563 break; /* no more room for candidates */
564 if (pref == 1)
565 break;
566 }
567 }
568
569 wpa_hexdump_buf(MSG_DEBUG,
570 "WNM: BSS Transition Management Response candidate list",
571 *buf);
572 }
573
574
575 #define BTM_RESP_MIN_SIZE 5 + ETH_ALEN
576
wnm_send_bss_transition_mgmt_resp(struct wpa_supplicant * wpa_s,u8 dialog_token,enum bss_trans_mgmt_status_code status,enum mbo_transition_reject_reason reason,u8 delay,const u8 * target_bssid)577 static void wnm_send_bss_transition_mgmt_resp(
578 struct wpa_supplicant *wpa_s, u8 dialog_token,
579 enum bss_trans_mgmt_status_code status,
580 enum mbo_transition_reject_reason reason,
581 u8 delay, const u8 *target_bssid)
582 {
583 struct wpabuf *buf;
584 int res;
585
586 if (!wpa_s->current_bss) {
587 wpa_printf(MSG_DEBUG,
588 "WNM: Current BSS not known - drop response");
589 return;
590 }
591 wpa_printf(MSG_DEBUG,
592 "WNM: Send BSS Transition Management Response to " MACSTR
593 " dialog_token=%u status=%u reason=%u delay=%d",
594 MAC2STR(wpa_s->current_bss->bssid), dialog_token, status, reason, delay);
595
596 buf = wpabuf_alloc(BTM_RESP_MIN_SIZE);
597 if (!buf) {
598 wpa_printf(MSG_DEBUG,
599 "WNM: Failed to allocate memory for BTM response");
600 return;
601 }
602
603 wpabuf_put_u8(buf, WLAN_ACTION_WNM);
604 wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP);
605 wpabuf_put_u8(buf, dialog_token);
606 wpabuf_put_u8(buf, status);
607 wpabuf_put_u8(buf, delay);
608 if (target_bssid) {
609 wpabuf_put_data(buf, target_bssid, ETH_ALEN);
610 } else if (status == WNM_BSS_TM_ACCEPT) {
611 /*
612 * P802.11-REVmc clarifies that the Target BSSID field is always
613 * present when status code is zero, so use a fake value here if
614 * no BSSID is yet known.
615 */
616 wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN);
617 }
618
619 if (status == WNM_BSS_TM_ACCEPT)
620 wnm_add_cand_list(wpa_s, &buf);
621
622 #ifdef CONFIG_MBO
623 if (status != WNM_BSS_TM_ACCEPT &&
624 wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) {
625 u8 mbo[10];
626 size_t ret;
627
628 ret = wpas_mbo_ie_bss_trans_reject(wpa_s, mbo, sizeof(mbo),
629 reason);
630 if (ret) {
631 if (wpabuf_resize(&buf, ret) < 0) {
632 wpabuf_free(buf);
633 wpa_printf(MSG_DEBUG,
634 "WNM: Failed to allocate memory for MBO IE");
635 return;
636 }
637
638 wpabuf_put_data(buf, mbo, ret);
639 }
640 }
641 #endif /* CONFIG_MBO */
642
643 res = wpa_drv_send_action(wpa_s, 0, 0,
644 wpabuf_head_u8(buf), wpabuf_len(buf), 0);
645 if (res < 0) {
646 wpa_printf(MSG_DEBUG,
647 "WNM: Failed to send BSS Transition Management Response");
648 }
649
650 wpabuf_free(buf);
651 }
652
wnm_bss_tm_connect(struct wpa_supplicant * wpa_s,struct wpa_bss * bss,char * ssid,int after_new_scan)653 void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s,
654 struct wpa_bss *bss, char *ssid,
655 int after_new_scan)
656 {
657 wpa_printf(MSG_DEBUG,
658 "WNM: Transition to BSS " MACSTR
659 " based on BSS Transition Management Request after_new_scan=%d)",
660 MAC2STR(bss->bssid), after_new_scan);
661
662 /* Send the BSS Management Response - Accept */
663 if (wpa_s->wnm_reply) {
664 wpa_s->wnm_reply = 0;
665 wpa_printf(MSG_DEBUG,
666 "WNM: Sending successful BSS Transition Management Response");
667 wnm_send_bss_transition_mgmt_resp(
668 wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT,
669 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
670 bss->bssid);
671 }
672
673 if (bss == wpa_s->current_bss) {
674 wpa_printf(MSG_DEBUG,
675 "WNM: Already associated with the preferred candidate");
676 wnm_deallocate_memory(wpa_s);
677 return;
678 }
679
680 wpa_printf(MSG_DEBUG, "WNM: Issuing connect");
681
682 wpa_supplicant_connect(wpa_s, bss, ssid);
683 wnm_deallocate_memory(wpa_s);
684 }
685
686
wnm_scan_process(struct wpa_supplicant * wpa_s,int reply_on_fail)687 int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
688 {
689 struct wpa_bss *bss;
690 enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
691 enum mbo_transition_reject_reason reason =
692 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED;
693
694 if (!wpa_s->wnm_neighbor_report_elements) {
695 wpa_printf(MSG_INFO, "WNM: Neighbor report not available");
696 return 0;
697 }
698
699 wpa_dbg(wpa_s, MSG_DEBUG,
700 "WNM: Process scan results for BSS Transition Management");
701 if (os_reltime_before(&wpa_s->wnm_cand_valid_until,
702 &wpa_s->scan_trigger_time)) {
703 wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
704 wnm_deallocate_memory(wpa_s);
705 return 0;
706 }
707
708 /* Compare the Neighbor Report and scan results */
709 bss = compare_scan_neighbor_results(wpa_s, 0, &reason);
710 if (!bss) {
711 wpa_printf(MSG_INFO, "WNM: No BSS transition candidate match found");
712 status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
713 goto send_bss_resp_fail;
714 }
715
716 /* Associate to the network */
717 wnm_bss_tm_connect(wpa_s, bss, NULL, 1);
718 return 1;
719
720 send_bss_resp_fail:
721 if (!reply_on_fail)
722 return 0;
723
724 /* Send reject response for all the failures */
725
726 if (wpa_s->wnm_reply) {
727 wpa_s->wnm_reply = 0;
728 wnm_send_bss_transition_mgmt_resp(wpa_s,
729 wpa_s->wnm_dialog_token,
730 status, reason, 0, NULL);
731 }
732 wnm_deallocate_memory(wpa_s);
733
734 return 0;
735 }
736
737
cand_pref_compar(const void * a,const void * b)738 static int cand_pref_compar(const void *a, const void *b)
739 {
740 const struct neighbor_report *aa = a;
741 const struct neighbor_report *bb = b;
742
743 if (!aa->preference_present && !bb->preference_present)
744 return 0;
745 if (!aa->preference_present)
746 return 1;
747 if (!bb->preference_present)
748 return -1;
749 if (bb->preference > aa->preference)
750 return 1;
751 if (bb->preference < aa->preference)
752 return -1;
753 return 0;
754 }
755
756
wnm_sort_cand_list(struct wpa_supplicant * wpa_s)757 static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s)
758 {
759 if (!wpa_s->wnm_neighbor_report_elements)
760 return;
761 qsort(wpa_s->wnm_neighbor_report_elements,
762 wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report),
763 cand_pref_compar);
764 }
765
766
wnm_dump_cand_list(struct wpa_supplicant * wpa_s)767 static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s)
768 {
769 #ifdef DEBUG_PRINT
770 unsigned int i;
771
772 wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List");
773 if (!wpa_s->wnm_neighbor_report_elements)
774 return;
775 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
776 struct neighbor_report *nei;
777
778 nei = &wpa_s->wnm_neighbor_report_elements[i];
779 wpa_printf(MSG_DEBUG, "%u: " MACSTR
780 " info=0x%x op_class=%u chan=%u phy=%u pref=%d",
781 i, MAC2STR(nei->bssid), nei->bssid_info,
782 nei->regulatory_class,
783 nei->channel_number, nei->phy_type,
784 nei->preference_present ? nei->preference : -1);
785 }
786 #endif
787 }
788
wnm_set_scan_freqs(struct wpa_supplicant * wpa_s)789 static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s)
790 {
791 unsigned int i;
792 int num_chan = 0;
793 u8 chan = 0;
794
795 wpa_s->next_scan_chan = 0;
796 if (!wpa_s->wnm_neighbor_report_elements)
797 return;
798
799 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
800 struct neighbor_report *nei;
801
802 nei = &wpa_s->wnm_neighbor_report_elements[i];
803 if (nei->channel_number <= 0) {
804 wpa_printf(MSG_DEBUG,
805 "WNM: Unknown neighbor operating channel_number for "
806 MACSTR " - scan all channels",
807 MAC2STR(nei->bssid));
808 return;
809 }
810 if (nei->channel_number != chan) {
811 chan = nei->channel_number;
812 num_chan++;
813 }
814 }
815
816 if (num_chan == 1) {
817 wpa_s->next_scan_chan = chan;
818 }
819 }
820
wnm_fetch_scan_results(struct wpa_supplicant * wpa_s)821 static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s)
822 {
823 /* ESP doesn't support this */
824 return 0;
825 }
826
ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant * wpa_s,const u8 * pos,const u8 * end,int reply)827 static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
828 const u8 *pos, const u8 *end,
829 int reply)
830 {
831 unsigned int beacon_int;
832 u8 valid_int;
833 #ifdef CONFIG_MBO
834 const u8 *vendor;
835 #endif /* CONFIG_MBO */
836 #ifdef ESP_SUPPLICANT
837 bool scan_required = false;
838 #endif
839
840 if (wpa_s->disable_mbo_oce || wpa_s->disable_btm)
841 return;
842
843 if (end - pos < 5)
844 return;
845
846 #ifdef CONFIG_MBO
847 wpa_s->wnm_mbo_trans_reason_present = 0;
848 wpa_s->wnm_mbo_transition_reason = 0;
849 #endif /* CONFIG_MBO */
850
851 if (wpa_s->current_bss)
852 beacon_int = wpa_s->current_bss->beacon_int;
853 else
854 beacon_int = 100; /* best guess */
855
856 wpa_s->wnm_dialog_token = pos[0];
857 wpa_s->wnm_mode = pos[1];
858 wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
859 valid_int = pos[4];
860 wpa_s->wnm_reply = reply;
861
862 wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
863 "dialog_token=%u request_mode=0x%x "
864 "disassoc_timer=%u validity_interval=%u",
865 wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
866 wpa_s->wnm_dissoc_timer, valid_int);
867
868 #if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS)
869 if (wpa_s->reject_btm_req_reason) {
870 wpa_printf(MSG_INFO,
871 "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d",
872 wpa_s->reject_btm_req_reason);
873 wnm_send_bss_transition_mgmt_resp(
874 wpa_s, wpa_s->wnm_dialog_token,
875 wpa_s->reject_btm_req_reason,
876 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
877 return;
878 }
879 #endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */
880
881 pos += 5;
882
883 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
884 if (end - pos < 12) {
885 wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
886 return;
887 }
888 os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
889 pos += 12; /* BSS Termination Duration */
890 }
891
892 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
893 char url[256];
894
895 if (end - pos < 1 || 1 + pos[0] > end - pos) {
896 wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
897 "Management Request (URL)");
898 return;
899 }
900 os_memcpy(url, pos + 1, pos[0]);
901 url[pos[0]] = '\0';
902 pos += 1 + pos[0];
903
904 wpa_msg(wpa_s, MSG_DEBUG, "ESS_DISASSOC_IMMINENT %u %s",
905 wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url);
906 }
907
908 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
909 wpa_msg(wpa_s, MSG_DEBUG, "WNM: Disassociation Imminent - "
910 "Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
911 if (wpa_s->wnm_dissoc_timer) {
912 /* TODO: mark current BSS less preferred for
913 * selection */
914 #ifdef ESP_SUPPLICANT
915 os_memset(wpa_s->next_scan_bssid, 0, ETH_ALEN);
916 wpa_s->next_scan_chan = 0;
917 scan_required = true;
918 #else
919 wpa_printf(MSG_DEBUG, "Trying to find another BSS");
920 wpa_supplicant_req_scan(wpa_s, 0, 0);
921 #endif
922 }
923 }
924
925 #ifdef CONFIG_MBO
926 vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC);
927 if (vendor)
928 wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]);
929 #endif /* CONFIG_MBO */
930
931 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
932 unsigned int valid_ms;
933
934 wpa_msg(wpa_s, MSG_DEBUG, "WNM: Preferred List Available");
935 wnm_deallocate_memory(wpa_s);
936 wpa_s->wnm_neighbor_report_elements = os_calloc(
937 WNM_MAX_NEIGHBOR_REPORT,
938 sizeof(struct neighbor_report));
939 if (wpa_s->wnm_neighbor_report_elements == NULL)
940 return;
941
942 while (end - pos >= 2 &&
943 wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT)
944 {
945 u8 tag = *pos++;
946 u8 len = *pos++;
947
948 wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u",
949 tag);
950 if (len > end - pos) {
951 wpa_printf(MSG_DEBUG, "WNM: Truncated request");
952 return;
953 }
954 if (tag == WLAN_EID_NEIGHBOR_REPORT) {
955 struct neighbor_report *rep;
956 rep = &wpa_s->wnm_neighbor_report_elements[
957 wpa_s->wnm_num_neighbor_report];
958 wnm_parse_neighbor_report(wpa_s, pos, len, rep);
959 wpa_s->wnm_num_neighbor_report++;
960 #ifdef CONFIG_MBO
961 if (wpa_s->wnm_mbo_trans_reason_present &&
962 wpa_s->wnm_num_neighbor_report == 1) {
963 rep->is_first = 1;
964 wpa_printf(MSG_DEBUG,
965 "WNM: First transition candidate is "
966 MACSTR, MAC2STR(rep->bssid));
967 }
968 #endif /* CONFIG_MBO */
969 }
970
971 pos += len;
972 }
973
974 if (!wpa_s->wnm_num_neighbor_report) {
975 wpa_printf(MSG_DEBUG,
976 "WNM: Candidate list included bit is set, but no candidates found");
977 wnm_send_bss_transition_mgmt_resp(
978 wpa_s, wpa_s->wnm_dialog_token,
979 WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES,
980 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0,
981 NULL);
982 return;
983 }
984
985 wnm_sort_cand_list(wpa_s);
986 wnm_dump_cand_list(wpa_s);
987 valid_ms = valid_int * beacon_int * 128 / 125;
988 wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms",
989 valid_ms);
990 os_get_reltime(&wpa_s->wnm_cand_valid_until);
991 wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000;
992 wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000;
993 wpa_s->wnm_cand_valid_until.sec +=
994 wpa_s->wnm_cand_valid_until.usec / 1000000;
995 wpa_s->wnm_cand_valid_until.usec %= 1000000;
996
997 /*
998 * Fetch the latest scan results from the kernel and check for
999 * candidates based on those results first. This can help in
1000 * finding more up-to-date information should the driver has
1001 * done some internal scanning operations after the last scan
1002 * result update in wpa_supplicant.
1003 */
1004 if (wnm_fetch_scan_results(wpa_s) > 0)
1005 return;
1006
1007 /*
1008 * Try to use previously received scan results, if they are
1009 * recent enough to use for a connection.
1010 */
1011 if (wpa_s->last_scan_res_used > 0) {
1012 struct os_reltime now;
1013
1014 os_get_reltime(&now);
1015 if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) {
1016 wpa_printf(MSG_DEBUG,
1017 "WNM: Try to use recent scan results");
1018 if (wnm_scan_process(wpa_s, 0) > 0)
1019 return;
1020 wpa_printf(MSG_DEBUG,
1021 "WNM: No match in previous scan results - try a new scan");
1022 }
1023 }
1024 wnm_set_scan_freqs(wpa_s);
1025 if (wpa_s->wnm_num_neighbor_report == 1) {
1026 os_memcpy(wpa_s->next_scan_bssid,
1027 wpa_s->wnm_neighbor_report_elements[0].bssid,
1028 ETH_ALEN);
1029 wpa_printf(MSG_DEBUG,
1030 "WNM: Scan only for a specific BSSID since there is only a single candidate "
1031 MACSTR, MAC2STR(wpa_s->next_scan_bssid));
1032 }
1033 #ifdef ESP_SUPPLICANT
1034 scan_required = true;
1035 }
1036 if (scan_required) {
1037 wpa_printf(MSG_DEBUG, "Trying to find another BSS");
1038 #endif
1039 wpa_supplicant_req_scan(wpa_s, 0, 0);
1040 } else if (reply) {
1041 enum bss_trans_mgmt_status_code status;
1042 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT)
1043 status = WNM_BSS_TM_ACCEPT;
1044 else {
1045 wpa_msg(wpa_s, MSG_DEBUG, "WNM: BSS Transition Management Request did not include candidates");
1046 status = WNM_BSS_TM_REJECT_UNSPECIFIED;
1047 }
1048 wpa_s->wnm_reply = 0;
1049 wnm_send_bss_transition_mgmt_resp(
1050 wpa_s, wpa_s->wnm_dialog_token, status,
1051 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL);
1052 }
1053 }
1054
1055
1056 #define BTM_QUERY_MIN_SIZE 4
1057
wnm_send_bss_transition_mgmt_query(struct wpa_supplicant * wpa_s,u8 query_reason,const char * btm_candidates,int cand_list)1058 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
1059 u8 query_reason,
1060 const char *btm_candidates,
1061 int cand_list)
1062 {
1063 struct wpabuf *buf;
1064 int ret;
1065
1066 wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
1067 MACSTR " query_reason=%u%s",
1068 MAC2STR(wpa_s->current_bss->bssid), query_reason,
1069 cand_list ? " candidate list" : "");
1070
1071 buf = wpabuf_alloc(BTM_QUERY_MIN_SIZE);
1072 if (!buf)
1073 return -1;
1074
1075 wpabuf_put_u8(buf, WLAN_ACTION_WNM);
1076 wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_QUERY);
1077 wpabuf_put_u8(buf, 1);
1078 wpabuf_put_u8(buf, query_reason);
1079
1080 if (cand_list)
1081 wnm_add_cand_list(wpa_s, &buf);
1082
1083 if (btm_candidates) {
1084 const size_t max_len = 1000;
1085
1086 ret = wpabuf_resize(&buf, max_len);
1087 if (ret < 0) {
1088 wpabuf_free(buf);
1089 return ret;
1090 }
1091
1092 ret = ieee802_11_parse_candidate_list(btm_candidates,
1093 wpabuf_put(buf, 0),
1094 max_len);
1095 if (ret < 0) {
1096 wpabuf_free(buf);
1097 return ret;
1098 }
1099
1100 wpabuf_put(buf, ret);
1101 }
1102
1103 ret = wpa_drv_send_action(wpa_s, 0, 0,
1104 wpabuf_head_u8(buf), wpabuf_len(buf), 0);
1105
1106 wpabuf_free(buf);
1107 return ret;
1108 }
1109
ieee802_11_rx_wnm_action(struct wpa_supplicant * wpa_s,u8 * sender,u8 * payload,size_t len)1110 void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
1111 u8 *sender, u8 *payload, size_t len)
1112 {
1113 const u8 *pos, *end;
1114 u8 act;
1115
1116 if (len < 2)
1117 return;
1118
1119 pos = payload;
1120 act = *pos++;
1121 end = payload + len;
1122
1123 wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
1124 act, MAC2STR(sender));
1125
1126 switch (act) {
1127 case WNM_BSS_TRANS_MGMT_REQ:
1128 ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
1129 0x01);
1130 break;
1131 default:
1132 wpa_printf(MSG_ERROR, "WNM: Unknown request");
1133 break;
1134 }
1135 }
1136