3 * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
9 #include "utils/includes.h"
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"
18 #include "wpa_supplicant_i.h"
21 #include "ctrl_iface.h"
25 #include "hs20_supplicant.h"
27 #define MAX_TFS_IE_LEN 1024
28 #define WNM_MAX_NEIGHBOR_REPORT 10
30 #define WNM_SCAN_RESULT_AGE 2 /* 2 seconds */
32 /* get the TFS IE from driver */
33 static int ieee80211_11_get_tfs_ie(struct wpa_supplicant
*wpa_s
, u8
*buf
,
34 u16
*buf_len
, enum wnm_oper oper
)
36 wpa_printf(MSG_DEBUG
, "%s: TFS get operation %d", __func__
, oper
);
38 return wpa_drv_wnm_oper(wpa_s
, oper
, wpa_s
->bssid
, buf
, buf_len
);
42 /* set the TFS IE to driver */
43 static int ieee80211_11_set_tfs_ie(struct wpa_supplicant
*wpa_s
,
44 const u8
*addr
, const u8
*buf
, u16 buf_len
,
49 wpa_printf(MSG_DEBUG
, "%s: TFS set operation %d", __func__
, oper
);
51 return wpa_drv_wnm_oper(wpa_s
, oper
, addr
, (u8
*) buf
, &len
);
55 /* MLME-SLEEPMODE.request */
56 int ieee802_11_send_wnmsleep_req(struct wpa_supplicant
*wpa_s
,
57 u8 action
, u16 intval
, struct wpabuf
*tfs_req
)
59 struct ieee80211_mgmt
*mgmt
;
62 struct wnm_sleep_element
*wnmsleep_ie
;
63 u8
*wnmtfs_ie
, *oci_ie
;
64 u8 wnmsleep_ie_len
, oci_ie_len
;
65 u16 wnmtfs_ie_len
; /* possibly multiple IE(s) */
66 enum wnm_oper tfs_oper
= action
== 0 ? WNM_SLEEP_TFS_REQ_IE_ADD
:
67 WNM_SLEEP_TFS_REQ_IE_NONE
;
69 wpa_printf(MSG_DEBUG
, "WNM: Request to send WNM-Sleep Mode Request "
70 "action=%s to " MACSTR
,
71 action
== 0 ? "enter" : "exit",
72 MAC2STR(wpa_s
->bssid
));
74 /* WNM-Sleep Mode IE */
75 wnmsleep_ie_len
= sizeof(struct wnm_sleep_element
);
76 wnmsleep_ie
= os_zalloc(sizeof(struct wnm_sleep_element
));
77 if (wnmsleep_ie
== NULL
)
79 wnmsleep_ie
->eid
= WLAN_EID_WNMSLEEP
;
80 wnmsleep_ie
->len
= wnmsleep_ie_len
- 2;
81 wnmsleep_ie
->action_type
= action
;
82 wnmsleep_ie
->status
= WNM_STATUS_SLEEP_ACCEPT
;
83 wnmsleep_ie
->intval
= host_to_le16(intval
);
84 wpa_hexdump(MSG_DEBUG
, "WNM: WNM-Sleep Mode element",
85 (u8
*) wnmsleep_ie
, wnmsleep_ie_len
);
89 wnmtfs_ie_len
= wpabuf_len(tfs_req
);
90 wnmtfs_ie
= os_memdup(wpabuf_head(tfs_req
), wnmtfs_ie_len
);
91 if (wnmtfs_ie
== NULL
) {
96 wnmtfs_ie
= os_zalloc(MAX_TFS_IE_LEN
);
97 if (wnmtfs_ie
== NULL
) {
101 if (ieee80211_11_get_tfs_ie(wpa_s
, wnmtfs_ie
, &wnmtfs_ie_len
,
108 wpa_hexdump(MSG_DEBUG
, "WNM: TFS Request element",
109 (u8
*) wnmtfs_ie
, wnmtfs_ie_len
);
114 if (action
== WNM_SLEEP_MODE_EXIT
&& wpa_sm_ocv_enabled(wpa_s
->wpa
)) {
115 struct wpa_channel_info ci
;
117 if (wpa_drv_channel_info(wpa_s
, &ci
) != 0) {
118 wpa_printf(MSG_WARNING
,
119 "Failed to get channel info for OCI element in WNM-Sleep Mode frame");
120 os_free(wnmsleep_ie
);
125 oci_ie_len
= OCV_OCI_EXTENDED_LEN
;
126 oci_ie
= os_zalloc(oci_ie_len
);
128 wpa_printf(MSG_WARNING
,
129 "Failed to allocate buffer for for OCI element in WNM-Sleep Mode frame");
130 os_free(wnmsleep_ie
);
135 if (ocv_insert_extended_oci(&ci
, oci_ie
) < 0) {
136 os_free(wnmsleep_ie
);
142 #endif /* CONFIG_OCV */
144 mgmt
= os_zalloc(sizeof(*mgmt
) + wnmsleep_ie_len
+ wnmtfs_ie_len
+
147 wpa_printf(MSG_DEBUG
, "MLME: Failed to allocate buffer for "
148 "WNM-Sleep Request action frame");
149 os_free(wnmsleep_ie
);
154 os_memcpy(mgmt
->da
, wpa_s
->bssid
, ETH_ALEN
);
155 os_memcpy(mgmt
->sa
, wpa_s
->own_addr
, ETH_ALEN
);
156 os_memcpy(mgmt
->bssid
, wpa_s
->bssid
, ETH_ALEN
);
157 mgmt
->frame_control
= IEEE80211_FC(WLAN_FC_TYPE_MGMT
,
158 WLAN_FC_STYPE_ACTION
);
159 mgmt
->u
.action
.category
= WLAN_ACTION_WNM
;
160 mgmt
->u
.action
.u
.wnm_sleep_req
.action
= WNM_SLEEP_MODE_REQ
;
161 mgmt
->u
.action
.u
.wnm_sleep_req
.dialogtoken
= 1;
162 os_memcpy(mgmt
->u
.action
.u
.wnm_sleep_req
.variable
, wnmsleep_ie
,
164 /* copy TFS IE here */
165 if (wnmtfs_ie_len
> 0) {
166 os_memcpy(mgmt
->u
.action
.u
.wnm_sleep_req
.variable
+
167 wnmsleep_ie_len
, wnmtfs_ie
, wnmtfs_ie_len
);
171 /* copy OCV OCI here */
172 if (oci_ie_len
> 0) {
173 os_memcpy(mgmt
->u
.action
.u
.wnm_sleep_req
.variable
+
174 wnmsleep_ie_len
+ wnmtfs_ie_len
, oci_ie
, oci_ie_len
);
176 #endif /* CONFIG_OCV */
178 len
= 1 + sizeof(mgmt
->u
.action
.u
.wnm_sleep_req
) + wnmsleep_ie_len
+
179 wnmtfs_ie_len
+ oci_ie_len
;
181 res
= wpa_drv_send_action(wpa_s
, wpa_s
->assoc_freq
, 0, wpa_s
->bssid
,
182 wpa_s
->own_addr
, wpa_s
->bssid
,
183 &mgmt
->u
.action
.category
, len
, 0);
185 wpa_printf(MSG_DEBUG
, "Failed to send WNM-Sleep Request "
186 "(action=%d, intval=%d)", action
, intval
);
188 wpa_s
->wnmsleep_used
= 1;
190 os_free(wnmsleep_ie
);
199 static void wnm_sleep_mode_enter_success(struct wpa_supplicant
*wpa_s
,
200 const u8
*tfsresp_ie_start
,
201 const u8
*tfsresp_ie_end
)
203 wpa_drv_wnm_oper(wpa_s
, WNM_SLEEP_ENTER_CONFIRM
,
204 wpa_s
->bssid
, NULL
, NULL
);
205 /* remove GTK/IGTK ?? */
207 /* set the TFS Resp IE(s) */
208 if (tfsresp_ie_start
&& tfsresp_ie_end
&&
209 tfsresp_ie_end
- tfsresp_ie_start
>= 0) {
211 tfsresp_ie_len
= (tfsresp_ie_end
+ tfsresp_ie_end
[1] + 2) -
213 wpa_printf(MSG_DEBUG
, "TFS Resp IE(s) found");
214 /* pass the TFS Resp IE(s) to driver for processing */
215 if (ieee80211_11_set_tfs_ie(wpa_s
, wpa_s
->bssid
,
218 WNM_SLEEP_TFS_RESP_IE_SET
))
219 wpa_printf(MSG_DEBUG
, "WNM: Fail to set TFS Resp IE");
224 static void wnm_sleep_mode_exit_success(struct wpa_supplicant
*wpa_s
,
225 const u8
*frm
, u16 key_len_total
)
230 wpa_drv_wnm_oper(wpa_s
, WNM_SLEEP_EXIT_CONFIRM
, wpa_s
->bssid
,
233 /* Install GTK/IGTK */
235 /* point to key data field */
236 ptr
= (u8
*) frm
+ 1 + 2;
237 end
= ptr
+ key_len_total
;
238 wpa_hexdump_key(MSG_DEBUG
, "WNM: Key Data", ptr
, key_len_total
);
240 if (key_len_total
&& !wpa_sm_pmf_enabled(wpa_s
->wpa
)) {
241 wpa_msg(wpa_s
, MSG_INFO
,
242 "WNM: Ignore Key Data in WNM-Sleep Mode Response - PMF not enabled");
246 while (end
- ptr
> 1) {
247 if (2 + ptr
[1] > end
- ptr
) {
248 wpa_printf(MSG_DEBUG
, "WNM: Invalid Key Data element "
251 wpa_hexdump(MSG_DEBUG
, "WNM: Remaining data",
256 if (*ptr
== WNM_SLEEP_SUBELEM_GTK
) {
257 if (ptr
[1] < 11 + 5) {
258 wpa_printf(MSG_DEBUG
, "WNM: Too short GTK "
262 gtk_len
= *(ptr
+ 4);
263 if (ptr
[1] < 11 + gtk_len
||
264 gtk_len
< 5 || gtk_len
> 32) {
265 wpa_printf(MSG_DEBUG
, "WNM: Invalid GTK "
269 wpa_wnmsleep_install_key(
271 WNM_SLEEP_SUBELEM_GTK
,
274 #ifdef CONFIG_IEEE80211W
275 } else if (*ptr
== WNM_SLEEP_SUBELEM_IGTK
) {
276 if (ptr
[1] < 2 + 6 + WPA_IGTK_LEN
) {
277 wpa_printf(MSG_DEBUG
, "WNM: Too short IGTK "
281 wpa_wnmsleep_install_key(wpa_s
->wpa
,
282 WNM_SLEEP_SUBELEM_IGTK
, ptr
);
283 ptr
+= 10 + WPA_IGTK_LEN
;
284 #endif /* CONFIG_IEEE80211W */
286 break; /* skip the loop */
291 static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant
*wpa_s
,
292 const u8
*frm
, int len
)
295 * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data |
296 * WNM-Sleep Mode IE | TFS Response IE
298 const u8
*pos
= frm
; /* point to payload after the action field */
300 struct wnm_sleep_element
*wnmsleep_ie
= NULL
;
301 /* multiple TFS Resp IE (assuming consecutive) */
302 const u8
*tfsresp_ie_start
= NULL
;
303 const u8
*tfsresp_ie_end
= NULL
;
305 const u8
*oci_ie
= NULL
;
307 #endif /* CONFIG_OCV */
310 if (!wpa_s
->wnmsleep_used
) {
311 wpa_printf(MSG_DEBUG
,
312 "WNM: Ignore WNM-Sleep Mode Response frame since WNM-Sleep Mode operation has not been requested");
318 key_len_total
= WPA_GET_LE16(frm
+ 1);
320 wpa_printf(MSG_DEBUG
, "WNM-Sleep Mode Response token=%u key_len_total=%d",
321 frm
[0], key_len_total
);
323 if (key_len_total
> left
) {
324 wpa_printf(MSG_INFO
, "WNM: Too short frame for Key Data field");
327 pos
+= 3 + key_len_total
;
328 while (pos
- frm
+ 1 < len
) {
329 u8 ie_len
= *(pos
+ 1);
330 if (2 + ie_len
> frm
+ len
- pos
) {
331 wpa_printf(MSG_INFO
, "WNM: Invalid IE len %u", ie_len
);
334 wpa_hexdump(MSG_DEBUG
, "WNM: Element", pos
, 2 + ie_len
);
335 if (*pos
== WLAN_EID_WNMSLEEP
&& ie_len
>= 4)
336 wnmsleep_ie
= (struct wnm_sleep_element
*) pos
;
337 else if (*pos
== WLAN_EID_TFS_RESP
) {
338 if (!tfsresp_ie_start
)
339 tfsresp_ie_start
= pos
;
340 tfsresp_ie_end
= pos
;
342 } else if (*pos
== WLAN_EID_EXTENSION
&& ie_len
>= 1 &&
343 pos
[2] == WLAN_EID_EXT_OCV_OCI
) {
345 oci_ie_len
= ie_len
- 1;
346 #endif /* CONFIG_OCV */
348 wpa_printf(MSG_DEBUG
, "EID %d not recognized", *pos
);
353 wpa_printf(MSG_DEBUG
, "No WNM-Sleep IE found");
358 if (wnmsleep_ie
->action_type
== WNM_SLEEP_MODE_EXIT
&&
359 wpa_sm_ocv_enabled(wpa_s
->wpa
)) {
360 struct wpa_channel_info ci
;
362 if (wpa_drv_channel_info(wpa_s
, &ci
) != 0) {
363 wpa_msg(wpa_s
, MSG_WARNING
,
364 "Failed to get channel info to validate received OCI in WNM-Sleep Mode frame");
368 if (ocv_verify_tx_params(oci_ie
, oci_ie_len
, &ci
,
369 channel_width_to_int(ci
.chanwidth
),
371 wpa_msg(wpa_s
, MSG_WARNING
, "WNM: %s", ocv_errorstr
);
375 #endif /* CONFIG_OCV */
377 wpa_s
->wnmsleep_used
= 0;
379 if (wnmsleep_ie
->status
== WNM_STATUS_SLEEP_ACCEPT
||
380 wnmsleep_ie
->status
== WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE
) {
381 wpa_printf(MSG_DEBUG
, "Successfully recv WNM-Sleep Response "
382 "frame (action=%d, intval=%d)",
383 wnmsleep_ie
->action_type
, wnmsleep_ie
->intval
);
384 if (wnmsleep_ie
->action_type
== WNM_SLEEP_MODE_ENTER
) {
385 wnm_sleep_mode_enter_success(wpa_s
, tfsresp_ie_start
,
387 } else if (wnmsleep_ie
->action_type
== WNM_SLEEP_MODE_EXIT
) {
388 wnm_sleep_mode_exit_success(wpa_s
, frm
, key_len_total
);
391 wpa_printf(MSG_DEBUG
, "Reject recv WNM-Sleep Response frame "
392 "(action=%d, intval=%d)",
393 wnmsleep_ie
->action_type
, wnmsleep_ie
->intval
);
394 if (wnmsleep_ie
->action_type
== WNM_SLEEP_MODE_ENTER
)
395 wpa_drv_wnm_oper(wpa_s
, WNM_SLEEP_ENTER_FAIL
,
396 wpa_s
->bssid
, NULL
, NULL
);
397 else if (wnmsleep_ie
->action_type
== WNM_SLEEP_MODE_EXIT
)
398 wpa_drv_wnm_oper(wpa_s
, WNM_SLEEP_EXIT_FAIL
,
399 wpa_s
->bssid
, NULL
, NULL
);
404 void wnm_deallocate_memory(struct wpa_supplicant
*wpa_s
)
408 for (i
= 0; i
< wpa_s
->wnm_num_neighbor_report
; i
++) {
409 os_free(wpa_s
->wnm_neighbor_report_elements
[i
].meas_pilot
);
410 os_free(wpa_s
->wnm_neighbor_report_elements
[i
].mul_bssid
);
413 wpa_s
->wnm_num_neighbor_report
= 0;
414 os_free(wpa_s
->wnm_neighbor_report_elements
);
415 wpa_s
->wnm_neighbor_report_elements
= NULL
;
417 wpabuf_free(wpa_s
->coloc_intf_elems
);
418 wpa_s
->coloc_intf_elems
= NULL
;
422 static void wnm_parse_neighbor_report_elem(struct neighbor_report
*rep
,
423 u8 id
, u8 elen
, const u8
*pos
)
426 case WNM_NEIGHBOR_TSF
:
428 wpa_printf(MSG_DEBUG
, "WNM: Too short TSF");
431 rep
->tsf_offset
= WPA_GET_LE16(pos
);
432 rep
->beacon_int
= WPA_GET_LE16(pos
+ 2);
433 rep
->tsf_present
= 1;
435 case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING
:
437 wpa_printf(MSG_DEBUG
, "WNM: Too short condensed "
441 os_memcpy(rep
->country
, pos
, 2);
442 rep
->country_present
= 1;
444 case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE
:
446 wpa_printf(MSG_DEBUG
, "WNM: Too short BSS transition "
450 rep
->preference
= pos
[0];
451 rep
->preference_present
= 1;
453 case WNM_NEIGHBOR_BSS_TERMINATION_DURATION
:
455 wpa_printf(MSG_DEBUG
,
456 "WNM: Too short BSS termination duration");
459 rep
->bss_term_tsf
= WPA_GET_LE64(pos
);
460 rep
->bss_term_dur
= WPA_GET_LE16(pos
+ 8);
461 rep
->bss_term_present
= 1;
463 case WNM_NEIGHBOR_BEARING
:
465 wpa_printf(MSG_DEBUG
, "WNM: Too short neighbor "
469 rep
->bearing
= WPA_GET_LE16(pos
);
470 rep
->distance
= WPA_GET_LE32(pos
+ 2);
471 rep
->rel_height
= WPA_GET_LE16(pos
+ 2 + 4);
472 rep
->bearing_present
= 1;
474 case WNM_NEIGHBOR_MEASUREMENT_PILOT
:
476 wpa_printf(MSG_DEBUG
, "WNM: Too short measurement "
480 os_free(rep
->meas_pilot
);
481 rep
->meas_pilot
= os_zalloc(sizeof(struct measurement_pilot
));
482 if (rep
->meas_pilot
== NULL
)
484 rep
->meas_pilot
->measurement_pilot
= pos
[0];
485 rep
->meas_pilot
->subelem_len
= elen
- 1;
486 os_memcpy(rep
->meas_pilot
->subelems
, pos
+ 1, elen
- 1);
488 case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES
:
490 wpa_printf(MSG_DEBUG
, "WNM: Too short RRM enabled "
494 os_memcpy(rep
->rm_capab
, pos
, 5);
495 rep
->rm_capab_present
= 1;
497 case WNM_NEIGHBOR_MULTIPLE_BSSID
:
499 wpa_printf(MSG_DEBUG
, "WNM: Too short multiple BSSID");
502 os_free(rep
->mul_bssid
);
503 rep
->mul_bssid
= os_zalloc(sizeof(struct multiple_bssid
));
504 if (rep
->mul_bssid
== NULL
)
506 rep
->mul_bssid
->max_bssid_indicator
= pos
[0];
507 rep
->mul_bssid
->subelem_len
= elen
- 1;
508 os_memcpy(rep
->mul_bssid
->subelems
, pos
+ 1, elen
- 1);
514 static int wnm_nei_get_chan(struct wpa_supplicant
*wpa_s
, u8 op_class
, u8 chan
)
516 struct wpa_bss
*bss
= wpa_s
->current_bss
;
517 const char *country
= NULL
;
521 const u8
*elem
= wpa_bss_get_ie(bss
, WLAN_EID_COUNTRY
);
523 if (elem
&& elem
[1] >= 2)
524 country
= (const char *) (elem
+ 2);
527 freq
= ieee80211_chan_to_freq(country
, op_class
, chan
);
528 if (freq
<= 0 && op_class
== 0) {
530 * Some APs do not advertise correct operating class
531 * information. Try to determine the most likely operating
532 * frequency based on the channel number.
534 if (chan
>= 1 && chan
<= 13)
535 freq
= 2407 + chan
* 5;
538 else if (chan
>= 36 && chan
<= 169)
539 freq
= 5000 + chan
* 5;
545 static void wnm_parse_neighbor_report(struct wpa_supplicant
*wpa_s
,
546 const u8
*pos
, u8 len
,
547 struct neighbor_report
*rep
)
552 wpa_printf(MSG_DEBUG
, "WNM: Too short neighbor report");
556 os_memcpy(rep
->bssid
, pos
, ETH_ALEN
);
557 rep
->bssid_info
= WPA_GET_LE32(pos
+ ETH_ALEN
);
558 rep
->regulatory_class
= *(pos
+ 10);
559 rep
->channel_number
= *(pos
+ 11);
560 rep
->phy_type
= *(pos
+ 12);
570 wpa_printf(MSG_DEBUG
, "WNM: Subelement id=%u len=%u", id
, elen
);
573 wpa_printf(MSG_DEBUG
,
574 "WNM: Truncated neighbor report subelement");
577 wnm_parse_neighbor_report_elem(rep
, id
, elen
, pos
);
582 rep
->freq
= wnm_nei_get_chan(wpa_s
, rep
->regulatory_class
,
583 rep
->channel_number
);
587 static void wnm_clear_acceptable(struct wpa_supplicant
*wpa_s
)
591 for (i
= 0; i
< wpa_s
->wnm_num_neighbor_report
; i
++)
592 wpa_s
->wnm_neighbor_report_elements
[i
].acceptable
= 0;
596 static struct wpa_bss
* get_first_acceptable(struct wpa_supplicant
*wpa_s
)
599 struct neighbor_report
*nei
;
601 for (i
= 0; i
< wpa_s
->wnm_num_neighbor_report
; i
++) {
602 nei
= &wpa_s
->wnm_neighbor_report_elements
[i
];
604 return wpa_bss_get_bssid(wpa_s
, nei
->bssid
);
612 static struct wpa_bss
*
613 get_mbo_transition_candidate(struct wpa_supplicant
*wpa_s
,
614 enum mbo_transition_reject_reason
*reason
)
616 struct wpa_bss
*target
= NULL
;
617 struct wpa_bss_trans_info params
;
618 struct wpa_bss_candidate_info
*info
= NULL
;
619 struct neighbor_report
*nei
= wpa_s
->wnm_neighbor_report_elements
;
620 u8
*first_candidate_bssid
= NULL
, *pos
;
623 params
.mbo_transition_reason
= wpa_s
->wnm_mbo_transition_reason
;
624 params
.n_candidates
= 0;
625 params
.bssid
= os_calloc(wpa_s
->wnm_num_neighbor_report
, ETH_ALEN
);
630 for (i
= 0; i
< wpa_s
->wnm_num_neighbor_report
; nei
++, i
++) {
632 first_candidate_bssid
= nei
->bssid
;
633 if (!nei
->acceptable
)
635 os_memcpy(pos
, nei
->bssid
, ETH_ALEN
);
637 params
.n_candidates
++;
640 if (!params
.n_candidates
)
643 info
= wpa_drv_get_bss_trans_status(wpa_s
, ¶ms
);
645 /* If failed to get candidate BSS transition status from driver,
646 * get the first acceptable candidate from wpa_supplicant.
648 target
= wpa_bss_get_bssid(wpa_s
, params
.bssid
);
652 /* Get the first acceptable candidate from driver */
653 for (i
= 0; i
< info
->num
; i
++) {
654 if (info
->candidates
[i
].is_accept
) {
655 target
= wpa_bss_get_bssid(wpa_s
,
656 info
->candidates
[i
].bssid
);
661 /* If Disassociation Imminent is set and driver rejects all the
662 * candidate select first acceptable candidate which has
663 * rssi > disassoc_imminent_rssi_threshold
665 if (wpa_s
->wnm_mode
& WNM_BSS_TM_REQ_DISASSOC_IMMINENT
) {
666 for (i
= 0; i
< info
->num
; i
++) {
667 target
= wpa_bss_get_bssid(wpa_s
,
668 info
->candidates
[i
].bssid
);
671 wpa_s
->conf
->disassoc_imminent_rssi_threshold
))
677 /* While sending BTM reject use reason code of the first candidate
678 * received in BTM request frame
681 for (i
= 0; i
< info
->num
; i
++) {
682 if (first_candidate_bssid
&&
683 os_memcmp(first_candidate_bssid
,
684 info
->candidates
[i
].bssid
, ETH_ALEN
) == 0)
686 *reason
= info
->candidates
[i
].reject_reason
;
695 os_free(params
.bssid
);
697 os_free(info
->candidates
);
702 #endif /* CONFIG_MBO */
705 static struct wpa_bss
*
706 compare_scan_neighbor_results(struct wpa_supplicant
*wpa_s
, os_time_t age_secs
,
707 enum mbo_transition_reject_reason
*reason
)
710 struct wpa_bss
*bss
= wpa_s
->current_bss
;
711 struct wpa_bss
*target
;
716 wpa_printf(MSG_DEBUG
, "WNM: Current BSS " MACSTR
" RSSI %d",
717 MAC2STR(wpa_s
->bssid
), bss
->level
);
719 wnm_clear_acceptable(wpa_s
);
721 for (i
= 0; i
< wpa_s
->wnm_num_neighbor_report
; i
++) {
722 struct neighbor_report
*nei
;
724 nei
= &wpa_s
->wnm_neighbor_report_elements
[i
];
725 if (nei
->preference_present
&& nei
->preference
== 0) {
726 wpa_printf(MSG_DEBUG
, "Skip excluded BSS " MACSTR
,
727 MAC2STR(nei
->bssid
));
731 target
= wpa_bss_get_bssid(wpa_s
, nei
->bssid
);
733 wpa_printf(MSG_DEBUG
, "Candidate BSS " MACSTR
734 " (pref %d) not found in scan results",
736 nei
->preference_present
? nei
->preference
:
742 struct os_reltime now
;
744 if (os_get_reltime(&now
) == 0 &&
745 os_reltime_expired(&now
, &target
->last_update
,
747 wpa_printf(MSG_DEBUG
,
748 "Candidate BSS is more than %ld seconds old",
754 if (bss
->ssid_len
!= target
->ssid_len
||
755 os_memcmp(bss
->ssid
, target
->ssid
, bss
->ssid_len
) != 0) {
757 * TODO: Could consider allowing transition to another
758 * ESS if PMF was enabled for the association.
760 wpa_printf(MSG_DEBUG
, "Candidate BSS " MACSTR
761 " (pref %d) in different ESS",
763 nei
->preference_present
? nei
->preference
:
768 if (wpa_s
->current_ssid
&&
769 !wpa_scan_res_match(wpa_s
, 0, target
, wpa_s
->current_ssid
,
771 wpa_printf(MSG_DEBUG
, "Candidate BSS " MACSTR
772 " (pref %d) does not match the current network profile",
774 nei
->preference_present
? nei
->preference
:
779 if (wpa_is_bss_tmp_disallowed(wpa_s
, target
)) {
780 wpa_printf(MSG_DEBUG
,
781 "MBO: Candidate BSS " MACSTR
782 " retry delay is not over yet",
783 MAC2STR(nei
->bssid
));
787 if (target
->level
< bss
->level
&& target
->level
< -80) {
788 wpa_printf(MSG_DEBUG
, "Candidate BSS " MACSTR
789 " (pref %d) does not have sufficient signal level (%d)",
791 nei
->preference_present
? nei
->preference
:
801 if (wpa_s
->wnm_mbo_trans_reason_present
)
802 target
= get_mbo_transition_candidate(wpa_s
, reason
);
804 target
= get_first_acceptable(wpa_s
);
805 #else /* CONFIG_MBO */
806 target
= get_first_acceptable(wpa_s
);
807 #endif /* CONFIG_MBO */
810 wpa_printf(MSG_DEBUG
,
811 "WNM: Found an acceptable preferred transition candidate BSS "
813 MAC2STR(target
->bssid
), target
->level
);
820 static int wpa_bss_ies_eq(struct wpa_bss
*a
, struct wpa_bss
*b
, u8 eid
)
822 const u8
*ie_a
, *ie_b
;
827 ie_a
= wpa_bss_get_ie(a
, eid
);
828 ie_b
= wpa_bss_get_ie(b
, eid
);
830 if (!ie_a
|| !ie_b
|| ie_a
[1] != ie_b
[1])
833 return os_memcmp(ie_a
, ie_b
, ie_a
[1]) == 0;
837 static u32
wnm_get_bss_info(struct wpa_supplicant
*wpa_s
, struct wpa_bss
*bss
)
841 info
|= NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH
;
844 * Leave the security and key scope bits unset to indicate that the
845 * security information is not available.
848 if (bss
->caps
& WLAN_CAPABILITY_SPECTRUM_MGMT
)
849 info
|= NEI_REP_BSSID_INFO_SPECTRUM_MGMT
;
850 if (bss
->caps
& WLAN_CAPABILITY_QOS
)
851 info
|= NEI_REP_BSSID_INFO_QOS
;
852 if (bss
->caps
& WLAN_CAPABILITY_APSD
)
853 info
|= NEI_REP_BSSID_INFO_APSD
;
854 if (bss
->caps
& WLAN_CAPABILITY_RADIO_MEASUREMENT
)
855 info
|= NEI_REP_BSSID_INFO_RM
;
856 if (bss
->caps
& WLAN_CAPABILITY_DELAYED_BLOCK_ACK
)
857 info
|= NEI_REP_BSSID_INFO_DELAYED_BA
;
858 if (bss
->caps
& WLAN_CAPABILITY_IMM_BLOCK_ACK
)
859 info
|= NEI_REP_BSSID_INFO_IMM_BA
;
860 if (wpa_bss_ies_eq(bss
, wpa_s
->current_bss
, WLAN_EID_MOBILITY_DOMAIN
))
861 info
|= NEI_REP_BSSID_INFO_MOBILITY_DOMAIN
;
862 if (wpa_bss_ies_eq(bss
, wpa_s
->current_bss
, WLAN_EID_HT_CAP
))
863 info
|= NEI_REP_BSSID_INFO_HT
;
869 static int wnm_add_nei_rep(struct wpabuf
**buf
, const u8
*bssid
,
870 u32 bss_info
, u8 op_class
, u8 chan
, u8 phy_type
,
873 if (wpabuf_len(*buf
) + 18 >
874 IEEE80211_MAX_MMPDU_SIZE
- IEEE80211_HDRLEN
) {
875 wpa_printf(MSG_DEBUG
,
876 "WNM: No room in frame for Neighbor Report element");
880 if (wpabuf_resize(buf
, 18) < 0) {
881 wpa_printf(MSG_DEBUG
,
882 "WNM: Failed to allocate memory for Neighbor Report element");
886 wpabuf_put_u8(*buf
, WLAN_EID_NEIGHBOR_REPORT
);
887 /* length: 13 for basic neighbor report + 3 for preference subelement */
888 wpabuf_put_u8(*buf
, 16);
889 wpabuf_put_data(*buf
, bssid
, ETH_ALEN
);
890 wpabuf_put_le32(*buf
, bss_info
);
891 wpabuf_put_u8(*buf
, op_class
);
892 wpabuf_put_u8(*buf
, chan
);
893 wpabuf_put_u8(*buf
, phy_type
);
894 wpabuf_put_u8(*buf
, WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE
);
895 wpabuf_put_u8(*buf
, 1);
896 wpabuf_put_u8(*buf
, pref
);
901 static int wnm_nei_rep_add_bss(struct wpa_supplicant
*wpa_s
,
902 struct wpa_bss
*bss
, struct wpabuf
**buf
,
907 int sec_chan
= 0, vht
= 0;
908 enum phy_type phy_type
;
910 struct ieee80211_ht_operation
*ht_oper
= NULL
;
911 struct ieee80211_vht_operation
*vht_oper
= NULL
;
913 ie
= wpa_bss_get_ie(bss
, WLAN_EID_HT_OPERATION
);
914 if (ie
&& ie
[1] >= 2) {
915 ht_oper
= (struct ieee80211_ht_operation
*) (ie
+ 2);
917 if (ht_oper
->ht_param
& HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE
)
919 else if (ht_oper
->ht_param
&
920 HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW
)
924 ie
= wpa_bss_get_ie(bss
, WLAN_EID_VHT_OPERATION
);
925 if (ie
&& ie
[1] >= 1) {
926 vht_oper
= (struct ieee80211_vht_operation
*) (ie
+ 2);
928 if (vht_oper
->vht_op_info_chwidth
== CHANWIDTH_80MHZ
||
929 vht_oper
->vht_op_info_chwidth
== CHANWIDTH_160MHZ
||
930 vht_oper
->vht_op_info_chwidth
== CHANWIDTH_80P80MHZ
)
931 vht
= vht_oper
->vht_op_info_chwidth
;
934 if (ieee80211_freq_to_channel_ext(bss
->freq
, sec_chan
, vht
, &op_class
,
935 &chan
) == NUM_HOSTAPD_MODES
) {
936 wpa_printf(MSG_DEBUG
,
937 "WNM: Cannot determine operating class and channel");
941 phy_type
= ieee80211_get_phy_type(bss
->freq
, (ht_oper
!= NULL
),
943 if (phy_type
== PHY_TYPE_UNSPECIFIED
) {
944 wpa_printf(MSG_DEBUG
,
945 "WNM: Cannot determine BSS phy type for Neighbor Report");
949 info
= wnm_get_bss_info(wpa_s
, bss
);
951 return wnm_add_nei_rep(buf
, bss
->bssid
, info
, op_class
, chan
, phy_type
,
956 static void wnm_add_cand_list(struct wpa_supplicant
*wpa_s
, struct wpabuf
**buf
)
958 unsigned int i
, pref
= 255;
959 struct os_reltime now
;
960 struct wpa_ssid
*ssid
= wpa_s
->current_ssid
;
966 * TODO: Define when scan results are no longer valid for the candidate
969 os_get_reltime(&now
);
970 if (os_reltime_expired(&now
, &wpa_s
->last_scan
, 10))
973 wpa_printf(MSG_DEBUG
,
974 "WNM: Add candidate list to BSS Transition Management Response frame");
975 for (i
= 0; i
< wpa_s
->last_scan_res_used
&& pref
; i
++) {
976 struct wpa_bss
*bss
= wpa_s
->last_scan_res
[i
];
979 if (wpa_scan_res_match(wpa_s
, i
, bss
, ssid
, 1, 0)) {
980 res
= wnm_nei_rep_add_bss(wpa_s
, bss
, buf
, pref
--);
982 continue; /* could not build entry for BSS */
984 break; /* no more room for candidates */
990 wpa_hexdump_buf(MSG_DEBUG
,
991 "WNM: BSS Transition Management Response candidate list",
996 #define BTM_RESP_MIN_SIZE 5 + ETH_ALEN
998 static void wnm_send_bss_transition_mgmt_resp(
999 struct wpa_supplicant
*wpa_s
, u8 dialog_token
,
1000 enum bss_trans_mgmt_status_code status
,
1001 enum mbo_transition_reject_reason reason
,
1002 u8 delay
, const u8
*target_bssid
)
1007 wpa_printf(MSG_DEBUG
,
1008 "WNM: Send BSS Transition Management Response to " MACSTR
1009 " dialog_token=%u status=%u reason=%u delay=%d",
1010 MAC2STR(wpa_s
->bssid
), dialog_token
, status
, reason
, delay
);
1011 if (!wpa_s
->current_bss
) {
1012 wpa_printf(MSG_DEBUG
,
1013 "WNM: Current BSS not known - drop response");
1017 buf
= wpabuf_alloc(BTM_RESP_MIN_SIZE
);
1019 wpa_printf(MSG_DEBUG
,
1020 "WNM: Failed to allocate memory for BTM response");
1024 wpa_s
->bss_tm_status
= status
;
1025 wpas_notify_bss_tm_status(wpa_s
);
1027 wpabuf_put_u8(buf
, WLAN_ACTION_WNM
);
1028 wpabuf_put_u8(buf
, WNM_BSS_TRANS_MGMT_RESP
);
1029 wpabuf_put_u8(buf
, dialog_token
);
1030 wpabuf_put_u8(buf
, status
);
1031 wpabuf_put_u8(buf
, delay
);
1033 wpabuf_put_data(buf
, target_bssid
, ETH_ALEN
);
1034 } else if (status
== WNM_BSS_TM_ACCEPT
) {
1036 * P802.11-REVmc clarifies that the Target BSSID field is always
1037 * present when status code is zero, so use a fake value here if
1038 * no BSSID is yet known.
1040 wpabuf_put_data(buf
, "\0\0\0\0\0\0", ETH_ALEN
);
1043 if (status
== WNM_BSS_TM_ACCEPT
)
1044 wnm_add_cand_list(wpa_s
, &buf
);
1047 if (status
!= WNM_BSS_TM_ACCEPT
&&
1048 wpa_bss_get_vendor_ie(wpa_s
->current_bss
, MBO_IE_VENDOR_TYPE
)) {
1052 ret
= wpas_mbo_ie_bss_trans_reject(wpa_s
, mbo
, sizeof(mbo
),
1055 if (wpabuf_resize(&buf
, ret
) < 0) {
1057 wpa_printf(MSG_DEBUG
,
1058 "WNM: Failed to allocate memory for MBO IE");
1062 wpabuf_put_data(buf
, mbo
, ret
);
1065 #endif /* CONFIG_MBO */
1067 res
= wpa_drv_send_action(wpa_s
, wpa_s
->assoc_freq
, 0, wpa_s
->bssid
,
1068 wpa_s
->own_addr
, wpa_s
->bssid
,
1069 wpabuf_head_u8(buf
), wpabuf_len(buf
), 0);
1071 wpa_printf(MSG_DEBUG
,
1072 "WNM: Failed to send BSS Transition Management Response");
1079 static void wnm_bss_tm_connect(struct wpa_supplicant
*wpa_s
,
1080 struct wpa_bss
*bss
, struct wpa_ssid
*ssid
,
1083 wpa_dbg(wpa_s
, MSG_DEBUG
,
1084 "WNM: Transition to BSS " MACSTR
1085 " based on BSS Transition Management Request (old BSSID "
1086 MACSTR
" after_new_scan=%d)",
1087 MAC2STR(bss
->bssid
), MAC2STR(wpa_s
->bssid
), after_new_scan
);
1089 /* Send the BSS Management Response - Accept */
1090 if (wpa_s
->wnm_reply
) {
1091 wpa_s
->wnm_reply
= 0;
1092 wpa_printf(MSG_DEBUG
,
1093 "WNM: Sending successful BSS Transition Management Response");
1094 wnm_send_bss_transition_mgmt_resp(
1095 wpa_s
, wpa_s
->wnm_dialog_token
, WNM_BSS_TM_ACCEPT
,
1096 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED
, 0,
1100 if (bss
== wpa_s
->current_bss
) {
1101 wpa_printf(MSG_DEBUG
,
1102 "WNM: Already associated with the preferred candidate");
1103 wnm_deallocate_memory(wpa_s
);
1107 wpa_s
->reassociate
= 1;
1108 wpa_printf(MSG_DEBUG
, "WNM: Issuing connect");
1109 wpa_supplicant_connect(wpa_s
, bss
, ssid
);
1110 wnm_deallocate_memory(wpa_s
);
1114 int wnm_scan_process(struct wpa_supplicant
*wpa_s
, int reply_on_fail
)
1116 struct wpa_bss
*bss
;
1117 struct wpa_ssid
*ssid
= wpa_s
->current_ssid
;
1118 enum bss_trans_mgmt_status_code status
= WNM_BSS_TM_REJECT_UNSPECIFIED
;
1119 enum mbo_transition_reject_reason reason
=
1120 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED
;
1122 if (!wpa_s
->wnm_neighbor_report_elements
)
1125 wpa_dbg(wpa_s
, MSG_DEBUG
,
1126 "WNM: Process scan results for BSS Transition Management");
1127 if (os_reltime_before(&wpa_s
->wnm_cand_valid_until
,
1128 &wpa_s
->scan_trigger_time
)) {
1129 wpa_printf(MSG_DEBUG
, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
1130 wnm_deallocate_memory(wpa_s
);
1134 if (!wpa_s
->current_bss
||
1135 os_memcmp(wpa_s
->wnm_cand_from_bss
, wpa_s
->current_bss
->bssid
,
1137 wpa_printf(MSG_DEBUG
, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it");
1141 /* Compare the Neighbor Report and scan results */
1142 bss
= compare_scan_neighbor_results(wpa_s
, 0, &reason
);
1144 wpa_printf(MSG_DEBUG
, "WNM: No BSS transition candidate match found");
1145 status
= WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES
;
1146 goto send_bss_resp_fail
;
1149 /* Associate to the network */
1150 wnm_bss_tm_connect(wpa_s
, bss
, ssid
, 1);
1157 /* Send reject response for all the failures */
1159 if (wpa_s
->wnm_reply
) {
1160 wpa_s
->wnm_reply
= 0;
1161 wnm_send_bss_transition_mgmt_resp(wpa_s
,
1162 wpa_s
->wnm_dialog_token
,
1163 status
, reason
, 0, NULL
);
1165 wnm_deallocate_memory(wpa_s
);
1171 static int cand_pref_compar(const void *a
, const void *b
)
1173 const struct neighbor_report
*aa
= a
;
1174 const struct neighbor_report
*bb
= b
;
1176 if (!aa
->preference_present
&& !bb
->preference_present
)
1178 if (!aa
->preference_present
)
1180 if (!bb
->preference_present
)
1182 if (bb
->preference
> aa
->preference
)
1184 if (bb
->preference
< aa
->preference
)
1190 static void wnm_sort_cand_list(struct wpa_supplicant
*wpa_s
)
1192 if (!wpa_s
->wnm_neighbor_report_elements
)
1194 qsort(wpa_s
->wnm_neighbor_report_elements
,
1195 wpa_s
->wnm_num_neighbor_report
, sizeof(struct neighbor_report
),
1200 static void wnm_dump_cand_list(struct wpa_supplicant
*wpa_s
)
1204 wpa_printf(MSG_DEBUG
, "WNM: BSS Transition Candidate List");
1205 if (!wpa_s
->wnm_neighbor_report_elements
)
1207 for (i
= 0; i
< wpa_s
->wnm_num_neighbor_report
; i
++) {
1208 struct neighbor_report
*nei
;
1210 nei
= &wpa_s
->wnm_neighbor_report_elements
[i
];
1211 wpa_printf(MSG_DEBUG
, "%u: " MACSTR
1212 " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d",
1213 i
, MAC2STR(nei
->bssid
), nei
->bssid_info
,
1214 nei
->regulatory_class
,
1215 nei
->channel_number
, nei
->phy_type
,
1216 nei
->preference_present
? nei
->preference
: -1,
1222 static int chan_supported(struct wpa_supplicant
*wpa_s
, int freq
)
1226 for (i
= 0; i
< wpa_s
->hw
.num_modes
; i
++) {
1227 struct hostapd_hw_modes
*mode
= &wpa_s
->hw
.modes
[i
];
1230 for (j
= 0; j
< mode
->num_channels
; j
++) {
1231 struct hostapd_channel_data
*chan
;
1233 chan
= &mode
->channels
[j
];
1234 if (chan
->freq
== freq
&&
1235 !(chan
->flag
& HOSTAPD_CHAN_DISABLED
))
1244 static void wnm_set_scan_freqs(struct wpa_supplicant
*wpa_s
)
1250 if (!wpa_s
->wnm_neighbor_report_elements
)
1253 if (wpa_s
->hw
.modes
== NULL
)
1256 os_free(wpa_s
->next_scan_freqs
);
1257 wpa_s
->next_scan_freqs
= NULL
;
1259 freqs
= os_calloc(wpa_s
->wnm_num_neighbor_report
+ 1, sizeof(int));
1263 for (i
= 0; i
< wpa_s
->wnm_num_neighbor_report
; i
++) {
1264 struct neighbor_report
*nei
;
1266 nei
= &wpa_s
->wnm_neighbor_report_elements
[i
];
1267 if (nei
->freq
<= 0) {
1268 wpa_printf(MSG_DEBUG
,
1269 "WNM: Unknown neighbor operating frequency for "
1270 MACSTR
" - scan all channels",
1271 MAC2STR(nei
->bssid
));
1275 if (chan_supported(wpa_s
, nei
->freq
))
1276 add_freq(freqs
, &num_freqs
, nei
->freq
);
1279 if (num_freqs
== 0) {
1284 wpa_printf(MSG_DEBUG
,
1285 "WNM: Scan %d frequencies based on transition candidate list",
1287 wpa_s
->next_scan_freqs
= freqs
;
1291 static int wnm_fetch_scan_results(struct wpa_supplicant
*wpa_s
)
1293 struct wpa_scan_results
*scan_res
;
1294 struct wpa_bss
*bss
;
1295 struct wpa_ssid
*ssid
= wpa_s
->current_ssid
;
1299 wpa_dbg(wpa_s
, MSG_DEBUG
,
1300 "WNM: Fetch current scan results from the driver for checking transition candidates");
1301 scan_res
= wpa_drv_get_scan_results2(wpa_s
);
1303 wpa_dbg(wpa_s
, MSG_DEBUG
, "WNM: Failed to get scan results");
1307 if (scan_res
->fetch_time
.sec
== 0)
1308 os_get_reltime(&scan_res
->fetch_time
);
1310 filter_scan_res(wpa_s
, scan_res
);
1312 for (i
= 0; i
< wpa_s
->wnm_num_neighbor_report
; i
++) {
1313 struct neighbor_report
*nei
;
1315 nei
= &wpa_s
->wnm_neighbor_report_elements
[i
];
1316 if (nei
->preference_present
&& nei
->preference
== 0)
1319 for (j
= 0; j
< scan_res
->num
; j
++) {
1320 struct wpa_scan_res
*res
;
1323 res
= scan_res
->res
[j
];
1324 if (os_memcmp(nei
->bssid
, res
->bssid
, ETH_ALEN
) != 0 ||
1325 res
->age
> WNM_SCAN_RESULT_AGE
* 1000)
1327 bss
= wpa_s
->current_bss
;
1328 ssid_ie
= wpa_scan_get_ie(res
, WLAN_EID_SSID
);
1329 if (bss
&& ssid_ie
&&
1330 (bss
->ssid_len
!= ssid_ie
[1] ||
1331 os_memcmp(bss
->ssid
, ssid_ie
+ 2,
1332 bss
->ssid_len
) != 0))
1335 /* Potential candidate found */
1338 scan_est_throughput(wpa_s
, res
);
1339 wpa_bss_update_scan_res(wpa_s
, res
,
1340 &scan_res
->fetch_time
);
1344 wpa_scan_results_free(scan_res
);
1346 wpa_dbg(wpa_s
, MSG_DEBUG
,
1347 "WNM: No transition candidate matches existing scan results");
1351 bss
= compare_scan_neighbor_results(wpa_s
, WNM_SCAN_RESULT_AGE
, NULL
);
1353 wpa_dbg(wpa_s
, MSG_DEBUG
,
1354 "WNM: Comparison of scan results against transition candidates did not find matches");
1358 /* Associate to the network */
1359 wnm_bss_tm_connect(wpa_s
, bss
, ssid
, 0);
1364 static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant
*wpa_s
,
1365 const u8
*pos
, const u8
*end
,
1368 unsigned int beacon_int
;
1372 #endif /* CONFIG_MBO */
1374 if (wpa_s
->conf
->disable_btm
)
1381 wpa_s
->wnm_mbo_trans_reason_present
= 0;
1382 wpa_s
->wnm_mbo_transition_reason
= 0;
1383 #endif /* CONFIG_MBO */
1385 if (wpa_s
->current_bss
)
1386 beacon_int
= wpa_s
->current_bss
->beacon_int
;
1388 beacon_int
= 100; /* best guess */
1390 wpa_s
->wnm_dialog_token
= pos
[0];
1391 wpa_s
->wnm_mode
= pos
[1];
1392 wpa_s
->wnm_dissoc_timer
= WPA_GET_LE16(pos
+ 2);
1394 wpa_s
->wnm_reply
= reply
;
1396 wpa_printf(MSG_DEBUG
, "WNM: BSS Transition Management Request: "
1397 "dialog_token=%u request_mode=0x%x "
1398 "disassoc_timer=%u validity_interval=%u",
1399 wpa_s
->wnm_dialog_token
, wpa_s
->wnm_mode
,
1400 wpa_s
->wnm_dissoc_timer
, valid_int
);
1402 #if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS)
1403 if (wpa_s
->reject_btm_req_reason
) {
1404 wpa_printf(MSG_INFO
,
1405 "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d",
1406 wpa_s
->reject_btm_req_reason
);
1407 wnm_send_bss_transition_mgmt_resp(
1408 wpa_s
, wpa_s
->wnm_dialog_token
,
1409 wpa_s
->reject_btm_req_reason
,
1410 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED
, 0, NULL
);
1413 #endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */
1417 if (wpa_s
->wnm_mode
& WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED
) {
1418 if (end
- pos
< 12) {
1419 wpa_printf(MSG_DEBUG
, "WNM: Too short BSS TM Request");
1422 os_memcpy(wpa_s
->wnm_bss_termination_duration
, pos
, 12);
1423 pos
+= 12; /* BSS Termination Duration */
1426 if (wpa_s
->wnm_mode
& WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT
) {
1429 if (end
- pos
< 1 || 1 + pos
[0] > end
- pos
) {
1430 wpa_printf(MSG_DEBUG
, "WNM: Invalid BSS Transition "
1431 "Management Request (URL)");
1434 os_memcpy(url
, pos
+ 1, pos
[0]);
1438 wpa_msg(wpa_s
, MSG_INFO
, ESS_DISASSOC_IMMINENT
"%d %u %s",
1439 wpa_sm_pmf_enabled(wpa_s
->wpa
),
1440 wpa_s
->wnm_dissoc_timer
* beacon_int
* 128 / 125, url
);
1443 if (wpa_s
->wnm_mode
& WNM_BSS_TM_REQ_DISASSOC_IMMINENT
) {
1444 wpa_msg(wpa_s
, MSG_INFO
, "WNM: Disassociation Imminent - "
1445 "Disassociation Timer %u", wpa_s
->wnm_dissoc_timer
);
1446 if (wpa_s
->wnm_dissoc_timer
&& !wpa_s
->scanning
) {
1447 /* TODO: mark current BSS less preferred for
1449 wpa_printf(MSG_DEBUG
, "Trying to find another BSS");
1450 wpa_supplicant_req_scan(wpa_s
, 0, 0);
1455 vendor
= get_ie(pos
, end
- pos
, WLAN_EID_VENDOR_SPECIFIC
);
1457 wpas_mbo_ie_trans_req(wpa_s
, vendor
+ 2, vendor
[1]);
1458 #endif /* CONFIG_MBO */
1460 if (wpa_s
->wnm_mode
& WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED
) {
1461 unsigned int valid_ms
;
1463 wpa_msg(wpa_s
, MSG_INFO
, "WNM: Preferred List Available");
1464 wnm_deallocate_memory(wpa_s
);
1465 wpa_s
->wnm_neighbor_report_elements
= os_calloc(
1466 WNM_MAX_NEIGHBOR_REPORT
,
1467 sizeof(struct neighbor_report
));
1468 if (wpa_s
->wnm_neighbor_report_elements
== NULL
)
1471 while (end
- pos
>= 2 &&
1472 wpa_s
->wnm_num_neighbor_report
< WNM_MAX_NEIGHBOR_REPORT
)
1477 wpa_printf(MSG_DEBUG
, "WNM: Neighbor report tag %u",
1479 if (len
> end
- pos
) {
1480 wpa_printf(MSG_DEBUG
, "WNM: Truncated request");
1483 if (tag
== WLAN_EID_NEIGHBOR_REPORT
) {
1484 struct neighbor_report
*rep
;
1485 rep
= &wpa_s
->wnm_neighbor_report_elements
[
1486 wpa_s
->wnm_num_neighbor_report
];
1487 wnm_parse_neighbor_report(wpa_s
, pos
, len
, rep
);
1488 wpa_s
->wnm_num_neighbor_report
++;
1490 if (wpa_s
->wnm_mbo_trans_reason_present
&&
1491 wpa_s
->wnm_num_neighbor_report
== 1) {
1493 wpa_printf(MSG_DEBUG
,
1494 "WNM: First transition candidate is "
1495 MACSTR
, MAC2STR(rep
->bssid
));
1497 #endif /* CONFIG_MBO */
1503 if (!wpa_s
->wnm_num_neighbor_report
) {
1504 wpa_printf(MSG_DEBUG
,
1505 "WNM: Candidate list included bit is set, but no candidates found");
1506 wnm_send_bss_transition_mgmt_resp(
1507 wpa_s
, wpa_s
->wnm_dialog_token
,
1508 WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES
,
1509 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED
, 0,
1514 wnm_sort_cand_list(wpa_s
);
1515 wnm_dump_cand_list(wpa_s
);
1516 valid_ms
= valid_int
* beacon_int
* 128 / 125;
1517 wpa_printf(MSG_DEBUG
, "WNM: Candidate list valid for %u ms",
1519 os_get_reltime(&wpa_s
->wnm_cand_valid_until
);
1520 wpa_s
->wnm_cand_valid_until
.sec
+= valid_ms
/ 1000;
1521 wpa_s
->wnm_cand_valid_until
.usec
+= (valid_ms
% 1000) * 1000;
1522 wpa_s
->wnm_cand_valid_until
.sec
+=
1523 wpa_s
->wnm_cand_valid_until
.usec
/ 1000000;
1524 wpa_s
->wnm_cand_valid_until
.usec
%= 1000000;
1525 os_memcpy(wpa_s
->wnm_cand_from_bss
, wpa_s
->bssid
, ETH_ALEN
);
1528 * Fetch the latest scan results from the kernel and check for
1529 * candidates based on those results first. This can help in
1530 * finding more up-to-date information should the driver has
1531 * done some internal scanning operations after the last scan
1532 * result update in wpa_supplicant.
1534 if (wnm_fetch_scan_results(wpa_s
) > 0)
1538 * Try to use previously received scan results, if they are
1539 * recent enough to use for a connection.
1541 if (wpa_s
->last_scan_res_used
> 0) {
1542 struct os_reltime now
;
1544 os_get_reltime(&now
);
1545 if (!os_reltime_expired(&now
, &wpa_s
->last_scan
, 10)) {
1546 wpa_printf(MSG_DEBUG
,
1547 "WNM: Try to use recent scan results");
1548 if (wnm_scan_process(wpa_s
, 0) > 0)
1550 wpa_printf(MSG_DEBUG
,
1551 "WNM: No match in previous scan results - try a new scan");
1555 wnm_set_scan_freqs(wpa_s
);
1556 if (wpa_s
->wnm_num_neighbor_report
== 1) {
1557 os_memcpy(wpa_s
->next_scan_bssid
,
1558 wpa_s
->wnm_neighbor_report_elements
[0].bssid
,
1560 wpa_printf(MSG_DEBUG
,
1561 "WNM: Scan only for a specific BSSID since there is only a single candidate "
1562 MACSTR
, MAC2STR(wpa_s
->next_scan_bssid
));
1564 wpa_supplicant_req_scan(wpa_s
, 0, 0);
1566 enum bss_trans_mgmt_status_code status
;
1567 if (wpa_s
->wnm_mode
& WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT
)
1568 status
= WNM_BSS_TM_ACCEPT
;
1570 wpa_msg(wpa_s
, MSG_INFO
, "WNM: BSS Transition Management Request did not include candidates");
1571 status
= WNM_BSS_TM_REJECT_UNSPECIFIED
;
1573 wnm_send_bss_transition_mgmt_resp(
1574 wpa_s
, wpa_s
->wnm_dialog_token
, status
,
1575 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED
, 0, NULL
);
1580 #define BTM_QUERY_MIN_SIZE 4
1582 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant
*wpa_s
,
1584 const char *btm_candidates
,
1590 wpa_printf(MSG_DEBUG
, "WNM: Send BSS Transition Management Query to "
1591 MACSTR
" query_reason=%u%s",
1592 MAC2STR(wpa_s
->bssid
), query_reason
,
1593 cand_list
? " candidate list" : "");
1595 buf
= wpabuf_alloc(BTM_QUERY_MIN_SIZE
);
1599 wpabuf_put_u8(buf
, WLAN_ACTION_WNM
);
1600 wpabuf_put_u8(buf
, WNM_BSS_TRANS_MGMT_QUERY
);
1601 wpabuf_put_u8(buf
, 1);
1602 wpabuf_put_u8(buf
, query_reason
);
1605 wnm_add_cand_list(wpa_s
, &buf
);
1607 if (btm_candidates
) {
1608 const size_t max_len
= 1000;
1610 ret
= wpabuf_resize(&buf
, max_len
);
1616 ret
= ieee802_11_parse_candidate_list(btm_candidates
,
1624 wpabuf_put(buf
, ret
);
1627 ret
= wpa_drv_send_action(wpa_s
, wpa_s
->assoc_freq
, 0, wpa_s
->bssid
,
1628 wpa_s
->own_addr
, wpa_s
->bssid
,
1629 wpabuf_head_u8(buf
), wpabuf_len(buf
), 0);
1636 static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant
*wpa_s
,
1637 const u8
*sa
, const u8
*data
,
1640 const u8
*pos
, *end
, *next
;
1646 while (end
- pos
> 1) {
1649 wpa_printf(MSG_DEBUG
, "WNM: WFA subelement %u len %u",
1651 if (ie_len
> end
- pos
) {
1652 wpa_printf(MSG_DEBUG
, "WNM: Not enough room for "
1656 next
= pos
+ ie_len
;
1661 wpa_printf(MSG_DEBUG
, "WNM: Subelement OUI %06x type %u",
1662 WPA_GET_BE24(pos
), pos
[3]);
1665 if (ie
== WLAN_EID_VENDOR_SPECIFIC
&& ie_len
>= 5 &&
1666 WPA_GET_BE24(pos
) == OUI_WFA
&&
1667 pos
[3] == HS20_WNM_SUB_REM_NEEDED
) {
1668 /* Subscription Remediation subelement */
1674 wpa_printf(MSG_DEBUG
, "WNM: Subscription Remediation "
1676 ie_end
= pos
+ ie_len
;
1680 wpa_printf(MSG_DEBUG
, "WNM: No Server URL included");
1684 if (url_len
+ 1 > ie_end
- pos
) {
1685 wpa_printf(MSG_DEBUG
, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)",
1687 (int) (ie_end
- pos
));
1690 url
= os_malloc(url_len
+ 1);
1693 os_memcpy(url
, pos
, url_len
);
1694 url
[url_len
] = '\0';
1695 osu_method
= pos
[url_len
];
1697 hs20_rx_subscription_remediation(wpa_s
, url
,
1704 if (ie
== WLAN_EID_VENDOR_SPECIFIC
&& ie_len
>= 8 &&
1705 WPA_GET_BE24(pos
) == OUI_WFA
&&
1706 pos
[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE
) {
1713 ie_end
= pos
+ ie_len
;
1716 reauth_delay
= WPA_GET_LE16(pos
);
1719 wpa_printf(MSG_DEBUG
, "WNM: HS 2.0 Deauthentication "
1720 "Imminent - Reason Code %u "
1721 "Re-Auth Delay %u URL Length %u",
1722 code
, reauth_delay
, url_len
);
1723 if (url_len
> ie_end
- pos
)
1725 url
= os_malloc(url_len
+ 1);
1728 os_memcpy(url
, pos
, url_len
);
1729 url
[url_len
] = '\0';
1730 hs20_rx_deauth_imminent_notice(wpa_s
, code
,
1737 if (ie
== WLAN_EID_VENDOR_SPECIFIC
&& ie_len
>= 5 &&
1738 WPA_GET_BE24(pos
) == OUI_WFA
&&
1739 pos
[3] == HS20_WNM_T_C_ACCEPTANCE
) {
1744 ie_end
= pos
+ ie_len
;
1747 wpa_printf(MSG_DEBUG
,
1748 "WNM: HS 2.0 Terms and Conditions Acceptance (URL Length %u)",
1750 if (url_len
> ie_end
- pos
)
1752 url
= os_malloc(url_len
+ 1);
1755 os_memcpy(url
, pos
, url_len
);
1756 url
[url_len
] = '\0';
1757 hs20_rx_t_c_acceptance(wpa_s
, url
);
1762 #endif /* CONFIG_HS20 */
1769 static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant
*wpa_s
,
1770 const u8
*sa
, const u8
*frm
, int len
)
1772 const u8
*pos
, *end
;
1773 u8 dialog_token
, type
;
1775 /* Dialog Token [1] | Type [1] | Subelements */
1777 if (len
< 2 || sa
== NULL
)
1781 dialog_token
= *pos
++;
1784 wpa_dbg(wpa_s
, MSG_DEBUG
, "WNM: Received WNM-Notification Request "
1785 "(dialog_token %u type %u sa " MACSTR
")",
1786 dialog_token
, type
, MAC2STR(sa
));
1787 wpa_hexdump(MSG_DEBUG
, "WNM-Notification Request subelements",
1790 if (wpa_s
->wpa_state
!= WPA_COMPLETED
||
1791 os_memcmp(sa
, wpa_s
->bssid
, ETH_ALEN
) != 0) {
1792 wpa_dbg(wpa_s
, MSG_DEBUG
, "WNM: WNM-Notification frame not "
1793 "from our AP - ignore it");
1799 ieee802_11_rx_wnm_notif_req_wfa(wpa_s
, sa
, pos
, end
- pos
);
1802 wpa_dbg(wpa_s
, MSG_DEBUG
, "WNM: Ignore unknown "
1803 "WNM-Notification type %u", type
);
1809 static void ieee802_11_rx_wnm_coloc_intf_req(struct wpa_supplicant
*wpa_s
,
1810 const u8
*sa
, const u8
*frm
,
1813 u8 dialog_token
, req_info
, auto_report
, timeout
;
1815 if (!wpa_s
->conf
->coloc_intf_reporting
)
1818 /* Dialog Token [1] | Request Info [1] */
1822 dialog_token
= frm
[0];
1824 auto_report
= req_info
& 0x03;
1825 timeout
= req_info
>> 2;
1827 wpa_dbg(wpa_s
, MSG_DEBUG
,
1828 "WNM: Received Collocated Interference Request (dialog_token %u auto_report %u timeout %u sa " MACSTR
")",
1829 dialog_token
, auto_report
, timeout
, MAC2STR(sa
));
1831 if (dialog_token
== 0)
1832 return; /* only nonzero values are used for request */
1834 if (wpa_s
->wpa_state
!= WPA_COMPLETED
||
1835 os_memcmp(sa
, wpa_s
->bssid
, ETH_ALEN
) != 0) {
1836 wpa_dbg(wpa_s
, MSG_DEBUG
,
1837 "WNM: Collocated Interference Request frame not from current AP - ignore it");
1841 wpa_msg(wpa_s
, MSG_INFO
, COLOC_INTF_REQ
"%u %u %u",
1842 dialog_token
, auto_report
, timeout
);
1843 wpa_s
->coloc_intf_dialog_token
= dialog_token
;
1844 wpa_s
->coloc_intf_auto_report
= auto_report
;
1845 wpa_s
->coloc_intf_timeout
= timeout
;
1849 void ieee802_11_rx_wnm_action(struct wpa_supplicant
*wpa_s
,
1850 const struct ieee80211_mgmt
*mgmt
, size_t len
)
1852 const u8
*pos
, *end
;
1855 if (len
< IEEE80211_HDRLEN
+ 2)
1858 pos
= ((const u8
*) mgmt
) + IEEE80211_HDRLEN
+ 1;
1860 end
= ((const u8
*) mgmt
) + len
;
1862 wpa_printf(MSG_DEBUG
, "WNM: RX action %u from " MACSTR
,
1863 act
, MAC2STR(mgmt
->sa
));
1864 if (wpa_s
->wpa_state
< WPA_ASSOCIATED
||
1865 os_memcmp(mgmt
->sa
, wpa_s
->bssid
, ETH_ALEN
) != 0) {
1866 wpa_printf(MSG_DEBUG
, "WNM: Ignore unexpected WNM Action "
1872 case WNM_BSS_TRANS_MGMT_REQ
:
1873 ieee802_11_rx_bss_trans_mgmt_req(wpa_s
, pos
, end
,
1874 !(mgmt
->da
[0] & 0x01));
1876 case WNM_SLEEP_MODE_RESP
:
1877 ieee802_11_rx_wnmsleep_resp(wpa_s
, pos
, end
- pos
);
1879 case WNM_NOTIFICATION_REQ
:
1880 ieee802_11_rx_wnm_notif_req(wpa_s
, mgmt
->sa
, pos
, end
- pos
);
1882 case WNM_COLLOCATED_INTERFERENCE_REQ
:
1883 ieee802_11_rx_wnm_coloc_intf_req(wpa_s
, mgmt
->sa
, pos
,
1887 wpa_printf(MSG_ERROR
, "WNM: Unknown request");
1893 int wnm_send_coloc_intf_report(struct wpa_supplicant
*wpa_s
, u8 dialog_token
,
1894 const struct wpabuf
*elems
)
1899 if (wpa_s
->wpa_state
< WPA_ASSOCIATED
|| !elems
)
1902 wpa_printf(MSG_DEBUG
, "WNM: Send Collocated Interference Report to "
1903 MACSTR
" (dialog token %u)",
1904 MAC2STR(wpa_s
->bssid
), dialog_token
);
1906 buf
= wpabuf_alloc(3 + wpabuf_len(elems
));
1910 wpabuf_put_u8(buf
, WLAN_ACTION_WNM
);
1911 wpabuf_put_u8(buf
, WNM_COLLOCATED_INTERFERENCE_REPORT
);
1912 wpabuf_put_u8(buf
, dialog_token
);
1913 wpabuf_put_buf(buf
, elems
);
1915 ret
= wpa_drv_send_action(wpa_s
, wpa_s
->assoc_freq
, 0, wpa_s
->bssid
,
1916 wpa_s
->own_addr
, wpa_s
->bssid
,
1917 wpabuf_head_u8(buf
), wpabuf_len(buf
), 0);
1923 void wnm_set_coloc_intf_elems(struct wpa_supplicant
*wpa_s
,
1924 struct wpabuf
*elems
)
1926 wpabuf_free(wpa_s
->coloc_intf_elems
);
1927 if (elems
&& wpabuf_len(elems
) == 0) {
1931 wpa_s
->coloc_intf_elems
= elems
;
1933 if (wpa_s
->conf
->coloc_intf_reporting
&& wpa_s
->coloc_intf_elems
&&
1934 wpa_s
->coloc_intf_dialog_token
&&
1935 (wpa_s
->coloc_intf_auto_report
== 1 ||
1936 wpa_s
->coloc_intf_auto_report
== 3)) {
1937 /* TODO: Check that there has not been less than
1938 * wpa_s->coloc_intf_timeout * 200 TU from the last report.
1940 wnm_send_coloc_intf_report(wpa_s
,
1941 wpa_s
->coloc_intf_dialog_token
,
1942 wpa_s
->coloc_intf_elems
);
1947 void wnm_clear_coloc_intf_reporting(struct wpa_supplicant
*wpa_s
)
1950 wpa_s
->coloc_intf_dialog_token
= 0;
1951 wpa_s
->coloc_intf_auto_report
= 0;
1952 #endif /* CONFIG_WNM */