1324714Scy/* 2324714Scy * wpa_supplicant - MBO 3324714Scy * 4324714Scy * Copyright(c) 2015 Intel Deutschland GmbH 5324714Scy * Contact Information: 6324714Scy * Intel Linux Wireless <ilw@linux.intel.com> 7324714Scy * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 8324714Scy * 9324714Scy * This software may be distributed under the terms of the BSD license. 10324714Scy * See README for more details. 11324714Scy */ 12324714Scy 13324714Scy#include "utils/includes.h" 14324714Scy 15324714Scy#include "utils/common.h" 16324714Scy#include "common/ieee802_11_defs.h" 17324714Scy#include "common/gas.h" 18324714Scy#include "config.h" 19324714Scy#include "wpa_supplicant_i.h" 20324714Scy#include "driver_i.h" 21324714Scy#include "bss.h" 22324714Scy#include "scan.h" 23324714Scy 24324714Scy/* type + length + oui + oui type */ 25324714Scy#define MBO_IE_HEADER 6 26324714Scy 27324714Scy 28324714Scystatic int wpas_mbo_validate_non_pref_chan(u8 oper_class, u8 chan, u8 reason) 29324714Scy{ 30324714Scy if (reason > MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE) 31324714Scy return -1; 32324714Scy 33324714Scy /* Only checking the validity of the channel and oper_class */ 34324714Scy if (ieee80211_chan_to_freq(NULL, oper_class, chan) == -1) 35324714Scy return -1; 36324714Scy 37324714Scy return 0; 38324714Scy} 39324714Scy 40324714Scy 41346981Scyconst u8 * mbo_attr_from_mbo_ie(const u8 *mbo_ie, enum mbo_attr_id attr) 42346981Scy{ 43346981Scy const u8 *mbo; 44346981Scy u8 ie_len = mbo_ie[1]; 45346981Scy 46346981Scy if (ie_len < MBO_IE_HEADER - 2) 47346981Scy return NULL; 48346981Scy mbo = mbo_ie + MBO_IE_HEADER; 49346981Scy 50346981Scy return get_ie(mbo, 2 + ie_len - MBO_IE_HEADER, attr); 51346981Scy} 52346981Scy 53346981Scy 54346981Scyconst u8 * mbo_get_attr_from_ies(const u8 *ies, size_t ies_len, 55346981Scy enum mbo_attr_id attr) 56346981Scy{ 57346981Scy const u8 *mbo_ie; 58346981Scy 59346981Scy mbo_ie = get_vendor_ie(ies, ies_len, MBO_IE_VENDOR_TYPE); 60346981Scy if (!mbo_ie) 61346981Scy return NULL; 62346981Scy 63346981Scy return mbo_attr_from_mbo_ie(mbo_ie, attr); 64346981Scy} 65346981Scy 66346981Scy 67324714Scyconst u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr) 68324714Scy{ 69324714Scy const u8 *mbo, *end; 70324714Scy 71324714Scy if (!bss) 72324714Scy return NULL; 73324714Scy 74324714Scy mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE); 75324714Scy if (!mbo) 76324714Scy return NULL; 77324714Scy 78324714Scy end = mbo + 2 + mbo[1]; 79324714Scy mbo += MBO_IE_HEADER; 80324714Scy 81324714Scy return get_ie(mbo, end - mbo, attr); 82324714Scy} 83324714Scy 84324714Scy 85324714Scystatic void wpas_mbo_non_pref_chan_attr_body(struct wpa_supplicant *wpa_s, 86324714Scy struct wpabuf *mbo, 87324714Scy u8 start, u8 end) 88324714Scy{ 89324714Scy u8 i; 90324714Scy 91324714Scy wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].oper_class); 92324714Scy 93324714Scy for (i = start; i < end; i++) 94324714Scy wpabuf_put_u8(mbo, wpa_s->non_pref_chan[i].chan); 95324714Scy 96324714Scy wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].preference); 97324714Scy wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].reason); 98324714Scy} 99324714Scy 100324714Scy 101346981Scystatic void wpas_mbo_non_pref_chan_attr_hdr(struct wpabuf *mbo, size_t size) 102346981Scy{ 103346981Scy wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT); 104346981Scy wpabuf_put_u8(mbo, size); /* Length */ 105346981Scy} 106346981Scy 107346981Scy 108324714Scystatic void wpas_mbo_non_pref_chan_attr(struct wpa_supplicant *wpa_s, 109324714Scy struct wpabuf *mbo, u8 start, u8 end) 110324714Scy{ 111324714Scy size_t size = end - start + 3; 112324714Scy 113324714Scy if (size + 2 > wpabuf_tailroom(mbo)) 114324714Scy return; 115324714Scy 116346981Scy wpas_mbo_non_pref_chan_attr_hdr(mbo, size); 117324714Scy wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end); 118324714Scy} 119324714Scy 120324714Scy 121324714Scystatic void wpas_mbo_non_pref_chan_subelem_hdr(struct wpabuf *mbo, u8 len) 122324714Scy{ 123324714Scy wpabuf_put_u8(mbo, WLAN_EID_VENDOR_SPECIFIC); 124324714Scy wpabuf_put_u8(mbo, len); /* Length */ 125324714Scy wpabuf_put_be24(mbo, OUI_WFA); 126324714Scy wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT); 127324714Scy} 128324714Scy 129324714Scy 130324714Scystatic void wpas_mbo_non_pref_chan_subelement(struct wpa_supplicant *wpa_s, 131324714Scy struct wpabuf *mbo, u8 start, 132324714Scy u8 end) 133324714Scy{ 134324714Scy size_t size = end - start + 7; 135324714Scy 136324714Scy if (size + 2 > wpabuf_tailroom(mbo)) 137324714Scy return; 138324714Scy 139324714Scy wpas_mbo_non_pref_chan_subelem_hdr(mbo, size); 140324714Scy wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end); 141324714Scy} 142324714Scy 143324714Scy 144324714Scystatic void wpas_mbo_non_pref_chan_attrs(struct wpa_supplicant *wpa_s, 145324714Scy struct wpabuf *mbo, int subelement) 146324714Scy{ 147324714Scy u8 i, start = 0; 148324714Scy struct wpa_mbo_non_pref_channel *start_pref; 149324714Scy 150324714Scy if (!wpa_s->non_pref_chan || !wpa_s->non_pref_chan_num) { 151324714Scy if (subelement) 152324714Scy wpas_mbo_non_pref_chan_subelem_hdr(mbo, 4); 153346981Scy else 154346981Scy wpas_mbo_non_pref_chan_attr_hdr(mbo, 0); 155324714Scy return; 156324714Scy } 157324714Scy start_pref = &wpa_s->non_pref_chan[0]; 158324714Scy 159324714Scy for (i = 1; i <= wpa_s->non_pref_chan_num; i++) { 160324714Scy struct wpa_mbo_non_pref_channel *non_pref = NULL; 161324714Scy 162324714Scy if (i < wpa_s->non_pref_chan_num) 163324714Scy non_pref = &wpa_s->non_pref_chan[i]; 164324714Scy if (!non_pref || 165324714Scy non_pref->oper_class != start_pref->oper_class || 166324714Scy non_pref->reason != start_pref->reason || 167324714Scy non_pref->preference != start_pref->preference) { 168324714Scy if (subelement) 169324714Scy wpas_mbo_non_pref_chan_subelement(wpa_s, mbo, 170324714Scy start, i); 171324714Scy else 172324714Scy wpas_mbo_non_pref_chan_attr(wpa_s, mbo, start, 173324714Scy i); 174324714Scy 175324714Scy if (!non_pref) 176324714Scy return; 177324714Scy 178324714Scy start = i; 179324714Scy start_pref = non_pref; 180324714Scy } 181324714Scy } 182324714Scy} 183324714Scy 184324714Scy 185346981Scyint wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len, 186346981Scy int add_oce_capa) 187324714Scy{ 188324714Scy struct wpabuf *mbo; 189324714Scy int res; 190324714Scy 191346981Scy if (len < MBO_IE_HEADER + 3 + 7 + 192346981Scy ((wpa_s->enable_oce & OCE_STA) ? 3 : 0)) 193324714Scy return 0; 194324714Scy 195324714Scy /* Leave room for the MBO IE header */ 196324714Scy mbo = wpabuf_alloc(len - MBO_IE_HEADER); 197324714Scy if (!mbo) 198324714Scy return 0; 199324714Scy 200324714Scy /* Add non-preferred channels attribute */ 201324714Scy wpas_mbo_non_pref_chan_attrs(wpa_s, mbo, 0); 202324714Scy 203324714Scy /* 204324714Scy * Send cellular capabilities attribute even if AP does not advertise 205324714Scy * cellular capabilities. 206324714Scy */ 207324714Scy wpabuf_put_u8(mbo, MBO_ATTR_ID_CELL_DATA_CAPA); 208324714Scy wpabuf_put_u8(mbo, 1); 209324714Scy wpabuf_put_u8(mbo, wpa_s->conf->mbo_cell_capa); 210324714Scy 211346981Scy /* Add OCE capability indication attribute if OCE is enabled */ 212346981Scy if ((wpa_s->enable_oce & OCE_STA) && add_oce_capa) { 213346981Scy wpabuf_put_u8(mbo, OCE_ATTR_ID_CAPA_IND); 214346981Scy wpabuf_put_u8(mbo, 1); 215346981Scy wpabuf_put_u8(mbo, OCE_RELEASE); 216346981Scy } 217346981Scy 218324714Scy res = mbo_add_ie(buf, len, wpabuf_head_u8(mbo), wpabuf_len(mbo)); 219324714Scy if (!res) 220346981Scy wpa_printf(MSG_ERROR, "Failed to add MBO/OCE IE"); 221324714Scy 222324714Scy wpabuf_free(mbo); 223324714Scy return res; 224324714Scy} 225324714Scy 226324714Scy 227324714Scystatic void wpas_mbo_send_wnm_notification(struct wpa_supplicant *wpa_s, 228324714Scy const u8 *data, size_t len) 229324714Scy{ 230324714Scy struct wpabuf *buf; 231324714Scy int res; 232324714Scy 233324714Scy /* 234324714Scy * Send WNM-Notification Request frame only in case of a change in 235324714Scy * non-preferred channels list during association, if the AP supports 236324714Scy * MBO. 237324714Scy */ 238324714Scy if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_bss || 239324714Scy !wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) 240324714Scy return; 241324714Scy 242324714Scy buf = wpabuf_alloc(4 + len); 243324714Scy if (!buf) 244324714Scy return; 245324714Scy 246324714Scy wpabuf_put_u8(buf, WLAN_ACTION_WNM); 247324714Scy wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ); 248324714Scy wpa_s->mbo_wnm_token++; 249324714Scy if (wpa_s->mbo_wnm_token == 0) 250324714Scy wpa_s->mbo_wnm_token++; 251324714Scy wpabuf_put_u8(buf, wpa_s->mbo_wnm_token); 252324714Scy wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); /* Type */ 253324714Scy 254324714Scy wpabuf_put_data(buf, data, len); 255324714Scy 256324714Scy res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, 257324714Scy wpa_s->own_addr, wpa_s->bssid, 258324714Scy wpabuf_head(buf), wpabuf_len(buf), 0); 259324714Scy if (res < 0) 260324714Scy wpa_printf(MSG_DEBUG, 261324714Scy "Failed to send WNM-Notification Request frame with non-preferred channel list"); 262324714Scy 263324714Scy wpabuf_free(buf); 264324714Scy} 265324714Scy 266324714Scy 267324714Scystatic void wpas_mbo_non_pref_chan_changed(struct wpa_supplicant *wpa_s) 268324714Scy{ 269324714Scy struct wpabuf *buf; 270324714Scy 271324714Scy buf = wpabuf_alloc(512); 272324714Scy if (!buf) 273324714Scy return; 274324714Scy 275324714Scy wpas_mbo_non_pref_chan_attrs(wpa_s, buf, 1); 276324714Scy wpas_mbo_send_wnm_notification(wpa_s, wpabuf_head_u8(buf), 277324714Scy wpabuf_len(buf)); 278346981Scy wpas_update_mbo_connect_params(wpa_s); 279324714Scy wpabuf_free(buf); 280324714Scy} 281324714Scy 282324714Scy 283324714Scystatic int wpa_non_pref_chan_is_eq(struct wpa_mbo_non_pref_channel *a, 284324714Scy struct wpa_mbo_non_pref_channel *b) 285324714Scy{ 286324714Scy return a->oper_class == b->oper_class && a->chan == b->chan; 287324714Scy} 288324714Scy 289324714Scy 290324714Scy/* 291324714Scy * wpa_non_pref_chan_cmp - Compare two channels for sorting 292324714Scy * 293324714Scy * In MBO IE non-preferred channel subelement we can put many channels in an 294324714Scy * attribute if they are in the same operating class and have the same 295324714Scy * preference and reason. To make it easy for the functions that build 296324714Scy * the IE attributes and WNM Request subelements, save the channels sorted 297324714Scy * by their oper_class and reason. 298324714Scy */ 299324714Scystatic int wpa_non_pref_chan_cmp(const void *_a, const void *_b) 300324714Scy{ 301324714Scy const struct wpa_mbo_non_pref_channel *a = _a, *b = _b; 302324714Scy 303324714Scy if (a->oper_class != b->oper_class) 304346981Scy return (int) a->oper_class - (int) b->oper_class; 305324714Scy if (a->reason != b->reason) 306346981Scy return (int) a->reason - (int) b->reason; 307346981Scy return (int) a->preference - (int) b->preference; 308324714Scy} 309324714Scy 310324714Scy 311324714Scyint wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s, 312324714Scy const char *non_pref_chan) 313324714Scy{ 314324714Scy char *cmd, *token, *context = NULL; 315324714Scy struct wpa_mbo_non_pref_channel *chans = NULL, *tmp_chans; 316324714Scy size_t num = 0, size = 0; 317324714Scy unsigned i; 318324714Scy 319324714Scy wpa_printf(MSG_DEBUG, "MBO: Update non-preferred channels, non_pref_chan=%s", 320324714Scy non_pref_chan ? non_pref_chan : "N/A"); 321324714Scy 322324714Scy /* 323346981Scy * The shortest channel configuration is 7 characters - 3 colons and 324346981Scy * 4 values. 325324714Scy */ 326346981Scy if (!non_pref_chan || os_strlen(non_pref_chan) < 7) 327324714Scy goto update; 328324714Scy 329324714Scy cmd = os_strdup(non_pref_chan); 330324714Scy if (!cmd) 331324714Scy return -1; 332324714Scy 333324714Scy while ((token = str_token(cmd, " ", &context))) { 334324714Scy struct wpa_mbo_non_pref_channel *chan; 335324714Scy int ret; 336324714Scy unsigned int _oper_class; 337324714Scy unsigned int _chan; 338324714Scy unsigned int _preference; 339324714Scy unsigned int _reason; 340324714Scy 341324714Scy if (num == size) { 342324714Scy size = size ? size * 2 : 1; 343324714Scy tmp_chans = os_realloc_array(chans, size, 344324714Scy sizeof(*chans)); 345324714Scy if (!tmp_chans) { 346324714Scy wpa_printf(MSG_ERROR, 347324714Scy "Couldn't reallocate non_pref_chan"); 348324714Scy goto fail; 349324714Scy } 350324714Scy chans = tmp_chans; 351324714Scy } 352324714Scy 353324714Scy chan = &chans[num]; 354324714Scy 355324714Scy ret = sscanf(token, "%u:%u:%u:%u", &_oper_class, 356324714Scy &_chan, &_preference, &_reason); 357324714Scy if (ret != 4 || 358324714Scy _oper_class > 255 || _chan > 255 || 359324714Scy _preference > 255 || _reason > 65535 ) { 360324714Scy wpa_printf(MSG_ERROR, "Invalid non-pref chan input %s", 361324714Scy token); 362324714Scy goto fail; 363324714Scy } 364324714Scy chan->oper_class = _oper_class; 365324714Scy chan->chan = _chan; 366324714Scy chan->preference = _preference; 367324714Scy chan->reason = _reason; 368324714Scy 369324714Scy if (wpas_mbo_validate_non_pref_chan(chan->oper_class, 370324714Scy chan->chan, chan->reason)) { 371324714Scy wpa_printf(MSG_ERROR, 372324714Scy "Invalid non_pref_chan: oper class %d chan %d reason %d", 373324714Scy chan->oper_class, chan->chan, chan->reason); 374324714Scy goto fail; 375324714Scy } 376324714Scy 377324714Scy for (i = 0; i < num; i++) 378324714Scy if (wpa_non_pref_chan_is_eq(chan, &chans[i])) 379324714Scy break; 380324714Scy if (i != num) { 381324714Scy wpa_printf(MSG_ERROR, 382324714Scy "oper class %d chan %d is duplicated", 383324714Scy chan->oper_class, chan->chan); 384324714Scy goto fail; 385324714Scy } 386324714Scy 387324714Scy num++; 388324714Scy } 389324714Scy 390324714Scy os_free(cmd); 391324714Scy 392324714Scy if (chans) { 393324714Scy qsort(chans, num, sizeof(struct wpa_mbo_non_pref_channel), 394324714Scy wpa_non_pref_chan_cmp); 395324714Scy } 396324714Scy 397324714Scyupdate: 398324714Scy os_free(wpa_s->non_pref_chan); 399324714Scy wpa_s->non_pref_chan = chans; 400324714Scy wpa_s->non_pref_chan_num = num; 401324714Scy wpas_mbo_non_pref_chan_changed(wpa_s); 402324714Scy 403324714Scy return 0; 404324714Scy 405324714Scyfail: 406324714Scy os_free(chans); 407324714Scy os_free(cmd); 408324714Scy return -1; 409324714Scy} 410324714Scy 411324714Scy 412324714Scyvoid wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie) 413324714Scy{ 414346981Scy u8 *len; 415346981Scy 416324714Scy wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); 417346981Scy len = wpabuf_put(ie, 1); 418346981Scy 419324714Scy wpabuf_put_be24(ie, OUI_WFA); 420324714Scy wpabuf_put_u8(ie, MBO_OUI_TYPE); 421324714Scy 422324714Scy wpabuf_put_u8(ie, MBO_ATTR_ID_CELL_DATA_CAPA); 423324714Scy wpabuf_put_u8(ie, 1); 424324714Scy wpabuf_put_u8(ie, wpa_s->conf->mbo_cell_capa); 425346981Scy if (wpa_s->enable_oce & OCE_STA) { 426346981Scy wpabuf_put_u8(ie, OCE_ATTR_ID_CAPA_IND); 427346981Scy wpabuf_put_u8(ie, 1); 428346981Scy wpabuf_put_u8(ie, OCE_RELEASE); 429324714Scy } 430346981Scy *len = (u8 *) wpabuf_put(ie, 0) - len - 1; 431324714Scy} 432324714Scy 433324714Scy 434324714Scyvoid wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie, 435324714Scy size_t len) 436324714Scy{ 437346981Scy const u8 *pos, *cell_pref = NULL; 438324714Scy u8 id, elen; 439324714Scy u16 disallowed_sec = 0; 440324714Scy 441324714Scy if (len <= 4 || WPA_GET_BE24(mbo_ie) != OUI_WFA || 442324714Scy mbo_ie[3] != MBO_OUI_TYPE) 443324714Scy return; 444324714Scy 445324714Scy pos = mbo_ie + 4; 446324714Scy len -= 4; 447324714Scy 448324714Scy while (len >= 2) { 449324714Scy id = *pos++; 450324714Scy elen = *pos++; 451324714Scy len -= 2; 452324714Scy 453324714Scy if (elen > len) 454324714Scy goto fail; 455324714Scy 456324714Scy switch (id) { 457324714Scy case MBO_ATTR_ID_CELL_DATA_PREF: 458324714Scy if (elen != 1) 459324714Scy goto fail; 460324714Scy 461324714Scy if (wpa_s->conf->mbo_cell_capa == 462324714Scy MBO_CELL_CAPA_AVAILABLE) 463324714Scy cell_pref = pos; 464324714Scy else 465324714Scy wpa_printf(MSG_DEBUG, 466324714Scy "MBO: Station does not support Cellular data connection"); 467324714Scy break; 468324714Scy case MBO_ATTR_ID_TRANSITION_REASON: 469324714Scy if (elen != 1) 470324714Scy goto fail; 471324714Scy 472346981Scy wpa_s->wnm_mbo_trans_reason_present = 1; 473346981Scy wpa_s->wnm_mbo_transition_reason = *pos; 474324714Scy break; 475324714Scy case MBO_ATTR_ID_ASSOC_RETRY_DELAY: 476324714Scy if (elen != 2) 477324714Scy goto fail; 478324714Scy 479324714Scy if (wpa_s->wnm_mode & 480324714Scy WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) { 481324714Scy wpa_printf(MSG_DEBUG, 482324714Scy "MBO: Unexpected association retry delay, BSS is terminating"); 483324714Scy goto fail; 484324714Scy } else if (wpa_s->wnm_mode & 485324714Scy WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { 486324714Scy disallowed_sec = WPA_GET_LE16(pos); 487346981Scy wpa_printf(MSG_DEBUG, 488346981Scy "MBO: Association retry delay: %u", 489346981Scy disallowed_sec); 490324714Scy } else { 491324714Scy wpa_printf(MSG_DEBUG, 492324714Scy "MBO: Association retry delay attribute not in disassoc imminent mode"); 493324714Scy } 494324714Scy 495324714Scy break; 496324714Scy case MBO_ATTR_ID_AP_CAPA_IND: 497324714Scy case MBO_ATTR_ID_NON_PREF_CHAN_REPORT: 498324714Scy case MBO_ATTR_ID_CELL_DATA_CAPA: 499324714Scy case MBO_ATTR_ID_ASSOC_DISALLOW: 500324714Scy case MBO_ATTR_ID_TRANSITION_REJECT_REASON: 501324714Scy wpa_printf(MSG_DEBUG, 502324714Scy "MBO: Attribute %d should not be included in BTM Request frame", 503324714Scy id); 504324714Scy break; 505324714Scy default: 506324714Scy wpa_printf(MSG_DEBUG, "MBO: Unknown attribute id %u", 507324714Scy id); 508324714Scy return; 509324714Scy } 510324714Scy 511324714Scy pos += elen; 512324714Scy len -= elen; 513324714Scy } 514324714Scy 515324714Scy if (cell_pref) 516324714Scy wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u", 517324714Scy *cell_pref); 518324714Scy 519346981Scy if (wpa_s->wnm_mbo_trans_reason_present) 520324714Scy wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u", 521346981Scy wpa_s->wnm_mbo_transition_reason); 522324714Scy 523324714Scy if (disallowed_sec && wpa_s->current_bss) 524324714Scy wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid, 525346981Scy disallowed_sec, 0); 526324714Scy 527324714Scy return; 528324714Scyfail: 529324714Scy wpa_printf(MSG_DEBUG, "MBO IE parsing failed (id=%u len=%u left=%zu)", 530324714Scy id, elen, len); 531324714Scy} 532324714Scy 533324714Scy 534324714Scysize_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos, 535324714Scy size_t len, 536324714Scy enum mbo_transition_reject_reason reason) 537324714Scy{ 538324714Scy u8 reject_attr[3]; 539324714Scy 540324714Scy reject_attr[0] = MBO_ATTR_ID_TRANSITION_REJECT_REASON; 541324714Scy reject_attr[1] = 1; 542324714Scy reject_attr[2] = reason; 543324714Scy 544324714Scy return mbo_add_ie(pos, len, reject_attr, sizeof(reject_attr)); 545324714Scy} 546324714Scy 547324714Scy 548324714Scyvoid wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa) 549324714Scy{ 550324714Scy u8 cell_capa[7]; 551324714Scy 552324714Scy if (wpa_s->conf->mbo_cell_capa == mbo_cell_capa) { 553324714Scy wpa_printf(MSG_DEBUG, 554324714Scy "MBO: Cellular capability already set to %u", 555324714Scy mbo_cell_capa); 556324714Scy return; 557324714Scy } 558324714Scy 559324714Scy wpa_s->conf->mbo_cell_capa = mbo_cell_capa; 560324714Scy 561324714Scy cell_capa[0] = WLAN_EID_VENDOR_SPECIFIC; 562324714Scy cell_capa[1] = 5; /* Length */ 563324714Scy WPA_PUT_BE24(cell_capa + 2, OUI_WFA); 564324714Scy cell_capa[5] = MBO_ATTR_ID_CELL_DATA_CAPA; 565324714Scy cell_capa[6] = mbo_cell_capa; 566324714Scy 567324714Scy wpas_mbo_send_wnm_notification(wpa_s, cell_capa, 7); 568324714Scy wpa_supplicant_set_default_scan_ies(wpa_s); 569346981Scy wpas_update_mbo_connect_params(wpa_s); 570324714Scy} 571324714Scy 572324714Scy 573324714Scystruct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s, 574346981Scy struct wpa_bss *bss, u32 mbo_subtypes) 575324714Scy{ 576324714Scy struct wpabuf *anqp_buf; 577324714Scy u8 *len_pos; 578346981Scy u8 i; 579324714Scy 580324714Scy if (!wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) { 581324714Scy wpa_printf(MSG_INFO, "MBO: " MACSTR 582324714Scy " does not support MBO - cannot request MBO ANQP elements from it", 583324714Scy MAC2STR(bss->bssid)); 584324714Scy return NULL; 585324714Scy } 586324714Scy 587346981Scy /* Allocate size for the maximum case - all MBO subtypes are set */ 588346981Scy anqp_buf = wpabuf_alloc(9 + MAX_MBO_ANQP_SUBTYPE); 589324714Scy if (!anqp_buf) 590324714Scy return NULL; 591324714Scy 592324714Scy len_pos = gas_anqp_add_element(anqp_buf, ANQP_VENDOR_SPECIFIC); 593324714Scy wpabuf_put_be24(anqp_buf, OUI_WFA); 594324714Scy wpabuf_put_u8(anqp_buf, MBO_ANQP_OUI_TYPE); 595324714Scy 596346981Scy wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_QUERY_LIST); 597346981Scy 598346981Scy /* The first valid MBO subtype is 1 */ 599346981Scy for (i = 1; i <= MAX_MBO_ANQP_SUBTYPE; i++) { 600346981Scy if (mbo_subtypes & BIT(i)) 601346981Scy wpabuf_put_u8(anqp_buf, i); 602346981Scy } 603346981Scy 604324714Scy gas_anqp_set_element_len(anqp_buf, len_pos); 605324714Scy 606324714Scy return anqp_buf; 607324714Scy} 608346981Scy 609346981Scy 610346981Scyvoid mbo_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s, 611346981Scy struct wpa_bss *bss, const u8 *sa, 612346981Scy const u8 *data, size_t slen) 613346981Scy{ 614346981Scy const u8 *pos = data; 615346981Scy u8 subtype; 616346981Scy 617346981Scy if (slen < 1) 618346981Scy return; 619346981Scy 620346981Scy subtype = *pos++; 621346981Scy slen--; 622346981Scy 623346981Scy switch (subtype) { 624346981Scy case MBO_ANQP_SUBTYPE_CELL_CONN_PREF: 625346981Scy if (slen < 1) 626346981Scy break; 627346981Scy wpa_msg(wpa_s, MSG_INFO, RX_MBO_ANQP MACSTR 628346981Scy " cell_conn_pref=%u", MAC2STR(sa), *pos); 629346981Scy break; 630346981Scy default: 631346981Scy wpa_printf(MSG_DEBUG, "MBO: Unsupported ANQP subtype %u", 632346981Scy subtype); 633346981Scy break; 634346981Scy } 635346981Scy} 636