4 * Copyright(c) 2015 Intel Deutschland GmbH
6 * Intel Linux Wireless <ilw@linux.intel.com>
7 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
9 * This software may be distributed under the terms of the BSD license.
10 * See README for more details.
13 #include "utils/includes.h"
15 #include "utils/common.h"
16 #include "common/ieee802_11_defs.h"
17 #include "common/gas.h"
19 #include "wpa_supplicant_i.h"
24 /* type + length + oui + oui type */
25 #define MBO_IE_HEADER 6
28 static int wpas_mbo_validate_non_pref_chan(u8 oper_class
, u8 chan
, u8 reason
)
30 if (reason
> MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE
)
33 /* Only checking the validity of the channel and oper_class */
34 if (ieee80211_chan_to_freq(NULL
, oper_class
, chan
) == -1)
41 const u8
* mbo_attr_from_mbo_ie(const u8
*mbo_ie
, enum mbo_attr_id attr
)
44 u8 ie_len
= mbo_ie
[1];
46 if (ie_len
< MBO_IE_HEADER
- 2)
48 mbo
= mbo_ie
+ MBO_IE_HEADER
;
50 return get_ie(mbo
, 2 + ie_len
- MBO_IE_HEADER
, attr
);
54 const u8
* mbo_get_attr_from_ies(const u8
*ies
, size_t ies_len
,
55 enum mbo_attr_id attr
)
59 mbo_ie
= get_vendor_ie(ies
, ies_len
, MBO_IE_VENDOR_TYPE
);
63 return mbo_attr_from_mbo_ie(mbo_ie
, attr
);
67 const u8
* wpas_mbo_get_bss_attr(struct wpa_bss
*bss
, enum mbo_attr_id attr
)
74 mbo
= wpa_bss_get_vendor_ie(bss
, MBO_IE_VENDOR_TYPE
);
78 end
= mbo
+ 2 + mbo
[1];
81 return get_ie(mbo
, end
- mbo
, attr
);
85 static void wpas_mbo_non_pref_chan_attr_body(struct wpa_supplicant
*wpa_s
,
91 wpabuf_put_u8(mbo
, wpa_s
->non_pref_chan
[start
].oper_class
);
93 for (i
= start
; i
< end
; i
++)
94 wpabuf_put_u8(mbo
, wpa_s
->non_pref_chan
[i
].chan
);
96 wpabuf_put_u8(mbo
, wpa_s
->non_pref_chan
[start
].preference
);
97 wpabuf_put_u8(mbo
, wpa_s
->non_pref_chan
[start
].reason
);
101 static void wpas_mbo_non_pref_chan_attr_hdr(struct wpabuf
*mbo
, size_t size
)
103 wpabuf_put_u8(mbo
, MBO_ATTR_ID_NON_PREF_CHAN_REPORT
);
104 wpabuf_put_u8(mbo
, size
); /* Length */
108 static void wpas_mbo_non_pref_chan_attr(struct wpa_supplicant
*wpa_s
,
109 struct wpabuf
*mbo
, u8 start
, u8 end
)
111 size_t size
= end
- start
+ 3;
113 if (size
+ 2 > wpabuf_tailroom(mbo
))
116 wpas_mbo_non_pref_chan_attr_hdr(mbo
, size
);
117 wpas_mbo_non_pref_chan_attr_body(wpa_s
, mbo
, start
, end
);
121 static void wpas_mbo_non_pref_chan_subelem_hdr(struct wpabuf
*mbo
, u8 len
)
123 wpabuf_put_u8(mbo
, WLAN_EID_VENDOR_SPECIFIC
);
124 wpabuf_put_u8(mbo
, len
); /* Length */
125 wpabuf_put_be24(mbo
, OUI_WFA
);
126 wpabuf_put_u8(mbo
, MBO_ATTR_ID_NON_PREF_CHAN_REPORT
);
130 static void wpas_mbo_non_pref_chan_subelement(struct wpa_supplicant
*wpa_s
,
131 struct wpabuf
*mbo
, u8 start
,
134 size_t size
= end
- start
+ 7;
136 if (size
+ 2 > wpabuf_tailroom(mbo
))
139 wpas_mbo_non_pref_chan_subelem_hdr(mbo
, size
);
140 wpas_mbo_non_pref_chan_attr_body(wpa_s
, mbo
, start
, end
);
144 static void wpas_mbo_non_pref_chan_attrs(struct wpa_supplicant
*wpa_s
,
145 struct wpabuf
*mbo
, int subelement
)
148 struct wpa_mbo_non_pref_channel
*start_pref
;
150 if (!wpa_s
->non_pref_chan
|| !wpa_s
->non_pref_chan_num
) {
152 wpas_mbo_non_pref_chan_subelem_hdr(mbo
, 4);
154 wpas_mbo_non_pref_chan_attr_hdr(mbo
, 0);
157 start_pref
= &wpa_s
->non_pref_chan
[0];
159 for (i
= 1; i
<= wpa_s
->non_pref_chan_num
; i
++) {
160 struct wpa_mbo_non_pref_channel
*non_pref
= NULL
;
162 if (i
< wpa_s
->non_pref_chan_num
)
163 non_pref
= &wpa_s
->non_pref_chan
[i
];
165 non_pref
->oper_class
!= start_pref
->oper_class
||
166 non_pref
->reason
!= start_pref
->reason
||
167 non_pref
->preference
!= start_pref
->preference
) {
169 wpas_mbo_non_pref_chan_subelement(wpa_s
, mbo
,
172 wpas_mbo_non_pref_chan_attr(wpa_s
, mbo
, start
,
179 start_pref
= non_pref
;
185 int wpas_mbo_ie(struct wpa_supplicant
*wpa_s
, u8
*buf
, size_t len
,
191 if (len
< MBO_IE_HEADER
+ 3 + 7 +
192 ((wpa_s
->enable_oce
& OCE_STA
) ? 3 : 0))
195 /* Leave room for the MBO IE header */
196 mbo
= wpabuf_alloc(len
- MBO_IE_HEADER
);
200 /* Add non-preferred channels attribute */
201 wpas_mbo_non_pref_chan_attrs(wpa_s
, mbo
, 0);
204 * Send cellular capabilities attribute even if AP does not advertise
205 * cellular capabilities.
207 wpabuf_put_u8(mbo
, MBO_ATTR_ID_CELL_DATA_CAPA
);
208 wpabuf_put_u8(mbo
, 1);
209 wpabuf_put_u8(mbo
, wpa_s
->conf
->mbo_cell_capa
);
211 /* Add OCE capability indication attribute if OCE is enabled */
212 if ((wpa_s
->enable_oce
& OCE_STA
) && add_oce_capa
) {
213 wpabuf_put_u8(mbo
, OCE_ATTR_ID_CAPA_IND
);
214 wpabuf_put_u8(mbo
, 1);
215 wpabuf_put_u8(mbo
, OCE_RELEASE
);
218 res
= mbo_add_ie(buf
, len
, wpabuf_head_u8(mbo
), wpabuf_len(mbo
));
220 wpa_printf(MSG_ERROR
, "Failed to add MBO/OCE IE");
227 static void wpas_mbo_send_wnm_notification(struct wpa_supplicant
*wpa_s
,
228 const u8
*data
, size_t len
)
234 * Send WNM-Notification Request frame only in case of a change in
235 * non-preferred channels list during association, if the AP supports
238 if (wpa_s
->wpa_state
!= WPA_COMPLETED
|| !wpa_s
->current_bss
||
239 !wpa_bss_get_vendor_ie(wpa_s
->current_bss
, MBO_IE_VENDOR_TYPE
))
242 buf
= wpabuf_alloc(4 + len
);
246 wpabuf_put_u8(buf
, WLAN_ACTION_WNM
);
247 wpabuf_put_u8(buf
, WNM_NOTIFICATION_REQ
);
248 wpa_s
->mbo_wnm_token
++;
249 if (wpa_s
->mbo_wnm_token
== 0)
250 wpa_s
->mbo_wnm_token
++;
251 wpabuf_put_u8(buf
, wpa_s
->mbo_wnm_token
);
252 wpabuf_put_u8(buf
, WLAN_EID_VENDOR_SPECIFIC
); /* Type */
254 wpabuf_put_data(buf
, data
, len
);
256 res
= wpa_drv_send_action(wpa_s
, wpa_s
->assoc_freq
, 0, wpa_s
->bssid
,
257 wpa_s
->own_addr
, wpa_s
->bssid
,
258 wpabuf_head(buf
), wpabuf_len(buf
), 0);
260 wpa_printf(MSG_DEBUG
,
261 "Failed to send WNM-Notification Request frame with non-preferred channel list");
267 static void wpas_mbo_non_pref_chan_changed(struct wpa_supplicant
*wpa_s
)
271 buf
= wpabuf_alloc(512);
275 wpas_mbo_non_pref_chan_attrs(wpa_s
, buf
, 1);
276 wpas_mbo_send_wnm_notification(wpa_s
, wpabuf_head_u8(buf
),
278 wpas_update_mbo_connect_params(wpa_s
);
283 static int wpa_non_pref_chan_is_eq(struct wpa_mbo_non_pref_channel
*a
,
284 struct wpa_mbo_non_pref_channel
*b
)
286 return a
->oper_class
== b
->oper_class
&& a
->chan
== b
->chan
;
291 * wpa_non_pref_chan_cmp - Compare two channels for sorting
293 * In MBO IE non-preferred channel subelement we can put many channels in an
294 * attribute if they are in the same operating class and have the same
295 * preference and reason. To make it easy for the functions that build
296 * the IE attributes and WNM Request subelements, save the channels sorted
297 * by their oper_class and reason.
299 static int wpa_non_pref_chan_cmp(const void *_a
, const void *_b
)
301 const struct wpa_mbo_non_pref_channel
*a
= _a
, *b
= _b
;
303 if (a
->oper_class
!= b
->oper_class
)
304 return (int) a
->oper_class
- (int) b
->oper_class
;
305 if (a
->reason
!= b
->reason
)
306 return (int) a
->reason
- (int) b
->reason
;
307 return (int) a
->preference
- (int) b
->preference
;
311 int wpas_mbo_update_non_pref_chan(struct wpa_supplicant
*wpa_s
,
312 const char *non_pref_chan
)
314 char *cmd
, *token
, *context
= NULL
;
315 struct wpa_mbo_non_pref_channel
*chans
= NULL
, *tmp_chans
;
316 size_t num
= 0, size
= 0;
319 wpa_printf(MSG_DEBUG
, "MBO: Update non-preferred channels, non_pref_chan=%s",
320 non_pref_chan
? non_pref_chan
: "N/A");
323 * The shortest channel configuration is 7 characters - 3 colons and
326 if (!non_pref_chan
|| os_strlen(non_pref_chan
) < 7)
329 cmd
= os_strdup(non_pref_chan
);
333 while ((token
= str_token(cmd
, " ", &context
))) {
334 struct wpa_mbo_non_pref_channel
*chan
;
336 unsigned int _oper_class
;
338 unsigned int _preference
;
339 unsigned int _reason
;
342 size
= size
? size
* 2 : 1;
343 tmp_chans
= os_realloc_array(chans
, size
,
346 wpa_printf(MSG_ERROR
,
347 "Couldn't reallocate non_pref_chan");
355 ret
= sscanf(token
, "%u:%u:%u:%u", &_oper_class
,
356 &_chan
, &_preference
, &_reason
);
358 _oper_class
> 255 || _chan
> 255 ||
359 _preference
> 255 || _reason
> 65535 ) {
360 wpa_printf(MSG_ERROR
, "Invalid non-pref chan input %s",
364 chan
->oper_class
= _oper_class
;
366 chan
->preference
= _preference
;
367 chan
->reason
= _reason
;
369 if (wpas_mbo_validate_non_pref_chan(chan
->oper_class
,
370 chan
->chan
, chan
->reason
)) {
371 wpa_printf(MSG_ERROR
,
372 "Invalid non_pref_chan: oper class %d chan %d reason %d",
373 chan
->oper_class
, chan
->chan
, chan
->reason
);
377 for (i
= 0; i
< num
; i
++)
378 if (wpa_non_pref_chan_is_eq(chan
, &chans
[i
]))
381 wpa_printf(MSG_ERROR
,
382 "oper class %d chan %d is duplicated",
383 chan
->oper_class
, chan
->chan
);
393 qsort(chans
, num
, sizeof(struct wpa_mbo_non_pref_channel
),
394 wpa_non_pref_chan_cmp
);
398 os_free(wpa_s
->non_pref_chan
);
399 wpa_s
->non_pref_chan
= chans
;
400 wpa_s
->non_pref_chan_num
= num
;
401 wpas_mbo_non_pref_chan_changed(wpa_s
);
412 void wpas_mbo_scan_ie(struct wpa_supplicant
*wpa_s
, struct wpabuf
*ie
)
416 wpabuf_put_u8(ie
, WLAN_EID_VENDOR_SPECIFIC
);
417 len
= wpabuf_put(ie
, 1);
419 wpabuf_put_be24(ie
, OUI_WFA
);
420 wpabuf_put_u8(ie
, MBO_OUI_TYPE
);
422 wpabuf_put_u8(ie
, MBO_ATTR_ID_CELL_DATA_CAPA
);
423 wpabuf_put_u8(ie
, 1);
424 wpabuf_put_u8(ie
, wpa_s
->conf
->mbo_cell_capa
);
425 if (wpa_s
->enable_oce
& OCE_STA
) {
426 wpabuf_put_u8(ie
, OCE_ATTR_ID_CAPA_IND
);
427 wpabuf_put_u8(ie
, 1);
428 wpabuf_put_u8(ie
, OCE_RELEASE
);
430 *len
= (u8
*) wpabuf_put(ie
, 0) - len
- 1;
434 void wpas_mbo_ie_trans_req(struct wpa_supplicant
*wpa_s
, const u8
*mbo_ie
,
437 const u8
*pos
, *cell_pref
= NULL
;
439 u16 disallowed_sec
= 0;
441 if (len
<= 4 || WPA_GET_BE24(mbo_ie
) != OUI_WFA
||
442 mbo_ie
[3] != MBO_OUI_TYPE
)
457 case MBO_ATTR_ID_CELL_DATA_PREF
:
461 if (wpa_s
->conf
->mbo_cell_capa
==
462 MBO_CELL_CAPA_AVAILABLE
)
465 wpa_printf(MSG_DEBUG
,
466 "MBO: Station does not support Cellular data connection");
468 case MBO_ATTR_ID_TRANSITION_REASON
:
472 wpa_s
->wnm_mbo_trans_reason_present
= 1;
473 wpa_s
->wnm_mbo_transition_reason
= *pos
;
475 case MBO_ATTR_ID_ASSOC_RETRY_DELAY
:
479 if (wpa_s
->wnm_mode
&
480 WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED
) {
481 wpa_printf(MSG_DEBUG
,
482 "MBO: Unexpected association retry delay, BSS is terminating");
484 } else if (wpa_s
->wnm_mode
&
485 WNM_BSS_TM_REQ_DISASSOC_IMMINENT
) {
486 disallowed_sec
= WPA_GET_LE16(pos
);
487 wpa_printf(MSG_DEBUG
,
488 "MBO: Association retry delay: %u",
491 wpa_printf(MSG_DEBUG
,
492 "MBO: Association retry delay attribute not in disassoc imminent mode");
496 case MBO_ATTR_ID_AP_CAPA_IND
:
497 case MBO_ATTR_ID_NON_PREF_CHAN_REPORT
:
498 case MBO_ATTR_ID_CELL_DATA_CAPA
:
499 case MBO_ATTR_ID_ASSOC_DISALLOW
:
500 case MBO_ATTR_ID_TRANSITION_REJECT_REASON
:
501 wpa_printf(MSG_DEBUG
,
502 "MBO: Attribute %d should not be included in BTM Request frame",
506 wpa_printf(MSG_DEBUG
, "MBO: Unknown attribute id %u",
516 wpa_msg(wpa_s
, MSG_INFO
, MBO_CELL_PREFERENCE
"preference=%u",
519 if (wpa_s
->wnm_mbo_trans_reason_present
)
520 wpa_msg(wpa_s
, MSG_INFO
, MBO_TRANSITION_REASON
"reason=%u",
521 wpa_s
->wnm_mbo_transition_reason
);
523 if (disallowed_sec
&& wpa_s
->current_bss
)
524 wpa_bss_tmp_disallow(wpa_s
, wpa_s
->current_bss
->bssid
,
529 wpa_printf(MSG_DEBUG
, "MBO IE parsing failed (id=%u len=%u left=%zu)",
534 size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant
*wpa_s
, u8
*pos
,
536 enum mbo_transition_reject_reason reason
)
540 reject_attr
[0] = MBO_ATTR_ID_TRANSITION_REJECT_REASON
;
542 reject_attr
[2] = reason
;
544 return mbo_add_ie(pos
, len
, reject_attr
, sizeof(reject_attr
));
548 void wpas_mbo_update_cell_capa(struct wpa_supplicant
*wpa_s
, u8 mbo_cell_capa
)
552 if (wpa_s
->conf
->mbo_cell_capa
== mbo_cell_capa
) {
553 wpa_printf(MSG_DEBUG
,
554 "MBO: Cellular capability already set to %u",
559 wpa_s
->conf
->mbo_cell_capa
= mbo_cell_capa
;
561 cell_capa
[0] = WLAN_EID_VENDOR_SPECIFIC
;
562 cell_capa
[1] = 5; /* Length */
563 WPA_PUT_BE24(cell_capa
+ 2, OUI_WFA
);
564 cell_capa
[5] = MBO_ATTR_ID_CELL_DATA_CAPA
;
565 cell_capa
[6] = mbo_cell_capa
;
567 wpas_mbo_send_wnm_notification(wpa_s
, cell_capa
, 7);
568 wpa_supplicant_set_default_scan_ies(wpa_s
);
569 wpas_update_mbo_connect_params(wpa_s
);
573 struct wpabuf
* mbo_build_anqp_buf(struct wpa_supplicant
*wpa_s
,
574 struct wpa_bss
*bss
, u32 mbo_subtypes
)
576 struct wpabuf
*anqp_buf
;
580 if (!wpa_bss_get_vendor_ie(bss
, MBO_IE_VENDOR_TYPE
)) {
581 wpa_printf(MSG_INFO
, "MBO: " MACSTR
582 " does not support MBO - cannot request MBO ANQP elements from it",
583 MAC2STR(bss
->bssid
));
587 /* Allocate size for the maximum case - all MBO subtypes are set */
588 anqp_buf
= wpabuf_alloc(9 + MAX_MBO_ANQP_SUBTYPE
);
592 len_pos
= gas_anqp_add_element(anqp_buf
, ANQP_VENDOR_SPECIFIC
);
593 wpabuf_put_be24(anqp_buf
, OUI_WFA
);
594 wpabuf_put_u8(anqp_buf
, MBO_ANQP_OUI_TYPE
);
596 wpabuf_put_u8(anqp_buf
, MBO_ANQP_SUBTYPE_QUERY_LIST
);
598 /* The first valid MBO subtype is 1 */
599 for (i
= 1; i
<= MAX_MBO_ANQP_SUBTYPE
; i
++) {
600 if (mbo_subtypes
& BIT(i
))
601 wpabuf_put_u8(anqp_buf
, i
);
604 gas_anqp_set_element_len(anqp_buf
, len_pos
);
610 void mbo_parse_rx_anqp_resp(struct wpa_supplicant
*wpa_s
,
611 struct wpa_bss
*bss
, const u8
*sa
,
612 const u8
*data
, size_t slen
)
614 const u8
*pos
= data
;
624 case MBO_ANQP_SUBTYPE_CELL_CONN_PREF
:
627 wpa_msg(wpa_s
, MSG_INFO
, RX_MBO_ANQP MACSTR
628 " cell_conn_pref=%u", MAC2STR(sa
), *pos
);
631 wpa_printf(MSG_DEBUG
, "MBO: Unsupported ANQP subtype %u",