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
* wpas_mbo_get_bss_attr(struct wpa_bss
*bss
, enum mbo_attr_id attr
)
48 mbo
= wpa_bss_get_vendor_ie(bss
, MBO_IE_VENDOR_TYPE
);
52 end
= mbo
+ 2 + mbo
[1];
55 return get_ie(mbo
, end
- mbo
, attr
);
59 static void wpas_mbo_non_pref_chan_attr_body(struct wpa_supplicant
*wpa_s
,
65 wpabuf_put_u8(mbo
, wpa_s
->non_pref_chan
[start
].oper_class
);
67 for (i
= start
; i
< end
; i
++)
68 wpabuf_put_u8(mbo
, wpa_s
->non_pref_chan
[i
].chan
);
70 wpabuf_put_u8(mbo
, wpa_s
->non_pref_chan
[start
].preference
);
71 wpabuf_put_u8(mbo
, wpa_s
->non_pref_chan
[start
].reason
);
75 static void wpas_mbo_non_pref_chan_attr(struct wpa_supplicant
*wpa_s
,
76 struct wpabuf
*mbo
, u8 start
, u8 end
)
78 size_t size
= end
- start
+ 3;
80 if (size
+ 2 > wpabuf_tailroom(mbo
))
83 wpabuf_put_u8(mbo
, MBO_ATTR_ID_NON_PREF_CHAN_REPORT
);
84 wpabuf_put_u8(mbo
, size
); /* Length */
86 wpas_mbo_non_pref_chan_attr_body(wpa_s
, mbo
, start
, end
);
90 static void wpas_mbo_non_pref_chan_subelem_hdr(struct wpabuf
*mbo
, u8 len
)
92 wpabuf_put_u8(mbo
, WLAN_EID_VENDOR_SPECIFIC
);
93 wpabuf_put_u8(mbo
, len
); /* Length */
94 wpabuf_put_be24(mbo
, OUI_WFA
);
95 wpabuf_put_u8(mbo
, MBO_ATTR_ID_NON_PREF_CHAN_REPORT
);
99 static void wpas_mbo_non_pref_chan_subelement(struct wpa_supplicant
*wpa_s
,
100 struct wpabuf
*mbo
, u8 start
,
103 size_t size
= end
- start
+ 7;
105 if (size
+ 2 > wpabuf_tailroom(mbo
))
108 wpas_mbo_non_pref_chan_subelem_hdr(mbo
, size
);
109 wpas_mbo_non_pref_chan_attr_body(wpa_s
, mbo
, start
, end
);
113 static void wpas_mbo_non_pref_chan_attrs(struct wpa_supplicant
*wpa_s
,
114 struct wpabuf
*mbo
, int subelement
)
117 struct wpa_mbo_non_pref_channel
*start_pref
;
119 if (!wpa_s
->non_pref_chan
|| !wpa_s
->non_pref_chan_num
) {
121 wpas_mbo_non_pref_chan_subelem_hdr(mbo
, 4);
124 start_pref
= &wpa_s
->non_pref_chan
[0];
126 for (i
= 1; i
<= wpa_s
->non_pref_chan_num
; i
++) {
127 struct wpa_mbo_non_pref_channel
*non_pref
= NULL
;
129 if (i
< wpa_s
->non_pref_chan_num
)
130 non_pref
= &wpa_s
->non_pref_chan
[i
];
132 non_pref
->oper_class
!= start_pref
->oper_class
||
133 non_pref
->reason
!= start_pref
->reason
||
134 non_pref
->preference
!= start_pref
->preference
) {
136 wpas_mbo_non_pref_chan_subelement(wpa_s
, mbo
,
139 wpas_mbo_non_pref_chan_attr(wpa_s
, mbo
, start
,
146 start_pref
= non_pref
;
152 int wpas_mbo_ie(struct wpa_supplicant
*wpa_s
, u8
*buf
, size_t len
)
157 if (len
< MBO_IE_HEADER
+ 3 + 7)
160 /* Leave room for the MBO IE header */
161 mbo
= wpabuf_alloc(len
- MBO_IE_HEADER
);
165 /* Add non-preferred channels attribute */
166 wpas_mbo_non_pref_chan_attrs(wpa_s
, mbo
, 0);
169 * Send cellular capabilities attribute even if AP does not advertise
170 * cellular capabilities.
172 wpabuf_put_u8(mbo
, MBO_ATTR_ID_CELL_DATA_CAPA
);
173 wpabuf_put_u8(mbo
, 1);
174 wpabuf_put_u8(mbo
, wpa_s
->conf
->mbo_cell_capa
);
176 res
= mbo_add_ie(buf
, len
, wpabuf_head_u8(mbo
), wpabuf_len(mbo
));
178 wpa_printf(MSG_ERROR
, "Failed to add MBO IE");
185 static void wpas_mbo_send_wnm_notification(struct wpa_supplicant
*wpa_s
,
186 const u8
*data
, size_t len
)
192 * Send WNM-Notification Request frame only in case of a change in
193 * non-preferred channels list during association, if the AP supports
196 if (wpa_s
->wpa_state
!= WPA_COMPLETED
|| !wpa_s
->current_bss
||
197 !wpa_bss_get_vendor_ie(wpa_s
->current_bss
, MBO_IE_VENDOR_TYPE
))
200 buf
= wpabuf_alloc(4 + len
);
204 wpabuf_put_u8(buf
, WLAN_ACTION_WNM
);
205 wpabuf_put_u8(buf
, WNM_NOTIFICATION_REQ
);
206 wpa_s
->mbo_wnm_token
++;
207 if (wpa_s
->mbo_wnm_token
== 0)
208 wpa_s
->mbo_wnm_token
++;
209 wpabuf_put_u8(buf
, wpa_s
->mbo_wnm_token
);
210 wpabuf_put_u8(buf
, WLAN_EID_VENDOR_SPECIFIC
); /* Type */
212 wpabuf_put_data(buf
, data
, len
);
214 res
= wpa_drv_send_action(wpa_s
, wpa_s
->assoc_freq
, 0, wpa_s
->bssid
,
215 wpa_s
->own_addr
, wpa_s
->bssid
,
216 wpabuf_head(buf
), wpabuf_len(buf
), 0);
218 wpa_printf(MSG_DEBUG
,
219 "Failed to send WNM-Notification Request frame with non-preferred channel list");
225 static void wpas_mbo_non_pref_chan_changed(struct wpa_supplicant
*wpa_s
)
229 buf
= wpabuf_alloc(512);
233 wpas_mbo_non_pref_chan_attrs(wpa_s
, buf
, 1);
234 wpas_mbo_send_wnm_notification(wpa_s
, wpabuf_head_u8(buf
),
240 static int wpa_non_pref_chan_is_eq(struct wpa_mbo_non_pref_channel
*a
,
241 struct wpa_mbo_non_pref_channel
*b
)
243 return a
->oper_class
== b
->oper_class
&& a
->chan
== b
->chan
;
248 * wpa_non_pref_chan_cmp - Compare two channels for sorting
250 * In MBO IE non-preferred channel subelement we can put many channels in an
251 * attribute if they are in the same operating class and have the same
252 * preference and reason. To make it easy for the functions that build
253 * the IE attributes and WNM Request subelements, save the channels sorted
254 * by their oper_class and reason.
256 static int wpa_non_pref_chan_cmp(const void *_a
, const void *_b
)
258 const struct wpa_mbo_non_pref_channel
*a
= _a
, *b
= _b
;
260 if (a
->oper_class
!= b
->oper_class
)
261 return a
->oper_class
- b
->oper_class
;
262 if (a
->reason
!= b
->reason
)
263 return a
->reason
- b
->reason
;
264 return a
->preference
- b
->preference
;
268 int wpas_mbo_update_non_pref_chan(struct wpa_supplicant
*wpa_s
,
269 const char *non_pref_chan
)
271 char *cmd
, *token
, *context
= NULL
;
272 struct wpa_mbo_non_pref_channel
*chans
= NULL
, *tmp_chans
;
273 size_t num
= 0, size
= 0;
276 wpa_printf(MSG_DEBUG
, "MBO: Update non-preferred channels, non_pref_chan=%s",
277 non_pref_chan
? non_pref_chan
: "N/A");
280 * The shortest channel configuration is 7 characters - 3 colons and
283 if (!non_pref_chan
|| os_strlen(non_pref_chan
) < 7)
286 cmd
= os_strdup(non_pref_chan
);
290 while ((token
= str_token(cmd
, " ", &context
))) {
291 struct wpa_mbo_non_pref_channel
*chan
;
293 unsigned int _oper_class
;
295 unsigned int _preference
;
296 unsigned int _reason
;
299 size
= size
? size
* 2 : 1;
300 tmp_chans
= os_realloc_array(chans
, size
,
303 wpa_printf(MSG_ERROR
,
304 "Couldn't reallocate non_pref_chan");
312 ret
= sscanf(token
, "%u:%u:%u:%u", &_oper_class
,
313 &_chan
, &_preference
, &_reason
);
315 _oper_class
> 255 || _chan
> 255 ||
316 _preference
> 255 || _reason
> 65535 ) {
317 wpa_printf(MSG_ERROR
, "Invalid non-pref chan input %s",
321 chan
->oper_class
= _oper_class
;
323 chan
->preference
= _preference
;
324 chan
->reason
= _reason
;
326 if (wpas_mbo_validate_non_pref_chan(chan
->oper_class
,
327 chan
->chan
, chan
->reason
)) {
328 wpa_printf(MSG_ERROR
,
329 "Invalid non_pref_chan: oper class %d chan %d reason %d",
330 chan
->oper_class
, chan
->chan
, chan
->reason
);
334 for (i
= 0; i
< num
; i
++)
335 if (wpa_non_pref_chan_is_eq(chan
, &chans
[i
]))
338 wpa_printf(MSG_ERROR
,
339 "oper class %d chan %d is duplicated",
340 chan
->oper_class
, chan
->chan
);
350 qsort(chans
, num
, sizeof(struct wpa_mbo_non_pref_channel
),
351 wpa_non_pref_chan_cmp
);
355 os_free(wpa_s
->non_pref_chan
);
356 wpa_s
->non_pref_chan
= chans
;
357 wpa_s
->non_pref_chan_num
= num
;
358 wpas_mbo_non_pref_chan_changed(wpa_s
);
369 void wpas_mbo_scan_ie(struct wpa_supplicant
*wpa_s
, struct wpabuf
*ie
)
371 wpabuf_put_u8(ie
, WLAN_EID_VENDOR_SPECIFIC
);
372 wpabuf_put_u8(ie
, 7);
373 wpabuf_put_be24(ie
, OUI_WFA
);
374 wpabuf_put_u8(ie
, MBO_OUI_TYPE
);
376 wpabuf_put_u8(ie
, MBO_ATTR_ID_CELL_DATA_CAPA
);
377 wpabuf_put_u8(ie
, 1);
378 wpabuf_put_u8(ie
, wpa_s
->conf
->mbo_cell_capa
);
382 void wpas_mbo_ie_trans_req(struct wpa_supplicant
*wpa_s
, const u8
*mbo_ie
,
385 const u8
*pos
, *cell_pref
= NULL
;
387 u16 disallowed_sec
= 0;
389 if (len
<= 4 || WPA_GET_BE24(mbo_ie
) != OUI_WFA
||
390 mbo_ie
[3] != MBO_OUI_TYPE
)
405 case MBO_ATTR_ID_CELL_DATA_PREF
:
409 if (wpa_s
->conf
->mbo_cell_capa
==
410 MBO_CELL_CAPA_AVAILABLE
)
413 wpa_printf(MSG_DEBUG
,
414 "MBO: Station does not support Cellular data connection");
416 case MBO_ATTR_ID_TRANSITION_REASON
:
420 wpa_s
->wnm_mbo_trans_reason_present
= 1;
421 wpa_s
->wnm_mbo_transition_reason
= *pos
;
423 case MBO_ATTR_ID_ASSOC_RETRY_DELAY
:
427 if (wpa_s
->wnm_mode
&
428 WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED
) {
429 wpa_printf(MSG_DEBUG
,
430 "MBO: Unexpected association retry delay, BSS is terminating");
432 } else if (wpa_s
->wnm_mode
&
433 WNM_BSS_TM_REQ_DISASSOC_IMMINENT
) {
434 disallowed_sec
= WPA_GET_LE16(pos
);
436 wpa_printf(MSG_DEBUG
,
437 "MBO: Association retry delay attribute not in disassoc imminent mode");
441 case MBO_ATTR_ID_AP_CAPA_IND
:
442 case MBO_ATTR_ID_NON_PREF_CHAN_REPORT
:
443 case MBO_ATTR_ID_CELL_DATA_CAPA
:
444 case MBO_ATTR_ID_ASSOC_DISALLOW
:
445 case MBO_ATTR_ID_TRANSITION_REJECT_REASON
:
446 wpa_printf(MSG_DEBUG
,
447 "MBO: Attribute %d should not be included in BTM Request frame",
451 wpa_printf(MSG_DEBUG
, "MBO: Unknown attribute id %u",
461 wpa_msg(wpa_s
, MSG_INFO
, MBO_CELL_PREFERENCE
"preference=%u",
464 if (wpa_s
->wnm_mbo_trans_reason_present
)
465 wpa_msg(wpa_s
, MSG_INFO
, MBO_TRANSITION_REASON
"reason=%u",
466 wpa_s
->wnm_mbo_transition_reason
);
468 if (disallowed_sec
&& wpa_s
->current_bss
)
469 wpa_bss_tmp_disallow(wpa_s
, wpa_s
->current_bss
->bssid
,
474 wpa_printf(MSG_DEBUG
, "MBO IE parsing failed (id=%u len=%u left=%zu)",
479 size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant
*wpa_s
, u8
*pos
,
481 enum mbo_transition_reject_reason reason
)
485 reject_attr
[0] = MBO_ATTR_ID_TRANSITION_REJECT_REASON
;
487 reject_attr
[2] = reason
;
489 return mbo_add_ie(pos
, len
, reject_attr
, sizeof(reject_attr
));
493 void wpas_mbo_update_cell_capa(struct wpa_supplicant
*wpa_s
, u8 mbo_cell_capa
)
497 if (wpa_s
->conf
->mbo_cell_capa
== mbo_cell_capa
) {
498 wpa_printf(MSG_DEBUG
,
499 "MBO: Cellular capability already set to %u",
504 wpa_s
->conf
->mbo_cell_capa
= mbo_cell_capa
;
506 cell_capa
[0] = WLAN_EID_VENDOR_SPECIFIC
;
507 cell_capa
[1] = 5; /* Length */
508 WPA_PUT_BE24(cell_capa
+ 2, OUI_WFA
);
509 cell_capa
[5] = MBO_ATTR_ID_CELL_DATA_CAPA
;
510 cell_capa
[6] = mbo_cell_capa
;
512 wpas_mbo_send_wnm_notification(wpa_s
, cell_capa
, 7);
513 wpa_supplicant_set_default_scan_ies(wpa_s
);
517 struct wpabuf
* mbo_build_anqp_buf(struct wpa_supplicant
*wpa_s
,
518 struct wpa_bss
*bss
, u32 mbo_subtypes
)
520 struct wpabuf
*anqp_buf
;
524 if (!wpa_bss_get_vendor_ie(bss
, MBO_IE_VENDOR_TYPE
)) {
525 wpa_printf(MSG_INFO
, "MBO: " MACSTR
526 " does not support MBO - cannot request MBO ANQP elements from it",
527 MAC2STR(bss
->bssid
));
531 /* Allocate size for the maximum case - all MBO subtypes are set */
532 anqp_buf
= wpabuf_alloc(9 + MAX_MBO_ANQP_SUBTYPE
);
536 len_pos
= gas_anqp_add_element(anqp_buf
, ANQP_VENDOR_SPECIFIC
);
537 wpabuf_put_be24(anqp_buf
, OUI_WFA
);
538 wpabuf_put_u8(anqp_buf
, MBO_ANQP_OUI_TYPE
);
540 wpabuf_put_u8(anqp_buf
, MBO_ANQP_SUBTYPE_QUERY_LIST
);
542 /* The first valid MBO subtype is 1 */
543 for (i
= 1; i
<= MAX_MBO_ANQP_SUBTYPE
; i
++) {
544 if (mbo_subtypes
& BIT(i
))
545 wpabuf_put_u8(anqp_buf
, i
);
548 gas_anqp_set_element_len(anqp_buf
, len_pos
);
554 void mbo_parse_rx_anqp_resp(struct wpa_supplicant
*wpa_s
,
555 struct wpa_bss
*bss
, const u8
*sa
,
556 const u8
*data
, size_t slen
)
558 const u8
*pos
= data
;
568 case MBO_ANQP_SUBTYPE_CELL_CONN_PREF
:
571 wpa_msg(wpa_s
, MSG_INFO
, RX_MBO_ANQP MACSTR
572 " cell_conn_pref=%u", MAC2STR(sa
), *pos
);
575 wpa_printf(MSG_DEBUG
, "MBO: Unsupported ANQP subtype %u",