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 10 characters - commas, 3
281 * colons, and 4 values that one of them (oper_class) is 2 digits or
284 if (!non_pref_chan
|| os_strlen(non_pref_chan
) < 10)
287 cmd
= os_strdup(non_pref_chan
);
291 while ((token
= str_token(cmd
, " ", &context
))) {
292 struct wpa_mbo_non_pref_channel
*chan
;
294 unsigned int _oper_class
;
296 unsigned int _preference
;
297 unsigned int _reason
;
300 size
= size
? size
* 2 : 1;
301 tmp_chans
= os_realloc_array(chans
, size
,
304 wpa_printf(MSG_ERROR
,
305 "Couldn't reallocate non_pref_chan");
313 ret
= sscanf(token
, "%u:%u:%u:%u", &_oper_class
,
314 &_chan
, &_preference
, &_reason
);
316 _oper_class
> 255 || _chan
> 255 ||
317 _preference
> 255 || _reason
> 65535 ) {
318 wpa_printf(MSG_ERROR
, "Invalid non-pref chan input %s",
322 chan
->oper_class
= _oper_class
;
324 chan
->preference
= _preference
;
325 chan
->reason
= _reason
;
327 if (wpas_mbo_validate_non_pref_chan(chan
->oper_class
,
328 chan
->chan
, chan
->reason
)) {
329 wpa_printf(MSG_ERROR
,
330 "Invalid non_pref_chan: oper class %d chan %d reason %d",
331 chan
->oper_class
, chan
->chan
, chan
->reason
);
335 for (i
= 0; i
< num
; i
++)
336 if (wpa_non_pref_chan_is_eq(chan
, &chans
[i
]))
339 wpa_printf(MSG_ERROR
,
340 "oper class %d chan %d is duplicated",
341 chan
->oper_class
, chan
->chan
);
351 qsort(chans
, num
, sizeof(struct wpa_mbo_non_pref_channel
),
352 wpa_non_pref_chan_cmp
);
356 os_free(wpa_s
->non_pref_chan
);
357 wpa_s
->non_pref_chan
= chans
;
358 wpa_s
->non_pref_chan_num
= num
;
359 wpas_mbo_non_pref_chan_changed(wpa_s
);
370 void wpas_mbo_scan_ie(struct wpa_supplicant
*wpa_s
, struct wpabuf
*ie
)
372 wpabuf_put_u8(ie
, WLAN_EID_VENDOR_SPECIFIC
);
373 wpabuf_put_u8(ie
, 7);
374 wpabuf_put_be24(ie
, OUI_WFA
);
375 wpabuf_put_u8(ie
, MBO_OUI_TYPE
);
377 wpabuf_put_u8(ie
, MBO_ATTR_ID_CELL_DATA_CAPA
);
378 wpabuf_put_u8(ie
, 1);
379 wpabuf_put_u8(ie
, wpa_s
->conf
->mbo_cell_capa
);
387 static enum chan_allowed
allow_channel(struct hostapd_hw_modes
*mode
, u8 chan
,
392 for (i
= 0; i
< mode
->num_channels
; i
++) {
393 if (mode
->channels
[i
].chan
== chan
)
397 if (i
== mode
->num_channels
||
398 (mode
->channels
[i
].flag
& HOSTAPD_CHAN_DISABLED
))
402 *flags
= mode
->channels
[i
].flag
;
408 static int get_center_80mhz(struct hostapd_hw_modes
*mode
, u8 channel
)
410 u8 center_channels
[] = {42, 58, 106, 122, 138, 155};
413 if (mode
->mode
!= HOSTAPD_MODE_IEEE80211A
)
416 for (i
= 0; i
< ARRAY_SIZE(center_channels
); i
++) {
418 * In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48),
419 * so the center channel is 6 channels away from the start/end.
421 if (channel
>= center_channels
[i
] - 6 &&
422 channel
<= center_channels
[i
] + 6)
423 return center_channels
[i
];
430 static enum chan_allowed
verify_80mhz(struct hostapd_hw_modes
*mode
, u8 channel
)
435 center_chan
= get_center_80mhz(mode
, channel
);
439 /* check all the channels are available */
440 for (i
= 0; i
< 4; i
++) {
442 u8 adj_chan
= center_chan
- 6 + i
* 4;
444 if (allow_channel(mode
, adj_chan
, &flags
) == NOT_ALLOWED
)
447 if ((i
== 0 && !(flags
& HOSTAPD_CHAN_VHT_10_70
)) ||
448 (i
== 1 && !(flags
& HOSTAPD_CHAN_VHT_30_50
)) ||
449 (i
== 2 && !(flags
& HOSTAPD_CHAN_VHT_50_30
)) ||
450 (i
== 3 && !(flags
& HOSTAPD_CHAN_VHT_70_10
)))
458 static int get_center_160mhz(struct hostapd_hw_modes
*mode
, u8 channel
)
460 u8 center_channels
[] = { 50, 114 };
463 if (mode
->mode
!= HOSTAPD_MODE_IEEE80211A
)
466 for (i
= 0; i
< ARRAY_SIZE(center_channels
); i
++) {
468 * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64),
469 * so the center channel is 14 channels away from the start/end.
471 if (channel
>= center_channels
[i
] - 14 &&
472 channel
<= center_channels
[i
] + 14)
473 return center_channels
[i
];
480 static enum chan_allowed
verify_160mhz(struct hostapd_hw_modes
*mode
,
486 center_chan
= get_center_160mhz(mode
, channel
);
490 /* Check all the channels are available */
491 for (i
= 0; i
< 8; i
++) {
493 u8 adj_chan
= center_chan
- 14 + i
* 4;
495 if (allow_channel(mode
, adj_chan
, &flags
) == NOT_ALLOWED
)
498 if ((i
== 0 && !(flags
& HOSTAPD_CHAN_VHT_10_150
)) ||
499 (i
== 1 && !(flags
& HOSTAPD_CHAN_VHT_30_130
)) ||
500 (i
== 2 && !(flags
& HOSTAPD_CHAN_VHT_50_110
)) ||
501 (i
== 3 && !(flags
& HOSTAPD_CHAN_VHT_70_90
)) ||
502 (i
== 4 && !(flags
& HOSTAPD_CHAN_VHT_90_70
)) ||
503 (i
== 5 && !(flags
& HOSTAPD_CHAN_VHT_110_50
)) ||
504 (i
== 6 && !(flags
& HOSTAPD_CHAN_VHT_130_30
)) ||
505 (i
== 7 && !(flags
& HOSTAPD_CHAN_VHT_150_10
)))
513 static enum chan_allowed
verify_channel(struct hostapd_hw_modes
*mode
,
516 unsigned int flag
= 0;
517 enum chan_allowed res
, res2
;
519 res2
= res
= allow_channel(mode
, channel
, &flag
);
520 if (bw
== BW40MINUS
) {
521 if (!(flag
& HOSTAPD_CHAN_HT40MINUS
))
523 res2
= allow_channel(mode
, channel
- 4, NULL
);
524 } else if (bw
== BW40PLUS
) {
525 if (!(flag
& HOSTAPD_CHAN_HT40PLUS
))
527 res2
= allow_channel(mode
, channel
+ 4, NULL
);
528 } else if (bw
== BW80
) {
530 * channel is a center channel and as such, not necessarily a
531 * valid 20 MHz channels. Override earlier allow_channel()
532 * result and use only the 80 MHz specific version.
534 res2
= res
= verify_80mhz(mode
, channel
);
535 } else if (bw
== BW160
) {
537 * channel is a center channel and as such, not necessarily a
538 * valid 20 MHz channels. Override earlier allow_channel()
539 * result and use only the 160 MHz specific version.
541 res2
= res
= verify_160mhz(mode
, channel
);
542 } else if (bw
== BW80P80
) {
544 * channel is a center channel and as such, not necessarily a
545 * valid 20 MHz channels. Override earlier allow_channel()
546 * result and use only the 80 MHz specific version.
548 res2
= res
= verify_80mhz(mode
, channel
);
551 if (res
== NOT_ALLOWED
|| res2
== NOT_ALLOWED
)
558 static int wpas_op_class_supported(struct wpa_supplicant
*wpa_s
,
559 const struct oper_class_map
*op_class
)
563 struct hostapd_hw_modes
*mode
;
566 mode
= get_mode(wpa_s
->hw
.modes
, wpa_s
->hw
.num_modes
, op_class
->mode
);
570 if (op_class
->op_class
== 128) {
571 u8 channels
[] = { 42, 58, 106, 122, 138, 155 };
573 for (i
= 0; i
< ARRAY_SIZE(channels
); i
++) {
574 if (verify_channel(mode
, channels
[i
], op_class
->bw
) ==
582 if (op_class
->op_class
== 129) {
583 /* Check if either 160 MHz channels is allowed */
584 return verify_channel(mode
, 50, op_class
->bw
) == ALLOWED
||
585 verify_channel(mode
, 114, op_class
->bw
) == ALLOWED
;
588 if (op_class
->op_class
== 130) {
589 /* Need at least two non-contiguous 80 MHz segments */
592 if (verify_channel(mode
, 42, op_class
->bw
) == ALLOWED
||
593 verify_channel(mode
, 58, op_class
->bw
) == ALLOWED
)
595 if (verify_channel(mode
, 106, op_class
->bw
) == ALLOWED
||
596 verify_channel(mode
, 122, op_class
->bw
) == ALLOWED
||
597 verify_channel(mode
, 138, op_class
->bw
) == ALLOWED
)
599 if (verify_channel(mode
, 106, op_class
->bw
) == ALLOWED
&&
600 verify_channel(mode
, 138, op_class
->bw
) == ALLOWED
)
602 if (verify_channel(mode
, 155, op_class
->bw
) == ALLOWED
)
612 for (chan
= op_class
->min_chan
; chan
<= op_class
->max_chan
;
613 chan
+= op_class
->inc
) {
614 if (verify_channel(mode
, chan
, op_class
->bw
) == ALLOWED
) {
624 int wpas_mbo_supp_op_class_ie(struct wpa_supplicant
*wpa_s
, int freq
, u8
*pos
,
628 u8 op
, current
, chan
;
633 * Assume 20 MHz channel for now.
634 * TODO: Use the secondary channel and VHT channel width that will be
635 * used after association.
637 if (ieee80211_freq_to_channel_ext(freq
, 0, VHT_CHANWIDTH_USE_HT
,
638 ¤t
, &chan
) == NUM_HOSTAPD_MODES
)
642 * Need 3 bytes for EID, length, and current operating class, plus
643 * 1 byte for every other supported operating class.
645 buf
= wpabuf_alloc(global_op_class_size
+ 3);
649 wpabuf_put_u8(buf
, WLAN_EID_SUPPORTED_OPERATING_CLASSES
);
650 /* Will set the length later, putting a placeholder */
651 ie_len
= wpabuf_put(buf
, 1);
652 wpabuf_put_u8(buf
, current
);
654 for (op
= 0; global_op_class
[op
].op_class
; op
++) {
655 if (wpas_op_class_supported(wpa_s
, &global_op_class
[op
]))
656 wpabuf_put_u8(buf
, global_op_class
[op
].op_class
);
659 *ie_len
= wpabuf_len(buf
) - 2;
660 if (*ie_len
< 2 || wpabuf_len(buf
) > len
) {
661 wpa_printf(MSG_ERROR
,
662 "Failed to add supported operating classes IE");
665 os_memcpy(pos
, wpabuf_head(buf
), wpabuf_len(buf
));
666 res
= wpabuf_len(buf
);
667 wpa_hexdump_buf(MSG_DEBUG
,
668 "MBO: Added supported operating classes IE",
677 void wpas_mbo_ie_trans_req(struct wpa_supplicant
*wpa_s
, const u8
*mbo_ie
,
680 const u8
*pos
, *cell_pref
= NULL
, *reason
= NULL
;
682 u16 disallowed_sec
= 0;
684 if (len
<= 4 || WPA_GET_BE24(mbo_ie
) != OUI_WFA
||
685 mbo_ie
[3] != MBO_OUI_TYPE
)
700 case MBO_ATTR_ID_CELL_DATA_PREF
:
704 if (wpa_s
->conf
->mbo_cell_capa
==
705 MBO_CELL_CAPA_AVAILABLE
)
708 wpa_printf(MSG_DEBUG
,
709 "MBO: Station does not support Cellular data connection");
711 case MBO_ATTR_ID_TRANSITION_REASON
:
717 case MBO_ATTR_ID_ASSOC_RETRY_DELAY
:
721 if (wpa_s
->wnm_mode
&
722 WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED
) {
723 wpa_printf(MSG_DEBUG
,
724 "MBO: Unexpected association retry delay, BSS is terminating");
726 } else if (wpa_s
->wnm_mode
&
727 WNM_BSS_TM_REQ_DISASSOC_IMMINENT
) {
728 disallowed_sec
= WPA_GET_LE16(pos
);
730 wpa_printf(MSG_DEBUG
,
731 "MBO: Association retry delay attribute not in disassoc imminent mode");
735 case MBO_ATTR_ID_AP_CAPA_IND
:
736 case MBO_ATTR_ID_NON_PREF_CHAN_REPORT
:
737 case MBO_ATTR_ID_CELL_DATA_CAPA
:
738 case MBO_ATTR_ID_ASSOC_DISALLOW
:
739 case MBO_ATTR_ID_TRANSITION_REJECT_REASON
:
740 wpa_printf(MSG_DEBUG
,
741 "MBO: Attribute %d should not be included in BTM Request frame",
745 wpa_printf(MSG_DEBUG
, "MBO: Unknown attribute id %u",
755 wpa_msg(wpa_s
, MSG_INFO
, MBO_CELL_PREFERENCE
"preference=%u",
759 wpa_msg(wpa_s
, MSG_INFO
, MBO_TRANSITION_REASON
"reason=%u",
762 if (disallowed_sec
&& wpa_s
->current_bss
)
763 wpa_bss_tmp_disallow(wpa_s
, wpa_s
->current_bss
->bssid
,
768 wpa_printf(MSG_DEBUG
, "MBO IE parsing failed (id=%u len=%u left=%zu)",
773 size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant
*wpa_s
, u8
*pos
,
775 enum mbo_transition_reject_reason reason
)
779 reject_attr
[0] = MBO_ATTR_ID_TRANSITION_REJECT_REASON
;
781 reject_attr
[2] = reason
;
783 return mbo_add_ie(pos
, len
, reject_attr
, sizeof(reject_attr
));
787 void wpas_mbo_update_cell_capa(struct wpa_supplicant
*wpa_s
, u8 mbo_cell_capa
)
791 if (wpa_s
->conf
->mbo_cell_capa
== mbo_cell_capa
) {
792 wpa_printf(MSG_DEBUG
,
793 "MBO: Cellular capability already set to %u",
798 wpa_s
->conf
->mbo_cell_capa
= mbo_cell_capa
;
800 cell_capa
[0] = WLAN_EID_VENDOR_SPECIFIC
;
801 cell_capa
[1] = 5; /* Length */
802 WPA_PUT_BE24(cell_capa
+ 2, OUI_WFA
);
803 cell_capa
[5] = MBO_ATTR_ID_CELL_DATA_CAPA
;
804 cell_capa
[6] = mbo_cell_capa
;
806 wpas_mbo_send_wnm_notification(wpa_s
, cell_capa
, 7);
807 wpa_supplicant_set_default_scan_ies(wpa_s
);
811 struct wpabuf
* mbo_build_anqp_buf(struct wpa_supplicant
*wpa_s
,
814 struct wpabuf
*anqp_buf
;
817 if (!wpa_bss_get_vendor_ie(bss
, MBO_IE_VENDOR_TYPE
)) {
818 wpa_printf(MSG_INFO
, "MBO: " MACSTR
819 " does not support MBO - cannot request MBO ANQP elements from it",
820 MAC2STR(bss
->bssid
));
824 anqp_buf
= wpabuf_alloc(10);
828 len_pos
= gas_anqp_add_element(anqp_buf
, ANQP_VENDOR_SPECIFIC
);
829 wpabuf_put_be24(anqp_buf
, OUI_WFA
);
830 wpabuf_put_u8(anqp_buf
, MBO_ANQP_OUI_TYPE
);
832 wpabuf_put_u8(anqp_buf
, MBO_ANQP_SUBTYPE_CELL_CONN_PREF
);
833 gas_anqp_set_element_len(anqp_buf
, len_pos
);