1 /* 2 * wpa_supplicant - MBO 3 * 4 * Copyright(c) 2015 Intel Deutschland GmbH 5 * Contact Information: 6 * Intel Linux Wireless <ilw@linux.intel.com> 7 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 8 * 9 * This software may be distributed under the terms of the BSD license. 10 * See README for more details. 11 */ 12 13 #include "utils/includes.h" 14 15 #include "utils/common.h" 16 #include "common/ieee802_11_defs.h" 17 #include "common/gas.h" 18 #include "config.h" 19 #include "wpa_supplicant_i.h" 20 #include "driver_i.h" 21 #include "bss.h" 22 #include "scan.h" 23 24 /* type + length + oui + oui type */ 25 #define MBO_IE_HEADER 6 26 27 28 static int wpas_mbo_validate_non_pref_chan(u8 oper_class, u8 chan, u8 reason) 29 { 30 if (reason > MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE) 31 return -1; 32 33 /* Only checking the validity of the channel and oper_class */ 34 if (ieee80211_chan_to_freq(NULL, oper_class, chan) == -1) 35 return -1; 36 37 return 0; 38 } 39 40 41 const u8 * mbo_attr_from_mbo_ie(const u8 *mbo_ie, enum mbo_attr_id attr) 42 { 43 const u8 *mbo; 44 u8 ie_len = mbo_ie[1]; 45 46 if (ie_len < MBO_IE_HEADER - 2) 47 return NULL; 48 mbo = mbo_ie + MBO_IE_HEADER; 49 50 return get_ie(mbo, 2 + ie_len - MBO_IE_HEADER, attr); 51 } 52 53 54 const u8 * mbo_get_attr_from_ies(const u8 *ies, size_t ies_len, 55 enum mbo_attr_id attr) 56 { 57 const u8 *mbo_ie; 58 59 mbo_ie = get_vendor_ie(ies, ies_len, MBO_IE_VENDOR_TYPE); 60 if (!mbo_ie) 61 return NULL; 62 63 return mbo_attr_from_mbo_ie(mbo_ie, attr); 64 } 65 66 67 const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr) 68 { 69 const u8 *mbo, *end; 70 71 if (!bss) 72 return NULL; 73 74 mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE); 75 if (!mbo) 76 return NULL; 77 78 end = mbo + 2 + mbo[1]; 79 mbo += MBO_IE_HEADER; 80 81 return get_ie(mbo, end - mbo, attr); 82 } 83 84 85 static void wpas_mbo_non_pref_chan_attr_body(struct wpa_supplicant *wpa_s, 86 struct wpabuf *mbo, 87 u8 start, u8 end) 88 { 89 u8 i; 90 91 wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].oper_class); 92 93 for (i = start; i < end; i++) 94 wpabuf_put_u8(mbo, wpa_s->non_pref_chan[i].chan); 95 96 wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].preference); 97 wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].reason); 98 } 99 100 101 static void wpas_mbo_non_pref_chan_attr_hdr(struct wpabuf *mbo, size_t size) 102 { 103 wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT); 104 wpabuf_put_u8(mbo, size); /* Length */ 105 } 106 107 108 static void wpas_mbo_non_pref_chan_attr(struct wpa_supplicant *wpa_s, 109 struct wpabuf *mbo, u8 start, u8 end) 110 { 111 size_t size = end - start + 3; 112 113 if (size + 2 > wpabuf_tailroom(mbo)) 114 return; 115 116 wpas_mbo_non_pref_chan_attr_hdr(mbo, size); 117 wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end); 118 } 119 120 121 static void wpas_mbo_non_pref_chan_subelem_hdr(struct wpabuf *mbo, u8 len) 122 { 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); 127 } 128 129 130 static void wpas_mbo_non_pref_chan_subelement(struct wpa_supplicant *wpa_s, 131 struct wpabuf *mbo, u8 start, 132 u8 end) 133 { 134 size_t size = end - start + 7; 135 136 if (size + 2 > wpabuf_tailroom(mbo)) 137 return; 138 139 wpas_mbo_non_pref_chan_subelem_hdr(mbo, size); 140 wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end); 141 } 142 143 144 static void wpas_mbo_non_pref_chan_attrs(struct wpa_supplicant *wpa_s, 145 struct wpabuf *mbo, int subelement) 146 { 147 u8 i, start = 0; 148 struct wpa_mbo_non_pref_channel *start_pref; 149 150 if (!wpa_s->non_pref_chan || !wpa_s->non_pref_chan_num) { 151 if (subelement) 152 wpas_mbo_non_pref_chan_subelem_hdr(mbo, 4); 153 else 154 wpas_mbo_non_pref_chan_attr_hdr(mbo, 0); 155 return; 156 } 157 start_pref = &wpa_s->non_pref_chan[0]; 158 159 for (i = 1; i <= wpa_s->non_pref_chan_num; i++) { 160 struct wpa_mbo_non_pref_channel *non_pref = NULL; 161 162 if (i < wpa_s->non_pref_chan_num) 163 non_pref = &wpa_s->non_pref_chan[i]; 164 if (!non_pref || 165 non_pref->oper_class != start_pref->oper_class || 166 non_pref->reason != start_pref->reason || 167 non_pref->preference != start_pref->preference) { 168 if (subelement) 169 wpas_mbo_non_pref_chan_subelement(wpa_s, mbo, 170 start, i); 171 else 172 wpas_mbo_non_pref_chan_attr(wpa_s, mbo, start, 173 i); 174 175 if (!non_pref) 176 return; 177 178 start = i; 179 start_pref = non_pref; 180 } 181 } 182 } 183 184 185 int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len, 186 int add_oce_capa) 187 { 188 struct wpabuf *mbo; 189 int res; 190 191 if (len < MBO_IE_HEADER + 3 + 7 + 192 ((wpa_s->enable_oce & OCE_STA) ? 3 : 0)) 193 return 0; 194 195 /* Leave room for the MBO IE header */ 196 mbo = wpabuf_alloc(len - MBO_IE_HEADER); 197 if (!mbo) 198 return 0; 199 200 /* Add non-preferred channels attribute */ 201 wpas_mbo_non_pref_chan_attrs(wpa_s, mbo, 0); 202 203 /* 204 * Send cellular capabilities attribute even if AP does not advertise 205 * cellular capabilities. 206 */ 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); 210 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); 216 } 217 218 res = mbo_add_ie(buf, len, wpabuf_head_u8(mbo), wpabuf_len(mbo)); 219 if (!res) 220 wpa_printf(MSG_ERROR, "Failed to add MBO/OCE IE"); 221 222 wpabuf_free(mbo); 223 return res; 224 } 225 226 227 static void wpas_mbo_send_wnm_notification(struct wpa_supplicant *wpa_s, 228 const u8 *data, size_t len) 229 { 230 struct wpabuf *buf; 231 int res; 232 233 /* 234 * Send WNM-Notification Request frame only in case of a change in 235 * non-preferred channels list during association, if the AP supports 236 * MBO. 237 */ 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)) 240 return; 241 242 buf = wpabuf_alloc(4 + len); 243 if (!buf) 244 return; 245 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 */ 253 254 wpabuf_put_data(buf, data, len); 255 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); 259 if (res < 0) 260 wpa_printf(MSG_DEBUG, 261 "Failed to send WNM-Notification Request frame with non-preferred channel list"); 262 263 wpabuf_free(buf); 264 } 265 266 267 static void wpas_mbo_non_pref_chan_changed(struct wpa_supplicant *wpa_s) 268 { 269 struct wpabuf *buf; 270 271 buf = wpabuf_alloc(512); 272 if (!buf) 273 return; 274 275 wpas_mbo_non_pref_chan_attrs(wpa_s, buf, 1); 276 wpas_mbo_send_wnm_notification(wpa_s, wpabuf_head_u8(buf), 277 wpabuf_len(buf)); 278 wpas_update_mbo_connect_params(wpa_s); 279 wpabuf_free(buf); 280 } 281 282 283 static int wpa_non_pref_chan_is_eq(struct wpa_mbo_non_pref_channel *a, 284 struct wpa_mbo_non_pref_channel *b) 285 { 286 return a->oper_class == b->oper_class && a->chan == b->chan; 287 } 288 289 290 /* 291 * wpa_non_pref_chan_cmp - Compare two channels for sorting 292 * 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. 298 */ 299 static int wpa_non_pref_chan_cmp(const void *_a, const void *_b) 300 { 301 const struct wpa_mbo_non_pref_channel *a = _a, *b = _b; 302 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; 308 } 309 310 311 int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s, 312 const char *non_pref_chan) 313 { 314 char *cmd, *token, *context = NULL; 315 struct wpa_mbo_non_pref_channel *chans = NULL, *tmp_chans; 316 size_t num = 0, size = 0; 317 unsigned i; 318 319 wpa_printf(MSG_DEBUG, "MBO: Update non-preferred channels, non_pref_chan=%s", 320 non_pref_chan ? non_pref_chan : "N/A"); 321 322 /* 323 * The shortest channel configuration is 7 characters - 3 colons and 324 * 4 values. 325 */ 326 if (!non_pref_chan || os_strlen(non_pref_chan) < 7) 327 goto update; 328 329 cmd = os_strdup(non_pref_chan); 330 if (!cmd) 331 return -1; 332 333 while ((token = str_token(cmd, " ", &context))) { 334 struct wpa_mbo_non_pref_channel *chan; 335 int ret; 336 unsigned int _oper_class; 337 unsigned int _chan; 338 unsigned int _preference; 339 unsigned int _reason; 340 341 if (num == size) { 342 size = size ? size * 2 : 1; 343 tmp_chans = os_realloc_array(chans, size, 344 sizeof(*chans)); 345 if (!tmp_chans) { 346 wpa_printf(MSG_ERROR, 347 "Couldn't reallocate non_pref_chan"); 348 goto fail; 349 } 350 chans = tmp_chans; 351 } 352 353 chan = &chans[num]; 354 355 ret = sscanf(token, "%u:%u:%u:%u", &_oper_class, 356 &_chan, &_preference, &_reason); 357 if (ret != 4 || 358 _oper_class > 255 || _chan > 255 || 359 _preference > 255 || _reason > 65535 ) { 360 wpa_printf(MSG_ERROR, "Invalid non-pref chan input %s", 361 token); 362 goto fail; 363 } 364 chan->oper_class = _oper_class; 365 chan->chan = _chan; 366 chan->preference = _preference; 367 chan->reason = _reason; 368 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); 374 goto fail; 375 } 376 377 for (i = 0; i < num; i++) 378 if (wpa_non_pref_chan_is_eq(chan, &chans[i])) 379 break; 380 if (i != num) { 381 wpa_printf(MSG_ERROR, 382 "oper class %d chan %d is duplicated", 383 chan->oper_class, chan->chan); 384 goto fail; 385 } 386 387 num++; 388 } 389 390 os_free(cmd); 391 392 if (chans) { 393 qsort(chans, num, sizeof(struct wpa_mbo_non_pref_channel), 394 wpa_non_pref_chan_cmp); 395 } 396 397 update: 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); 402 403 return 0; 404 405 fail: 406 os_free(chans); 407 os_free(cmd); 408 return -1; 409 } 410 411 412 void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie) 413 { 414 u8 *len; 415 416 wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); 417 len = wpabuf_put(ie, 1); 418 419 wpabuf_put_be24(ie, OUI_WFA); 420 wpabuf_put_u8(ie, MBO_OUI_TYPE); 421 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); 429 } 430 *len = (u8 *) wpabuf_put(ie, 0) - len - 1; 431 } 432 433 434 void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie, 435 size_t len) 436 { 437 const u8 *pos, *cell_pref = NULL; 438 u8 id, elen; 439 u16 disallowed_sec = 0; 440 441 if (len <= 4 || WPA_GET_BE24(mbo_ie) != OUI_WFA || 442 mbo_ie[3] != MBO_OUI_TYPE) 443 return; 444 445 pos = mbo_ie + 4; 446 len -= 4; 447 448 while (len >= 2) { 449 id = *pos++; 450 elen = *pos++; 451 len -= 2; 452 453 if (elen > len) 454 goto fail; 455 456 switch (id) { 457 case MBO_ATTR_ID_CELL_DATA_PREF: 458 if (elen != 1) 459 goto fail; 460 461 if (wpa_s->conf->mbo_cell_capa == 462 MBO_CELL_CAPA_AVAILABLE) 463 cell_pref = pos; 464 else 465 wpa_printf(MSG_DEBUG, 466 "MBO: Station does not support Cellular data connection"); 467 break; 468 case MBO_ATTR_ID_TRANSITION_REASON: 469 if (elen != 1) 470 goto fail; 471 472 wpa_s->wnm_mbo_trans_reason_present = 1; 473 wpa_s->wnm_mbo_transition_reason = *pos; 474 break; 475 case MBO_ATTR_ID_ASSOC_RETRY_DELAY: 476 if (elen != 2) 477 goto fail; 478 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"); 483 goto fail; 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", 489 disallowed_sec); 490 } else { 491 wpa_printf(MSG_DEBUG, 492 "MBO: Association retry delay attribute not in disassoc imminent mode"); 493 } 494 495 break; 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", 503 id); 504 break; 505 default: 506 wpa_printf(MSG_DEBUG, "MBO: Unknown attribute id %u", 507 id); 508 return; 509 } 510 511 pos += elen; 512 len -= elen; 513 } 514 515 if (cell_pref) 516 wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u", 517 *cell_pref); 518 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); 522 523 if (disallowed_sec && wpa_s->current_bss) 524 wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid, 525 disallowed_sec, 0); 526 527 return; 528 fail: 529 wpa_printf(MSG_DEBUG, "MBO IE parsing failed (id=%u len=%u left=%zu)", 530 id, elen, len); 531 } 532 533 534 size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos, 535 size_t len, 536 enum mbo_transition_reject_reason reason) 537 { 538 u8 reject_attr[3]; 539 540 reject_attr[0] = MBO_ATTR_ID_TRANSITION_REJECT_REASON; 541 reject_attr[1] = 1; 542 reject_attr[2] = reason; 543 544 return mbo_add_ie(pos, len, reject_attr, sizeof(reject_attr)); 545 } 546 547 548 void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa) 549 { 550 u8 cell_capa[7]; 551 552 if (wpa_s->conf->mbo_cell_capa == mbo_cell_capa) { 553 wpa_printf(MSG_DEBUG, 554 "MBO: Cellular capability already set to %u", 555 mbo_cell_capa); 556 return; 557 } 558 559 wpa_s->conf->mbo_cell_capa = mbo_cell_capa; 560 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; 566 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); 570 } 571 572 573 struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s, 574 struct wpa_bss *bss, u32 mbo_subtypes) 575 { 576 struct wpabuf *anqp_buf; 577 u8 *len_pos; 578 u8 i; 579 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)); 584 return NULL; 585 } 586 587 /* Allocate size for the maximum case - all MBO subtypes are set */ 588 anqp_buf = wpabuf_alloc(9 + MAX_MBO_ANQP_SUBTYPE); 589 if (!anqp_buf) 590 return NULL; 591 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); 595 596 wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_QUERY_LIST); 597 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); 602 } 603 604 gas_anqp_set_element_len(anqp_buf, len_pos); 605 606 return anqp_buf; 607 } 608 609 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) 613 { 614 const u8 *pos = data; 615 u8 subtype; 616 617 if (slen < 1) 618 return; 619 620 subtype = *pos++; 621 slen--; 622 623 switch (subtype) { 624 case MBO_ANQP_SUBTYPE_CELL_CONN_PREF: 625 if (slen < 1) 626 break; 627 wpa_msg(wpa_s, MSG_INFO, RX_MBO_ANQP MACSTR 628 " cell_conn_pref=%u", MAC2STR(sa), *pos); 629 break; 630 default: 631 wpa_printf(MSG_DEBUG, "MBO: Unsupported ANQP subtype %u", 632 subtype); 633 break; 634 } 635 } 636