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"
18 #include "rsn_supp/wpa.h"
20 #include "wpa_supplicant_i.h"
25 /* type + length + oui + oui type */
26 #define MBO_IE_HEADER 6
29 static int wpas_mbo_validate_non_pref_chan(u8 oper_class
, u8 chan
, u8 reason
)
31 if (reason
> MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE
)
34 /* Only checking the validity of the channel and oper_class */
35 if (ieee80211_chan_to_freq(NULL
, oper_class
, chan
) == -1)
42 const u8
* mbo_attr_from_mbo_ie(const u8
*mbo_ie
, enum mbo_attr_id attr
)
45 u8 ie_len
= mbo_ie
[1];
47 if (ie_len
< MBO_IE_HEADER
- 2)
49 mbo
= mbo_ie
+ MBO_IE_HEADER
;
51 return get_ie(mbo
, 2 + ie_len
- MBO_IE_HEADER
, attr
);
55 const u8
* mbo_get_attr_from_ies(const u8
*ies
, size_t ies_len
,
56 enum mbo_attr_id attr
)
60 mbo_ie
= get_vendor_ie(ies
, ies_len
, MBO_IE_VENDOR_TYPE
);
64 return mbo_attr_from_mbo_ie(mbo_ie
, attr
);
68 const u8
* wpas_mbo_get_bss_attr(struct wpa_bss
*bss
, enum mbo_attr_id attr
)
75 mbo
= wpa_bss_get_vendor_ie(bss
, MBO_IE_VENDOR_TYPE
);
79 end
= mbo
+ 2 + mbo
[1];
82 return get_ie(mbo
, end
- mbo
, attr
);
86 void wpas_mbo_check_pmf(struct wpa_supplicant
*wpa_s
, struct wpa_bss
*bss
,
87 struct wpa_ssid
*ssid
)
89 const u8
*rsne
, *mbo
, *oce
;
90 struct wpa_ie_data ie
;
92 wpa_s
->disable_mbo_oce
= 0;
95 mbo
= wpas_mbo_get_bss_attr(bss
, MBO_ATTR_ID_AP_CAPA_IND
);
96 oce
= wpas_mbo_get_bss_attr(bss
, OCE_ATTR_ID_CAPA_IND
);
99 if (oce
&& oce
[1] >= 1 && (oce
[2] & OCE_IS_STA_CFON
))
100 return; /* STA-CFON is not required to enable PMF */
101 rsne
= wpa_bss_get_ie(bss
, WLAN_EID_RSN
);
102 if (!rsne
|| wpa_parse_wpa_ie(rsne
, 2 + rsne
[1], &ie
) < 0)
103 return; /* AP is not using RSN */
105 if (!(ie
.capabilities
& WPA_CAPABILITY_MFPC
))
106 wpa_s
->disable_mbo_oce
= 1; /* AP uses RSN without PMF */
107 if (wpas_get_ssid_pmf(wpa_s
, ssid
) == NO_MGMT_FRAME_PROTECTION
)
108 wpa_s
->disable_mbo_oce
= 1; /* STA uses RSN without PMF */
109 if (wpa_s
->disable_mbo_oce
)
111 "MBO: Disable MBO/OCE due to misbehaving AP not having enabled PMF");
115 static void wpas_mbo_non_pref_chan_attr_body(struct wpa_supplicant
*wpa_s
,
121 wpabuf_put_u8(mbo
, wpa_s
->non_pref_chan
[start
].oper_class
);
123 for (i
= start
; i
< end
; i
++)
124 wpabuf_put_u8(mbo
, wpa_s
->non_pref_chan
[i
].chan
);
126 wpabuf_put_u8(mbo
, wpa_s
->non_pref_chan
[start
].preference
);
127 wpabuf_put_u8(mbo
, wpa_s
->non_pref_chan
[start
].reason
);
131 static void wpas_mbo_non_pref_chan_attr_hdr(struct wpabuf
*mbo
, size_t size
)
133 wpabuf_put_u8(mbo
, MBO_ATTR_ID_NON_PREF_CHAN_REPORT
);
134 wpabuf_put_u8(mbo
, size
); /* Length */
138 static void wpas_mbo_non_pref_chan_attr(struct wpa_supplicant
*wpa_s
,
139 struct wpabuf
*mbo
, u8 start
, u8 end
)
141 size_t size
= end
- start
+ 3;
143 if (size
+ 2 > wpabuf_tailroom(mbo
))
146 wpas_mbo_non_pref_chan_attr_hdr(mbo
, size
);
147 wpas_mbo_non_pref_chan_attr_body(wpa_s
, mbo
, start
, end
);
151 static void wpas_mbo_non_pref_chan_subelem_hdr(struct wpabuf
*mbo
, u8 len
)
153 wpabuf_put_u8(mbo
, WLAN_EID_VENDOR_SPECIFIC
);
154 wpabuf_put_u8(mbo
, len
); /* Length */
155 wpabuf_put_be24(mbo
, OUI_WFA
);
156 wpabuf_put_u8(mbo
, MBO_ATTR_ID_NON_PREF_CHAN_REPORT
);
160 static void wpas_mbo_non_pref_chan_subelement(struct wpa_supplicant
*wpa_s
,
161 struct wpabuf
*mbo
, u8 start
,
164 size_t size
= end
- start
+ 7;
166 if (size
+ 2 > wpabuf_tailroom(mbo
))
169 wpas_mbo_non_pref_chan_subelem_hdr(mbo
, size
);
170 wpas_mbo_non_pref_chan_attr_body(wpa_s
, mbo
, start
, end
);
174 static void wpas_mbo_non_pref_chan_attrs(struct wpa_supplicant
*wpa_s
,
175 struct wpabuf
*mbo
, int subelement
)
178 struct wpa_mbo_non_pref_channel
*start_pref
;
180 if (!wpa_s
->non_pref_chan
|| !wpa_s
->non_pref_chan_num
) {
182 wpas_mbo_non_pref_chan_subelem_hdr(mbo
, 4);
184 wpas_mbo_non_pref_chan_attr_hdr(mbo
, 0);
187 start_pref
= &wpa_s
->non_pref_chan
[0];
189 for (i
= 1; i
<= wpa_s
->non_pref_chan_num
; i
++) {
190 struct wpa_mbo_non_pref_channel
*non_pref
= NULL
;
192 if (i
< wpa_s
->non_pref_chan_num
)
193 non_pref
= &wpa_s
->non_pref_chan
[i
];
195 non_pref
->oper_class
!= start_pref
->oper_class
||
196 non_pref
->reason
!= start_pref
->reason
||
197 non_pref
->preference
!= start_pref
->preference
) {
199 wpas_mbo_non_pref_chan_subelement(wpa_s
, mbo
,
202 wpas_mbo_non_pref_chan_attr(wpa_s
, mbo
, start
,
209 start_pref
= non_pref
;
215 int wpas_mbo_ie(struct wpa_supplicant
*wpa_s
, u8
*buf
, size_t len
,
221 if (len
< MBO_IE_HEADER
+ 3 + 7 +
222 ((wpa_s
->enable_oce
& OCE_STA
) ? 3 : 0))
225 /* Leave room for the MBO IE header */
226 mbo
= wpabuf_alloc(len
- MBO_IE_HEADER
);
230 /* Add non-preferred channels attribute */
231 wpas_mbo_non_pref_chan_attrs(wpa_s
, mbo
, 0);
234 * Send cellular capabilities attribute even if AP does not advertise
235 * cellular capabilities.
237 wpabuf_put_u8(mbo
, MBO_ATTR_ID_CELL_DATA_CAPA
);
238 wpabuf_put_u8(mbo
, 1);
239 wpabuf_put_u8(mbo
, wpa_s
->conf
->mbo_cell_capa
);
241 /* Add OCE capability indication attribute if OCE is enabled */
242 if ((wpa_s
->enable_oce
& OCE_STA
) && add_oce_capa
) {
243 wpabuf_put_u8(mbo
, OCE_ATTR_ID_CAPA_IND
);
244 wpabuf_put_u8(mbo
, 1);
245 wpabuf_put_u8(mbo
, OCE_RELEASE
);
248 res
= mbo_add_ie(buf
, len
, wpabuf_head_u8(mbo
), wpabuf_len(mbo
));
250 wpa_printf(MSG_ERROR
, "Failed to add MBO/OCE IE");
257 static void wpas_mbo_send_wnm_notification(struct wpa_supplicant
*wpa_s
,
258 const u8
*data
, size_t len
)
264 * Send WNM-Notification Request frame only in case of a change in
265 * non-preferred channels list during association, if the AP supports
268 if (wpa_s
->wpa_state
!= WPA_COMPLETED
|| !wpa_s
->current_bss
||
269 !wpa_bss_get_vendor_ie(wpa_s
->current_bss
, MBO_IE_VENDOR_TYPE
))
272 buf
= wpabuf_alloc(4 + len
);
276 wpabuf_put_u8(buf
, WLAN_ACTION_WNM
);
277 wpabuf_put_u8(buf
, WNM_NOTIFICATION_REQ
);
278 wpa_s
->mbo_wnm_token
++;
279 if (wpa_s
->mbo_wnm_token
== 0)
280 wpa_s
->mbo_wnm_token
++;
281 wpabuf_put_u8(buf
, wpa_s
->mbo_wnm_token
);
282 wpabuf_put_u8(buf
, WLAN_EID_VENDOR_SPECIFIC
); /* Type */
284 wpabuf_put_data(buf
, data
, len
);
286 res
= wpa_drv_send_action(wpa_s
, wpa_s
->assoc_freq
, 0, wpa_s
->bssid
,
287 wpa_s
->own_addr
, wpa_s
->bssid
,
288 wpabuf_head(buf
), wpabuf_len(buf
), 0);
290 wpa_printf(MSG_DEBUG
,
291 "Failed to send WNM-Notification Request frame with non-preferred channel list");
297 static void wpas_mbo_non_pref_chan_changed(struct wpa_supplicant
*wpa_s
)
301 buf
= wpabuf_alloc(512);
305 wpas_mbo_non_pref_chan_attrs(wpa_s
, buf
, 1);
306 wpas_mbo_send_wnm_notification(wpa_s
, wpabuf_head_u8(buf
),
308 wpas_update_mbo_connect_params(wpa_s
);
313 static int wpa_non_pref_chan_is_eq(struct wpa_mbo_non_pref_channel
*a
,
314 struct wpa_mbo_non_pref_channel
*b
)
316 return a
->oper_class
== b
->oper_class
&& a
->chan
== b
->chan
;
321 * wpa_non_pref_chan_cmp - Compare two channels for sorting
323 * In MBO IE non-preferred channel subelement we can put many channels in an
324 * attribute if they are in the same operating class and have the same
325 * preference and reason. To make it easy for the functions that build
326 * the IE attributes and WNM Request subelements, save the channels sorted
327 * by their oper_class and reason.
329 static int wpa_non_pref_chan_cmp(const void *_a
, const void *_b
)
331 const struct wpa_mbo_non_pref_channel
*a
= _a
, *b
= _b
;
333 if (a
->oper_class
!= b
->oper_class
)
334 return (int) a
->oper_class
- (int) b
->oper_class
;
335 if (a
->reason
!= b
->reason
)
336 return (int) a
->reason
- (int) b
->reason
;
337 return (int) a
->preference
- (int) b
->preference
;
341 int wpas_mbo_update_non_pref_chan(struct wpa_supplicant
*wpa_s
,
342 const char *non_pref_chan
)
344 char *cmd
, *token
, *context
= NULL
;
345 struct wpa_mbo_non_pref_channel
*chans
= NULL
, *tmp_chans
;
346 size_t num
= 0, size
= 0;
349 wpa_printf(MSG_DEBUG
, "MBO: Update non-preferred channels, non_pref_chan=%s",
350 non_pref_chan
? non_pref_chan
: "N/A");
353 * The shortest channel configuration is 7 characters - 3 colons and
356 if (!non_pref_chan
|| os_strlen(non_pref_chan
) < 7)
359 cmd
= os_strdup(non_pref_chan
);
363 while ((token
= str_token(cmd
, " ", &context
))) {
364 struct wpa_mbo_non_pref_channel
*chan
;
366 unsigned int _oper_class
;
368 unsigned int _preference
;
369 unsigned int _reason
;
372 size
= size
? size
* 2 : 1;
373 tmp_chans
= os_realloc_array(chans
, size
,
376 wpa_printf(MSG_ERROR
,
377 "Couldn't reallocate non_pref_chan");
385 ret
= sscanf(token
, "%u:%u:%u:%u", &_oper_class
,
386 &_chan
, &_preference
, &_reason
);
388 _oper_class
> 255 || _chan
> 255 ||
389 _preference
> 255 || _reason
> 65535 ) {
390 wpa_printf(MSG_ERROR
, "Invalid non-pref chan input %s",
394 chan
->oper_class
= _oper_class
;
396 chan
->preference
= _preference
;
397 chan
->reason
= _reason
;
399 if (wpas_mbo_validate_non_pref_chan(chan
->oper_class
,
400 chan
->chan
, chan
->reason
)) {
401 wpa_printf(MSG_ERROR
,
402 "Invalid non_pref_chan: oper class %d chan %d reason %d",
403 chan
->oper_class
, chan
->chan
, chan
->reason
);
407 for (i
= 0; i
< num
; i
++)
408 if (wpa_non_pref_chan_is_eq(chan
, &chans
[i
]))
411 wpa_printf(MSG_ERROR
,
412 "oper class %d chan %d is duplicated",
413 chan
->oper_class
, chan
->chan
);
423 qsort(chans
, num
, sizeof(struct wpa_mbo_non_pref_channel
),
424 wpa_non_pref_chan_cmp
);
428 os_free(wpa_s
->non_pref_chan
);
429 wpa_s
->non_pref_chan
= chans
;
430 wpa_s
->non_pref_chan_num
= num
;
431 wpas_mbo_non_pref_chan_changed(wpa_s
);
442 void wpas_mbo_scan_ie(struct wpa_supplicant
*wpa_s
, struct wpabuf
*ie
)
446 wpabuf_put_u8(ie
, WLAN_EID_VENDOR_SPECIFIC
);
447 len
= wpabuf_put(ie
, 1);
449 wpabuf_put_be24(ie
, OUI_WFA
);
450 wpabuf_put_u8(ie
, MBO_OUI_TYPE
);
452 wpabuf_put_u8(ie
, MBO_ATTR_ID_CELL_DATA_CAPA
);
453 wpabuf_put_u8(ie
, 1);
454 wpabuf_put_u8(ie
, wpa_s
->conf
->mbo_cell_capa
);
455 if (wpa_s
->enable_oce
& OCE_STA
) {
456 wpabuf_put_u8(ie
, OCE_ATTR_ID_CAPA_IND
);
457 wpabuf_put_u8(ie
, 1);
458 wpabuf_put_u8(ie
, OCE_RELEASE
);
460 *len
= (u8
*) wpabuf_put(ie
, 0) - len
- 1;
464 void wpas_mbo_ie_trans_req(struct wpa_supplicant
*wpa_s
, const u8
*mbo_ie
,
467 const u8
*pos
, *cell_pref
= NULL
;
469 u16 disallowed_sec
= 0;
471 if (len
<= 4 || WPA_GET_BE24(mbo_ie
) != OUI_WFA
||
472 mbo_ie
[3] != MBO_OUI_TYPE
)
487 case MBO_ATTR_ID_CELL_DATA_PREF
:
491 if (wpa_s
->conf
->mbo_cell_capa
==
492 MBO_CELL_CAPA_AVAILABLE
)
495 wpa_printf(MSG_DEBUG
,
496 "MBO: Station does not support Cellular data connection");
498 case MBO_ATTR_ID_TRANSITION_REASON
:
502 wpa_s
->wnm_mbo_trans_reason_present
= 1;
503 wpa_s
->wnm_mbo_transition_reason
= *pos
;
505 case MBO_ATTR_ID_ASSOC_RETRY_DELAY
:
509 if (wpa_s
->wnm_mode
&
510 WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED
) {
511 wpa_printf(MSG_DEBUG
,
512 "MBO: Unexpected association retry delay, BSS is terminating");
514 } else if (wpa_s
->wnm_mode
&
515 WNM_BSS_TM_REQ_DISASSOC_IMMINENT
) {
516 disallowed_sec
= WPA_GET_LE16(pos
);
517 wpa_printf(MSG_DEBUG
,
518 "MBO: Association retry delay: %u",
521 wpa_printf(MSG_DEBUG
,
522 "MBO: Association retry delay attribute not in disassoc imminent mode");
526 case MBO_ATTR_ID_AP_CAPA_IND
:
527 case MBO_ATTR_ID_NON_PREF_CHAN_REPORT
:
528 case MBO_ATTR_ID_CELL_DATA_CAPA
:
529 case MBO_ATTR_ID_ASSOC_DISALLOW
:
530 case MBO_ATTR_ID_TRANSITION_REJECT_REASON
:
531 wpa_printf(MSG_DEBUG
,
532 "MBO: Attribute %d should not be included in BTM Request frame",
536 wpa_printf(MSG_DEBUG
, "MBO: Unknown attribute id %u",
546 wpa_msg(wpa_s
, MSG_INFO
, MBO_CELL_PREFERENCE
"preference=%u",
549 if (wpa_s
->wnm_mbo_trans_reason_present
)
550 wpa_msg(wpa_s
, MSG_INFO
, MBO_TRANSITION_REASON
"reason=%u",
551 wpa_s
->wnm_mbo_transition_reason
);
553 if (disallowed_sec
&& wpa_s
->current_bss
)
554 wpa_bss_tmp_disallow(wpa_s
, wpa_s
->current_bss
->bssid
,
559 wpa_printf(MSG_DEBUG
, "MBO IE parsing failed (id=%u len=%u left=%zu)",
564 size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant
*wpa_s
, u8
*pos
,
566 enum mbo_transition_reject_reason reason
)
570 reject_attr
[0] = MBO_ATTR_ID_TRANSITION_REJECT_REASON
;
572 reject_attr
[2] = reason
;
574 return mbo_add_ie(pos
, len
, reject_attr
, sizeof(reject_attr
));
578 void wpas_mbo_update_cell_capa(struct wpa_supplicant
*wpa_s
, u8 mbo_cell_capa
)
582 if (wpa_s
->conf
->mbo_cell_capa
== mbo_cell_capa
) {
583 wpa_printf(MSG_DEBUG
,
584 "MBO: Cellular capability already set to %u",
589 wpa_s
->conf
->mbo_cell_capa
= mbo_cell_capa
;
591 cell_capa
[0] = WLAN_EID_VENDOR_SPECIFIC
;
592 cell_capa
[1] = 5; /* Length */
593 WPA_PUT_BE24(cell_capa
+ 2, OUI_WFA
);
594 cell_capa
[5] = MBO_ATTR_ID_CELL_DATA_CAPA
;
595 cell_capa
[6] = mbo_cell_capa
;
597 wpas_mbo_send_wnm_notification(wpa_s
, cell_capa
, 7);
598 wpa_supplicant_set_default_scan_ies(wpa_s
);
599 wpas_update_mbo_connect_params(wpa_s
);
603 struct wpabuf
* mbo_build_anqp_buf(struct wpa_supplicant
*wpa_s
,
604 struct wpa_bss
*bss
, u32 mbo_subtypes
)
606 struct wpabuf
*anqp_buf
;
610 if (!wpa_bss_get_vendor_ie(bss
, MBO_IE_VENDOR_TYPE
)) {
611 wpa_printf(MSG_INFO
, "MBO: " MACSTR
612 " does not support MBO - cannot request MBO ANQP elements from it",
613 MAC2STR(bss
->bssid
));
617 /* Allocate size for the maximum case - all MBO subtypes are set */
618 anqp_buf
= wpabuf_alloc(9 + MAX_MBO_ANQP_SUBTYPE
);
622 len_pos
= gas_anqp_add_element(anqp_buf
, ANQP_VENDOR_SPECIFIC
);
623 wpabuf_put_be24(anqp_buf
, OUI_WFA
);
624 wpabuf_put_u8(anqp_buf
, MBO_ANQP_OUI_TYPE
);
626 wpabuf_put_u8(anqp_buf
, MBO_ANQP_SUBTYPE_QUERY_LIST
);
628 /* The first valid MBO subtype is 1 */
629 for (i
= 1; i
<= MAX_MBO_ANQP_SUBTYPE
; i
++) {
630 if (mbo_subtypes
& BIT(i
))
631 wpabuf_put_u8(anqp_buf
, i
);
634 gas_anqp_set_element_len(anqp_buf
, len_pos
);
640 void mbo_parse_rx_anqp_resp(struct wpa_supplicant
*wpa_s
,
641 struct wpa_bss
*bss
, const u8
*sa
,
642 const u8
*data
, size_t slen
)
644 const u8
*pos
= data
;
654 case MBO_ANQP_SUBTYPE_CELL_CONN_PREF
:
657 wpa_msg(wpa_s
, MSG_INFO
, RX_MBO_ANQP MACSTR
658 " cell_conn_pref=%u", MAC2STR(sa
), *pos
);
661 wpa_printf(MSG_DEBUG
, "MBO: Unsupported ANQP subtype %u",