1 /* 2 * wpa_supplicant - WNM 3 * Copyright (c) 2011-2013, Qualcomm Atheros, Inc. 4 * 5 * This software may be distributed under the terms of the BSD license. 6 * See README for more details. 7 */ 8 9 #include "utils/includes.h" 10 11 #include "utils/common.h" 12 #include "common/ieee802_11_defs.h" 13 #include "common/ieee802_11_common.h" 14 #include "common/wpa_ctrl.h" 15 #include "rsn_supp/wpa.h" 16 #include "config.h" 17 #include "wpa_supplicant_i.h" 18 #include "driver_i.h" 19 #include "scan.h" 20 #include "ctrl_iface.h" 21 #include "bss.h" 22 #include "wnm_sta.h" 23 #include "hs20_supplicant.h" 24 25 #define MAX_TFS_IE_LEN 1024 26 #define WNM_MAX_NEIGHBOR_REPORT 10 27 28 #define WNM_SCAN_RESULT_AGE 2 /* 2 seconds */ 29 30 /* get the TFS IE from driver */ 31 static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf, 32 u16 *buf_len, enum wnm_oper oper) 33 { 34 wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper); 35 36 return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len); 37 } 38 39 40 /* set the TFS IE to driver */ 41 static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s, 42 const u8 *addr, const u8 *buf, u16 buf_len, 43 enum wnm_oper oper) 44 { 45 u16 len = buf_len; 46 47 wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); 48 49 return wpa_drv_wnm_oper(wpa_s, oper, addr, (u8 *) buf, &len); 50 } 51 52 53 /* MLME-SLEEPMODE.request */ 54 int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, 55 u8 action, u16 intval, struct wpabuf *tfs_req) 56 { 57 struct ieee80211_mgmt *mgmt; 58 int res; 59 size_t len; 60 struct wnm_sleep_element *wnmsleep_ie; 61 u8 *wnmtfs_ie; 62 u8 wnmsleep_ie_len; 63 u16 wnmtfs_ie_len; /* possibly multiple IE(s) */ 64 enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD : 65 WNM_SLEEP_TFS_REQ_IE_NONE; 66 67 wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request " 68 "action=%s to " MACSTR, 69 action == 0 ? "enter" : "exit", 70 MAC2STR(wpa_s->bssid)); 71 72 /* WNM-Sleep Mode IE */ 73 wnmsleep_ie_len = sizeof(struct wnm_sleep_element); 74 wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element)); 75 if (wnmsleep_ie == NULL) 76 return -1; 77 wnmsleep_ie->eid = WLAN_EID_WNMSLEEP; 78 wnmsleep_ie->len = wnmsleep_ie_len - 2; 79 wnmsleep_ie->action_type = action; 80 wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT; 81 wnmsleep_ie->intval = host_to_le16(intval); 82 wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element", 83 (u8 *) wnmsleep_ie, wnmsleep_ie_len); 84 85 /* TFS IE(s) */ 86 if (tfs_req) { 87 wnmtfs_ie_len = wpabuf_len(tfs_req); 88 wnmtfs_ie = os_memdup(wpabuf_head(tfs_req), wnmtfs_ie_len); 89 if (wnmtfs_ie == NULL) { 90 os_free(wnmsleep_ie); 91 return -1; 92 } 93 } else { 94 wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); 95 if (wnmtfs_ie == NULL) { 96 os_free(wnmsleep_ie); 97 return -1; 98 } 99 if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len, 100 tfs_oper)) { 101 wnmtfs_ie_len = 0; 102 os_free(wnmtfs_ie); 103 wnmtfs_ie = NULL; 104 } 105 } 106 wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element", 107 (u8 *) wnmtfs_ie, wnmtfs_ie_len); 108 109 mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len); 110 if (mgmt == NULL) { 111 wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " 112 "WNM-Sleep Request action frame"); 113 os_free(wnmsleep_ie); 114 os_free(wnmtfs_ie); 115 return -1; 116 } 117 118 os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); 119 os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); 120 os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); 121 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 122 WLAN_FC_STYPE_ACTION); 123 mgmt->u.action.category = WLAN_ACTION_WNM; 124 mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ; 125 mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1; 126 os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie, 127 wnmsleep_ie_len); 128 /* copy TFS IE here */ 129 if (wnmtfs_ie_len > 0) { 130 os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable + 131 wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len); 132 } 133 134 len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len + 135 wnmtfs_ie_len; 136 137 res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, 138 wpa_s->own_addr, wpa_s->bssid, 139 &mgmt->u.action.category, len, 0); 140 if (res < 0) 141 wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request " 142 "(action=%d, intval=%d)", action, intval); 143 else 144 wpa_s->wnmsleep_used = 1; 145 146 os_free(wnmsleep_ie); 147 os_free(wnmtfs_ie); 148 os_free(mgmt); 149 150 return res; 151 } 152 153 154 static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s, 155 const u8 *tfsresp_ie_start, 156 const u8 *tfsresp_ie_end) 157 { 158 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM, 159 wpa_s->bssid, NULL, NULL); 160 /* remove GTK/IGTK ?? */ 161 162 /* set the TFS Resp IE(s) */ 163 if (tfsresp_ie_start && tfsresp_ie_end && 164 tfsresp_ie_end - tfsresp_ie_start >= 0) { 165 u16 tfsresp_ie_len; 166 tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) - 167 tfsresp_ie_start; 168 wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found"); 169 /* pass the TFS Resp IE(s) to driver for processing */ 170 if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid, 171 tfsresp_ie_start, 172 tfsresp_ie_len, 173 WNM_SLEEP_TFS_RESP_IE_SET)) 174 wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE"); 175 } 176 } 177 178 179 static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s, 180 const u8 *frm, u16 key_len_total) 181 { 182 u8 *ptr, *end; 183 u8 gtk_len; 184 185 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM, wpa_s->bssid, 186 NULL, NULL); 187 188 /* Install GTK/IGTK */ 189 190 /* point to key data field */ 191 ptr = (u8 *) frm + 1 + 2; 192 end = ptr + key_len_total; 193 wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total); 194 195 if (key_len_total && !wpa_sm_pmf_enabled(wpa_s->wpa)) { 196 wpa_msg(wpa_s, MSG_INFO, 197 "WNM: Ignore Key Data in WNM-Sleep Mode Response - PMF not enabled"); 198 return; 199 } 200 201 while (end - ptr > 1) { 202 if (2 + ptr[1] > end - ptr) { 203 wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element " 204 "length"); 205 if (end > ptr) { 206 wpa_hexdump(MSG_DEBUG, "WNM: Remaining data", 207 ptr, end - ptr); 208 } 209 break; 210 } 211 if (*ptr == WNM_SLEEP_SUBELEM_GTK) { 212 if (ptr[1] < 11 + 5) { 213 wpa_printf(MSG_DEBUG, "WNM: Too short GTK " 214 "subelem"); 215 break; 216 } 217 gtk_len = *(ptr + 4); 218 if (ptr[1] < 11 + gtk_len || 219 gtk_len < 5 || gtk_len > 32) { 220 wpa_printf(MSG_DEBUG, "WNM: Invalid GTK " 221 "subelem"); 222 break; 223 } 224 wpa_wnmsleep_install_key( 225 wpa_s->wpa, 226 WNM_SLEEP_SUBELEM_GTK, 227 ptr); 228 ptr += 13 + gtk_len; 229 #ifdef CONFIG_IEEE80211W 230 } else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) { 231 if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) { 232 wpa_printf(MSG_DEBUG, "WNM: Too short IGTK " 233 "subelem"); 234 break; 235 } 236 wpa_wnmsleep_install_key(wpa_s->wpa, 237 WNM_SLEEP_SUBELEM_IGTK, ptr); 238 ptr += 10 + WPA_IGTK_LEN; 239 #endif /* CONFIG_IEEE80211W */ 240 } else 241 break; /* skip the loop */ 242 } 243 } 244 245 246 static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, 247 const u8 *frm, int len) 248 { 249 /* 250 * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data | 251 * WNM-Sleep Mode IE | TFS Response IE 252 */ 253 const u8 *pos = frm; /* point to payload after the action field */ 254 u16 key_len_total; 255 struct wnm_sleep_element *wnmsleep_ie = NULL; 256 /* multiple TFS Resp IE (assuming consecutive) */ 257 const u8 *tfsresp_ie_start = NULL; 258 const u8 *tfsresp_ie_end = NULL; 259 size_t left; 260 261 if (!wpa_s->wnmsleep_used) { 262 wpa_printf(MSG_DEBUG, 263 "WNM: Ignore WNM-Sleep Mode Response frame since WNM-Sleep Mode operation has not been requested"); 264 return; 265 } 266 267 if (len < 3) 268 return; 269 key_len_total = WPA_GET_LE16(frm + 1); 270 271 wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d", 272 frm[0], key_len_total); 273 left = len - 3; 274 if (key_len_total > left) { 275 wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field"); 276 return; 277 } 278 pos += 3 + key_len_total; 279 while (pos - frm + 1 < len) { 280 u8 ie_len = *(pos + 1); 281 if (2 + ie_len > frm + len - pos) { 282 wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len); 283 break; 284 } 285 wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len); 286 if (*pos == WLAN_EID_WNMSLEEP && ie_len >= 4) 287 wnmsleep_ie = (struct wnm_sleep_element *) pos; 288 else if (*pos == WLAN_EID_TFS_RESP) { 289 if (!tfsresp_ie_start) 290 tfsresp_ie_start = pos; 291 tfsresp_ie_end = pos; 292 } else 293 wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos); 294 pos += ie_len + 2; 295 } 296 297 if (!wnmsleep_ie) { 298 wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found"); 299 return; 300 } 301 302 wpa_s->wnmsleep_used = 0; 303 304 if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT || 305 wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) { 306 wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response " 307 "frame (action=%d, intval=%d)", 308 wnmsleep_ie->action_type, wnmsleep_ie->intval); 309 if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) { 310 wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start, 311 tfsresp_ie_end); 312 } else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) { 313 wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total); 314 } 315 } else { 316 wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame " 317 "(action=%d, intval=%d)", 318 wnmsleep_ie->action_type, wnmsleep_ie->intval); 319 if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) 320 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL, 321 wpa_s->bssid, NULL, NULL); 322 else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) 323 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL, 324 wpa_s->bssid, NULL, NULL); 325 } 326 } 327 328 329 void wnm_deallocate_memory(struct wpa_supplicant *wpa_s) 330 { 331 int i; 332 333 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { 334 os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot); 335 os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid); 336 } 337 338 wpa_s->wnm_num_neighbor_report = 0; 339 os_free(wpa_s->wnm_neighbor_report_elements); 340 wpa_s->wnm_neighbor_report_elements = NULL; 341 342 wpabuf_free(wpa_s->coloc_intf_elems); 343 wpa_s->coloc_intf_elems = NULL; 344 } 345 346 347 static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep, 348 u8 id, u8 elen, const u8 *pos) 349 { 350 switch (id) { 351 case WNM_NEIGHBOR_TSF: 352 if (elen < 2 + 2) { 353 wpa_printf(MSG_DEBUG, "WNM: Too short TSF"); 354 break; 355 } 356 rep->tsf_offset = WPA_GET_LE16(pos); 357 rep->beacon_int = WPA_GET_LE16(pos + 2); 358 rep->tsf_present = 1; 359 break; 360 case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING: 361 if (elen < 2) { 362 wpa_printf(MSG_DEBUG, "WNM: Too short condensed " 363 "country string"); 364 break; 365 } 366 os_memcpy(rep->country, pos, 2); 367 rep->country_present = 1; 368 break; 369 case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE: 370 if (elen < 1) { 371 wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition " 372 "candidate"); 373 break; 374 } 375 rep->preference = pos[0]; 376 rep->preference_present = 1; 377 break; 378 case WNM_NEIGHBOR_BSS_TERMINATION_DURATION: 379 rep->bss_term_tsf = WPA_GET_LE64(pos); 380 rep->bss_term_dur = WPA_GET_LE16(pos + 8); 381 rep->bss_term_present = 1; 382 break; 383 case WNM_NEIGHBOR_BEARING: 384 if (elen < 8) { 385 wpa_printf(MSG_DEBUG, "WNM: Too short neighbor " 386 "bearing"); 387 break; 388 } 389 rep->bearing = WPA_GET_LE16(pos); 390 rep->distance = WPA_GET_LE32(pos + 2); 391 rep->rel_height = WPA_GET_LE16(pos + 2 + 4); 392 rep->bearing_present = 1; 393 break; 394 case WNM_NEIGHBOR_MEASUREMENT_PILOT: 395 if (elen < 1) { 396 wpa_printf(MSG_DEBUG, "WNM: Too short measurement " 397 "pilot"); 398 break; 399 } 400 os_free(rep->meas_pilot); 401 rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot)); 402 if (rep->meas_pilot == NULL) 403 break; 404 rep->meas_pilot->measurement_pilot = pos[0]; 405 rep->meas_pilot->subelem_len = elen - 1; 406 os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1); 407 break; 408 case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES: 409 if (elen < 5) { 410 wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled " 411 "capabilities"); 412 break; 413 } 414 os_memcpy(rep->rm_capab, pos, 5); 415 rep->rm_capab_present = 1; 416 break; 417 case WNM_NEIGHBOR_MULTIPLE_BSSID: 418 if (elen < 1) { 419 wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID"); 420 break; 421 } 422 os_free(rep->mul_bssid); 423 rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid)); 424 if (rep->mul_bssid == NULL) 425 break; 426 rep->mul_bssid->max_bssid_indicator = pos[0]; 427 rep->mul_bssid->subelem_len = elen - 1; 428 os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1); 429 break; 430 } 431 } 432 433 434 static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan) 435 { 436 struct wpa_bss *bss = wpa_s->current_bss; 437 const char *country = NULL; 438 int freq; 439 440 if (bss) { 441 const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY); 442 443 if (elem && elem[1] >= 2) 444 country = (const char *) (elem + 2); 445 } 446 447 freq = ieee80211_chan_to_freq(country, op_class, chan); 448 if (freq <= 0 && op_class == 0) { 449 /* 450 * Some APs do not advertise correct operating class 451 * information. Try to determine the most likely operating 452 * frequency based on the channel number. 453 */ 454 if (chan >= 1 && chan <= 13) 455 freq = 2407 + chan * 5; 456 else if (chan == 14) 457 freq = 2484; 458 else if (chan >= 36 && chan <= 169) 459 freq = 5000 + chan * 5; 460 } 461 return freq; 462 } 463 464 465 static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s, 466 const u8 *pos, u8 len, 467 struct neighbor_report *rep) 468 { 469 u8 left = len; 470 471 if (left < 13) { 472 wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report"); 473 return; 474 } 475 476 os_memcpy(rep->bssid, pos, ETH_ALEN); 477 rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN); 478 rep->regulatory_class = *(pos + 10); 479 rep->channel_number = *(pos + 11); 480 rep->phy_type = *(pos + 12); 481 482 pos += 13; 483 left -= 13; 484 485 while (left >= 2) { 486 u8 id, elen; 487 488 id = *pos++; 489 elen = *pos++; 490 wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen); 491 left -= 2; 492 if (elen > left) { 493 wpa_printf(MSG_DEBUG, 494 "WNM: Truncated neighbor report subelement"); 495 break; 496 } 497 wnm_parse_neighbor_report_elem(rep, id, elen, pos); 498 left -= elen; 499 pos += elen; 500 } 501 502 rep->freq = wnm_nei_get_chan(wpa_s, rep->regulatory_class, 503 rep->channel_number); 504 } 505 506 507 static void wnm_clear_acceptable(struct wpa_supplicant *wpa_s) 508 { 509 unsigned int i; 510 511 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) 512 wpa_s->wnm_neighbor_report_elements[i].acceptable = 0; 513 } 514 515 516 static struct wpa_bss * get_first_acceptable(struct wpa_supplicant *wpa_s) 517 { 518 unsigned int i; 519 struct neighbor_report *nei; 520 521 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { 522 nei = &wpa_s->wnm_neighbor_report_elements[i]; 523 if (nei->acceptable) 524 return wpa_bss_get_bssid(wpa_s, nei->bssid); 525 } 526 527 return NULL; 528 } 529 530 531 #ifdef CONFIG_MBO 532 static struct wpa_bss * 533 get_mbo_transition_candidate(struct wpa_supplicant *wpa_s, 534 enum mbo_transition_reject_reason *reason) 535 { 536 struct wpa_bss *target = NULL; 537 struct wpa_bss_trans_info params; 538 struct wpa_bss_candidate_info *info = NULL; 539 struct neighbor_report *nei = wpa_s->wnm_neighbor_report_elements; 540 u8 *first_candidate_bssid = NULL, *pos; 541 unsigned int i; 542 543 params.mbo_transition_reason = wpa_s->wnm_mbo_transition_reason; 544 params.n_candidates = 0; 545 params.bssid = os_calloc(wpa_s->wnm_num_neighbor_report, ETH_ALEN); 546 if (!params.bssid) 547 return NULL; 548 549 pos = params.bssid; 550 for (i = 0; i < wpa_s->wnm_num_neighbor_report; nei++, i++) { 551 if (nei->is_first) 552 first_candidate_bssid = nei->bssid; 553 if (!nei->acceptable) 554 continue; 555 os_memcpy(pos, nei->bssid, ETH_ALEN); 556 pos += ETH_ALEN; 557 params.n_candidates++; 558 } 559 560 if (!params.n_candidates) 561 goto end; 562 563 info = wpa_drv_get_bss_trans_status(wpa_s, ¶ms); 564 if (!info) { 565 /* If failed to get candidate BSS transition status from driver, 566 * get the first acceptable candidate from wpa_supplicant. 567 */ 568 target = wpa_bss_get_bssid(wpa_s, params.bssid); 569 goto end; 570 } 571 572 /* Get the first acceptable candidate from driver */ 573 for (i = 0; i < info->num; i++) { 574 if (info->candidates[i].is_accept) { 575 target = wpa_bss_get_bssid(wpa_s, 576 info->candidates[i].bssid); 577 goto end; 578 } 579 } 580 581 /* If Disassociation Imminent is set and driver rejects all the 582 * candidate select first acceptable candidate which has 583 * rssi > disassoc_imminent_rssi_threshold 584 */ 585 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { 586 for (i = 0; i < info->num; i++) { 587 target = wpa_bss_get_bssid(wpa_s, 588 info->candidates[i].bssid); 589 if (target && 590 (target->level < 591 wpa_s->conf->disassoc_imminent_rssi_threshold)) 592 continue; 593 goto end; 594 } 595 } 596 597 /* While sending BTM reject use reason code of the first candidate 598 * received in BTM request frame 599 */ 600 if (reason) { 601 for (i = 0; i < info->num; i++) { 602 if (first_candidate_bssid && 603 os_memcmp(first_candidate_bssid, 604 info->candidates[i].bssid, ETH_ALEN) == 0) 605 { 606 *reason = info->candidates[i].reject_reason; 607 break; 608 } 609 } 610 } 611 612 target = NULL; 613 614 end: 615 os_free(params.bssid); 616 if (info) { 617 os_free(info->candidates); 618 os_free(info); 619 } 620 return target; 621 } 622 #endif /* CONFIG_MBO */ 623 624 625 static struct wpa_bss * 626 compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs, 627 enum mbo_transition_reject_reason *reason) 628 { 629 u8 i; 630 struct wpa_bss *bss = wpa_s->current_bss; 631 struct wpa_bss *target; 632 633 if (!bss) 634 return NULL; 635 636 wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d", 637 MAC2STR(wpa_s->bssid), bss->level); 638 639 wnm_clear_acceptable(wpa_s); 640 641 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { 642 struct neighbor_report *nei; 643 644 nei = &wpa_s->wnm_neighbor_report_elements[i]; 645 if (nei->preference_present && nei->preference == 0) { 646 wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR, 647 MAC2STR(nei->bssid)); 648 continue; 649 } 650 651 target = wpa_bss_get_bssid(wpa_s, nei->bssid); 652 if (!target) { 653 wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR 654 " (pref %d) not found in scan results", 655 MAC2STR(nei->bssid), 656 nei->preference_present ? nei->preference : 657 -1); 658 continue; 659 } 660 661 if (age_secs) { 662 struct os_reltime now; 663 664 if (os_get_reltime(&now) == 0 && 665 os_reltime_expired(&now, &target->last_update, 666 age_secs)) { 667 wpa_printf(MSG_DEBUG, 668 "Candidate BSS is more than %ld seconds old", 669 age_secs); 670 continue; 671 } 672 } 673 674 if (bss->ssid_len != target->ssid_len || 675 os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) { 676 /* 677 * TODO: Could consider allowing transition to another 678 * ESS if PMF was enabled for the association. 679 */ 680 wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR 681 " (pref %d) in different ESS", 682 MAC2STR(nei->bssid), 683 nei->preference_present ? nei->preference : 684 -1); 685 continue; 686 } 687 688 if (wpa_s->current_ssid && 689 !wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid, 690 1, 0)) { 691 wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR 692 " (pref %d) does not match the current network profile", 693 MAC2STR(nei->bssid), 694 nei->preference_present ? nei->preference : 695 -1); 696 continue; 697 } 698 699 if (wpa_is_bss_tmp_disallowed(wpa_s, target->bssid)) { 700 wpa_printf(MSG_DEBUG, 701 "MBO: Candidate BSS " MACSTR 702 " retry delay is not over yet", 703 MAC2STR(nei->bssid)); 704 continue; 705 } 706 707 if (target->level < bss->level && target->level < -80) { 708 wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR 709 " (pref %d) does not have sufficient signal level (%d)", 710 MAC2STR(nei->bssid), 711 nei->preference_present ? nei->preference : 712 -1, 713 target->level); 714 continue; 715 } 716 717 nei->acceptable = 1; 718 } 719 720 #ifdef CONFIG_MBO 721 if (wpa_s->wnm_mbo_trans_reason_present) 722 target = get_mbo_transition_candidate(wpa_s, reason); 723 else 724 target = get_first_acceptable(wpa_s); 725 #else /* CONFIG_MBO */ 726 target = get_first_acceptable(wpa_s); 727 #endif /* CONFIG_MBO */ 728 729 if (target) { 730 wpa_printf(MSG_DEBUG, 731 "WNM: Found an acceptable preferred transition candidate BSS " 732 MACSTR " (RSSI %d)", 733 MAC2STR(target->bssid), target->level); 734 } 735 736 return target; 737 } 738 739 740 static int wpa_bss_ies_eq(struct wpa_bss *a, struct wpa_bss *b, u8 eid) 741 { 742 const u8 *ie_a, *ie_b; 743 744 if (!a || !b) 745 return 0; 746 747 ie_a = wpa_bss_get_ie(a, eid); 748 ie_b = wpa_bss_get_ie(b, eid); 749 750 if (!ie_a || !ie_b || ie_a[1] != ie_b[1]) 751 return 0; 752 753 return os_memcmp(ie_a, ie_b, ie_a[1]) == 0; 754 } 755 756 757 static u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) 758 { 759 u32 info = 0; 760 761 info |= NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH; 762 763 /* 764 * Leave the security and key scope bits unset to indicate that the 765 * security information is not available. 766 */ 767 768 if (bss->caps & WLAN_CAPABILITY_SPECTRUM_MGMT) 769 info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT; 770 if (bss->caps & WLAN_CAPABILITY_QOS) 771 info |= NEI_REP_BSSID_INFO_QOS; 772 if (bss->caps & WLAN_CAPABILITY_APSD) 773 info |= NEI_REP_BSSID_INFO_APSD; 774 if (bss->caps & WLAN_CAPABILITY_RADIO_MEASUREMENT) 775 info |= NEI_REP_BSSID_INFO_RM; 776 if (bss->caps & WLAN_CAPABILITY_DELAYED_BLOCK_ACK) 777 info |= NEI_REP_BSSID_INFO_DELAYED_BA; 778 if (bss->caps & WLAN_CAPABILITY_IMM_BLOCK_ACK) 779 info |= NEI_REP_BSSID_INFO_IMM_BA; 780 if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_MOBILITY_DOMAIN)) 781 info |= NEI_REP_BSSID_INFO_MOBILITY_DOMAIN; 782 if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_HT_CAP)) 783 info |= NEI_REP_BSSID_INFO_HT; 784 785 return info; 786 } 787 788 789 static int wnm_add_nei_rep(struct wpabuf **buf, const u8 *bssid, 790 u32 bss_info, u8 op_class, u8 chan, u8 phy_type, 791 u8 pref) 792 { 793 if (wpabuf_len(*buf) + 18 > 794 IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN) { 795 wpa_printf(MSG_DEBUG, 796 "WNM: No room in frame for Neighbor Report element"); 797 return -1; 798 } 799 800 if (wpabuf_resize(buf, 18) < 0) { 801 wpa_printf(MSG_DEBUG, 802 "WNM: Failed to allocate memory for Neighbor Report element"); 803 return -1; 804 } 805 806 wpabuf_put_u8(*buf, WLAN_EID_NEIGHBOR_REPORT); 807 /* length: 13 for basic neighbor report + 3 for preference subelement */ 808 wpabuf_put_u8(*buf, 16); 809 wpabuf_put_data(*buf, bssid, ETH_ALEN); 810 wpabuf_put_le32(*buf, bss_info); 811 wpabuf_put_u8(*buf, op_class); 812 wpabuf_put_u8(*buf, chan); 813 wpabuf_put_u8(*buf, phy_type); 814 wpabuf_put_u8(*buf, WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE); 815 wpabuf_put_u8(*buf, 1); 816 wpabuf_put_u8(*buf, pref); 817 return 0; 818 } 819 820 821 static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s, 822 struct wpa_bss *bss, struct wpabuf **buf, 823 u8 pref) 824 { 825 const u8 *ie; 826 u8 op_class, chan; 827 int sec_chan = 0, vht = 0; 828 enum phy_type phy_type; 829 u32 info; 830 struct ieee80211_ht_operation *ht_oper = NULL; 831 struct ieee80211_vht_operation *vht_oper = NULL; 832 833 ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION); 834 if (ie && ie[1] >= 2) { 835 ht_oper = (struct ieee80211_ht_operation *) (ie + 2); 836 837 if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) 838 sec_chan = 1; 839 else if (ht_oper->ht_param & 840 HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) 841 sec_chan = -1; 842 } 843 844 ie = wpa_bss_get_ie(bss, WLAN_EID_VHT_OPERATION); 845 if (ie && ie[1] >= 1) { 846 vht_oper = (struct ieee80211_vht_operation *) (ie + 2); 847 848 if (vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_80MHZ || 849 vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_160MHZ || 850 vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_80P80MHZ) 851 vht = vht_oper->vht_op_info_chwidth; 852 } 853 854 if (ieee80211_freq_to_channel_ext(bss->freq, sec_chan, vht, &op_class, 855 &chan) == NUM_HOSTAPD_MODES) { 856 wpa_printf(MSG_DEBUG, 857 "WNM: Cannot determine operating class and channel"); 858 return -2; 859 } 860 861 phy_type = ieee80211_get_phy_type(bss->freq, (ht_oper != NULL), 862 (vht_oper != NULL)); 863 if (phy_type == PHY_TYPE_UNSPECIFIED) { 864 wpa_printf(MSG_DEBUG, 865 "WNM: Cannot determine BSS phy type for Neighbor Report"); 866 return -2; 867 } 868 869 info = wnm_get_bss_info(wpa_s, bss); 870 871 return wnm_add_nei_rep(buf, bss->bssid, info, op_class, chan, phy_type, 872 pref); 873 } 874 875 876 static void wnm_add_cand_list(struct wpa_supplicant *wpa_s, struct wpabuf **buf) 877 { 878 unsigned int i, pref = 255; 879 struct os_reltime now; 880 struct wpa_ssid *ssid = wpa_s->current_ssid; 881 882 if (!ssid) 883 return; 884 885 /* 886 * TODO: Define when scan results are no longer valid for the candidate 887 * list. 888 */ 889 os_get_reltime(&now); 890 if (os_reltime_expired(&now, &wpa_s->last_scan, 10)) 891 return; 892 893 wpa_printf(MSG_DEBUG, 894 "WNM: Add candidate list to BSS Transition Management Response frame"); 895 for (i = 0; i < wpa_s->last_scan_res_used && pref; i++) { 896 struct wpa_bss *bss = wpa_s->last_scan_res[i]; 897 int res; 898 899 if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0)) { 900 res = wnm_nei_rep_add_bss(wpa_s, bss, buf, pref--); 901 if (res == -2) 902 continue; /* could not build entry for BSS */ 903 if (res < 0) 904 break; /* no more room for candidates */ 905 if (pref == 1) 906 break; 907 } 908 } 909 910 wpa_hexdump_buf(MSG_DEBUG, 911 "WNM: BSS Transition Management Response candidate list", 912 *buf); 913 } 914 915 916 #define BTM_RESP_MIN_SIZE 5 + ETH_ALEN 917 918 static void wnm_send_bss_transition_mgmt_resp( 919 struct wpa_supplicant *wpa_s, u8 dialog_token, 920 enum bss_trans_mgmt_status_code status, 921 enum mbo_transition_reject_reason reason, 922 u8 delay, const u8 *target_bssid) 923 { 924 struct wpabuf *buf; 925 int res; 926 927 wpa_printf(MSG_DEBUG, 928 "WNM: Send BSS Transition Management Response to " MACSTR 929 " dialog_token=%u status=%u reason=%u delay=%d", 930 MAC2STR(wpa_s->bssid), dialog_token, status, reason, delay); 931 if (!wpa_s->current_bss) { 932 wpa_printf(MSG_DEBUG, 933 "WNM: Current BSS not known - drop response"); 934 return; 935 } 936 937 buf = wpabuf_alloc(BTM_RESP_MIN_SIZE); 938 if (!buf) { 939 wpa_printf(MSG_DEBUG, 940 "WNM: Failed to allocate memory for BTM response"); 941 return; 942 } 943 944 wpabuf_put_u8(buf, WLAN_ACTION_WNM); 945 wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_RESP); 946 wpabuf_put_u8(buf, dialog_token); 947 wpabuf_put_u8(buf, status); 948 wpabuf_put_u8(buf, delay); 949 if (target_bssid) { 950 wpabuf_put_data(buf, target_bssid, ETH_ALEN); 951 } else if (status == WNM_BSS_TM_ACCEPT) { 952 /* 953 * P802.11-REVmc clarifies that the Target BSSID field is always 954 * present when status code is zero, so use a fake value here if 955 * no BSSID is yet known. 956 */ 957 wpabuf_put_data(buf, "\0\0\0\0\0\0", ETH_ALEN); 958 } 959 960 if (status == WNM_BSS_TM_ACCEPT) 961 wnm_add_cand_list(wpa_s, &buf); 962 963 #ifdef CONFIG_MBO 964 if (status != WNM_BSS_TM_ACCEPT && 965 wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE)) { 966 u8 mbo[10]; 967 size_t ret; 968 969 ret = wpas_mbo_ie_bss_trans_reject(wpa_s, mbo, sizeof(mbo), 970 reason); 971 if (ret) { 972 if (wpabuf_resize(&buf, ret) < 0) { 973 wpabuf_free(buf); 974 wpa_printf(MSG_DEBUG, 975 "WNM: Failed to allocate memory for MBO IE"); 976 return; 977 } 978 979 wpabuf_put_data(buf, mbo, ret); 980 } 981 } 982 #endif /* CONFIG_MBO */ 983 984 res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, 985 wpa_s->own_addr, wpa_s->bssid, 986 wpabuf_head_u8(buf), wpabuf_len(buf), 0); 987 if (res < 0) { 988 wpa_printf(MSG_DEBUG, 989 "WNM: Failed to send BSS Transition Management Response"); 990 } 991 992 wpabuf_free(buf); 993 } 994 995 996 static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s, 997 struct wpa_bss *bss, struct wpa_ssid *ssid, 998 int after_new_scan) 999 { 1000 wpa_dbg(wpa_s, MSG_DEBUG, 1001 "WNM: Transition to BSS " MACSTR 1002 " based on BSS Transition Management Request (old BSSID " 1003 MACSTR " after_new_scan=%d)", 1004 MAC2STR(bss->bssid), MAC2STR(wpa_s->bssid), after_new_scan); 1005 1006 /* Send the BSS Management Response - Accept */ 1007 if (wpa_s->wnm_reply) { 1008 wpa_s->wnm_reply = 0; 1009 wpa_printf(MSG_DEBUG, 1010 "WNM: Sending successful BSS Transition Management Response"); 1011 wnm_send_bss_transition_mgmt_resp( 1012 wpa_s, wpa_s->wnm_dialog_token, WNM_BSS_TM_ACCEPT, 1013 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, 1014 bss->bssid); 1015 } 1016 1017 if (bss == wpa_s->current_bss) { 1018 wpa_printf(MSG_DEBUG, 1019 "WNM: Already associated with the preferred candidate"); 1020 wnm_deallocate_memory(wpa_s); 1021 return; 1022 } 1023 1024 wpa_s->reassociate = 1; 1025 wpa_printf(MSG_DEBUG, "WNM: Issuing connect"); 1026 wpa_supplicant_connect(wpa_s, bss, ssid); 1027 wnm_deallocate_memory(wpa_s); 1028 } 1029 1030 1031 int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail) 1032 { 1033 struct wpa_bss *bss; 1034 struct wpa_ssid *ssid = wpa_s->current_ssid; 1035 enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED; 1036 enum mbo_transition_reject_reason reason = 1037 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED; 1038 1039 if (!wpa_s->wnm_neighbor_report_elements) 1040 return 0; 1041 1042 wpa_dbg(wpa_s, MSG_DEBUG, 1043 "WNM: Process scan results for BSS Transition Management"); 1044 if (os_reltime_before(&wpa_s->wnm_cand_valid_until, 1045 &wpa_s->scan_trigger_time)) { 1046 wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it"); 1047 wnm_deallocate_memory(wpa_s); 1048 return 0; 1049 } 1050 1051 if (!wpa_s->current_bss || 1052 os_memcmp(wpa_s->wnm_cand_from_bss, wpa_s->current_bss->bssid, 1053 ETH_ALEN) != 0) { 1054 wpa_printf(MSG_DEBUG, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it"); 1055 return 0; 1056 } 1057 1058 /* Compare the Neighbor Report and scan results */ 1059 bss = compare_scan_neighbor_results(wpa_s, 0, &reason); 1060 if (!bss) { 1061 wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found"); 1062 status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES; 1063 goto send_bss_resp_fail; 1064 } 1065 1066 /* Associate to the network */ 1067 wnm_bss_tm_connect(wpa_s, bss, ssid, 1); 1068 return 1; 1069 1070 send_bss_resp_fail: 1071 if (!reply_on_fail) 1072 return 0; 1073 1074 /* Send reject response for all the failures */ 1075 1076 if (wpa_s->wnm_reply) { 1077 wpa_s->wnm_reply = 0; 1078 wnm_send_bss_transition_mgmt_resp(wpa_s, 1079 wpa_s->wnm_dialog_token, 1080 status, reason, 0, NULL); 1081 } 1082 wnm_deallocate_memory(wpa_s); 1083 1084 return 0; 1085 } 1086 1087 1088 static int cand_pref_compar(const void *a, const void *b) 1089 { 1090 const struct neighbor_report *aa = a; 1091 const struct neighbor_report *bb = b; 1092 1093 if (!aa->preference_present && !bb->preference_present) 1094 return 0; 1095 if (!aa->preference_present) 1096 return 1; 1097 if (!bb->preference_present) 1098 return -1; 1099 if (bb->preference > aa->preference) 1100 return 1; 1101 if (bb->preference < aa->preference) 1102 return -1; 1103 return 0; 1104 } 1105 1106 1107 static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s) 1108 { 1109 if (!wpa_s->wnm_neighbor_report_elements) 1110 return; 1111 qsort(wpa_s->wnm_neighbor_report_elements, 1112 wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report), 1113 cand_pref_compar); 1114 } 1115 1116 1117 static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s) 1118 { 1119 unsigned int i; 1120 1121 wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List"); 1122 if (!wpa_s->wnm_neighbor_report_elements) 1123 return; 1124 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { 1125 struct neighbor_report *nei; 1126 1127 nei = &wpa_s->wnm_neighbor_report_elements[i]; 1128 wpa_printf(MSG_DEBUG, "%u: " MACSTR 1129 " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d", 1130 i, MAC2STR(nei->bssid), nei->bssid_info, 1131 nei->regulatory_class, 1132 nei->channel_number, nei->phy_type, 1133 nei->preference_present ? nei->preference : -1, 1134 nei->freq); 1135 } 1136 } 1137 1138 1139 static int chan_supported(struct wpa_supplicant *wpa_s, int freq) 1140 { 1141 unsigned int i; 1142 1143 for (i = 0; i < wpa_s->hw.num_modes; i++) { 1144 struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i]; 1145 int j; 1146 1147 for (j = 0; j < mode->num_channels; j++) { 1148 struct hostapd_channel_data *chan; 1149 1150 chan = &mode->channels[j]; 1151 if (chan->freq == freq && 1152 !(chan->flag & HOSTAPD_CHAN_DISABLED)) 1153 return 1; 1154 } 1155 } 1156 1157 return 0; 1158 } 1159 1160 1161 static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s) 1162 { 1163 int *freqs; 1164 int num_freqs = 0; 1165 unsigned int i; 1166 1167 if (!wpa_s->wnm_neighbor_report_elements) 1168 return; 1169 1170 if (wpa_s->hw.modes == NULL) 1171 return; 1172 1173 os_free(wpa_s->next_scan_freqs); 1174 wpa_s->next_scan_freqs = NULL; 1175 1176 freqs = os_calloc(wpa_s->wnm_num_neighbor_report + 1, sizeof(int)); 1177 if (freqs == NULL) 1178 return; 1179 1180 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { 1181 struct neighbor_report *nei; 1182 1183 nei = &wpa_s->wnm_neighbor_report_elements[i]; 1184 if (nei->freq <= 0) { 1185 wpa_printf(MSG_DEBUG, 1186 "WNM: Unknown neighbor operating frequency for " 1187 MACSTR " - scan all channels", 1188 MAC2STR(nei->bssid)); 1189 os_free(freqs); 1190 return; 1191 } 1192 if (chan_supported(wpa_s, nei->freq)) 1193 add_freq(freqs, &num_freqs, nei->freq); 1194 } 1195 1196 if (num_freqs == 0) { 1197 os_free(freqs); 1198 return; 1199 } 1200 1201 wpa_printf(MSG_DEBUG, 1202 "WNM: Scan %d frequencies based on transition candidate list", 1203 num_freqs); 1204 wpa_s->next_scan_freqs = freqs; 1205 } 1206 1207 1208 static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s) 1209 { 1210 struct wpa_scan_results *scan_res; 1211 struct wpa_bss *bss; 1212 struct wpa_ssid *ssid = wpa_s->current_ssid; 1213 u8 i, found = 0; 1214 size_t j; 1215 1216 wpa_dbg(wpa_s, MSG_DEBUG, 1217 "WNM: Fetch current scan results from the driver for checking transition candidates"); 1218 scan_res = wpa_drv_get_scan_results2(wpa_s); 1219 if (!scan_res) { 1220 wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Failed to get scan results"); 1221 return 0; 1222 } 1223 1224 if (scan_res->fetch_time.sec == 0) 1225 os_get_reltime(&scan_res->fetch_time); 1226 1227 filter_scan_res(wpa_s, scan_res); 1228 1229 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { 1230 struct neighbor_report *nei; 1231 1232 nei = &wpa_s->wnm_neighbor_report_elements[i]; 1233 if (nei->preference_present && nei->preference == 0) 1234 continue; 1235 1236 for (j = 0; j < scan_res->num; j++) { 1237 struct wpa_scan_res *res; 1238 const u8 *ssid_ie; 1239 1240 res = scan_res->res[j]; 1241 if (os_memcmp(nei->bssid, res->bssid, ETH_ALEN) != 0 || 1242 res->age > WNM_SCAN_RESULT_AGE * 1000) 1243 continue; 1244 bss = wpa_s->current_bss; 1245 ssid_ie = wpa_scan_get_ie(res, WLAN_EID_SSID); 1246 if (bss && ssid_ie && 1247 (bss->ssid_len != ssid_ie[1] || 1248 os_memcmp(bss->ssid, ssid_ie + 2, 1249 bss->ssid_len) != 0)) 1250 continue; 1251 1252 /* Potential candidate found */ 1253 found = 1; 1254 scan_snr(res); 1255 scan_est_throughput(wpa_s, res); 1256 wpa_bss_update_scan_res(wpa_s, res, 1257 &scan_res->fetch_time); 1258 } 1259 } 1260 1261 wpa_scan_results_free(scan_res); 1262 if (!found) { 1263 wpa_dbg(wpa_s, MSG_DEBUG, 1264 "WNM: No transition candidate matches existing scan results"); 1265 return 0; 1266 } 1267 1268 bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE, NULL); 1269 if (!bss) { 1270 wpa_dbg(wpa_s, MSG_DEBUG, 1271 "WNM: Comparison of scan results against transition candidates did not find matches"); 1272 return 0; 1273 } 1274 1275 /* Associate to the network */ 1276 wnm_bss_tm_connect(wpa_s, bss, ssid, 0); 1277 return 1; 1278 } 1279 1280 1281 static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, 1282 const u8 *pos, const u8 *end, 1283 int reply) 1284 { 1285 unsigned int beacon_int; 1286 u8 valid_int; 1287 #ifdef CONFIG_MBO 1288 const u8 *vendor; 1289 #endif /* CONFIG_MBO */ 1290 1291 if (end - pos < 5) 1292 return; 1293 1294 #ifdef CONFIG_MBO 1295 wpa_s->wnm_mbo_trans_reason_present = 0; 1296 wpa_s->wnm_mbo_transition_reason = 0; 1297 #endif /* CONFIG_MBO */ 1298 1299 if (wpa_s->current_bss) 1300 beacon_int = wpa_s->current_bss->beacon_int; 1301 else 1302 beacon_int = 100; /* best guess */ 1303 1304 wpa_s->wnm_dialog_token = pos[0]; 1305 wpa_s->wnm_mode = pos[1]; 1306 wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2); 1307 valid_int = pos[4]; 1308 wpa_s->wnm_reply = reply; 1309 1310 wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: " 1311 "dialog_token=%u request_mode=0x%x " 1312 "disassoc_timer=%u validity_interval=%u", 1313 wpa_s->wnm_dialog_token, wpa_s->wnm_mode, 1314 wpa_s->wnm_dissoc_timer, valid_int); 1315 1316 #if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS) 1317 if (wpa_s->reject_btm_req_reason) { 1318 wpa_printf(MSG_INFO, 1319 "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d", 1320 wpa_s->reject_btm_req_reason); 1321 wnm_send_bss_transition_mgmt_resp( 1322 wpa_s, wpa_s->wnm_dialog_token, 1323 wpa_s->reject_btm_req_reason, 1324 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL); 1325 return; 1326 } 1327 #endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */ 1328 1329 pos += 5; 1330 1331 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) { 1332 if (end - pos < 12) { 1333 wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request"); 1334 return; 1335 } 1336 os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12); 1337 pos += 12; /* BSS Termination Duration */ 1338 } 1339 1340 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) { 1341 char url[256]; 1342 1343 if (end - pos < 1 || 1 + pos[0] > end - pos) { 1344 wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition " 1345 "Management Request (URL)"); 1346 return; 1347 } 1348 os_memcpy(url, pos + 1, pos[0]); 1349 url[pos[0]] = '\0'; 1350 pos += 1 + pos[0]; 1351 1352 wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s", 1353 wpa_sm_pmf_enabled(wpa_s->wpa), 1354 wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url); 1355 } 1356 1357 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { 1358 wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - " 1359 "Disassociation Timer %u", wpa_s->wnm_dissoc_timer); 1360 if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) { 1361 /* TODO: mark current BSS less preferred for 1362 * selection */ 1363 wpa_printf(MSG_DEBUG, "Trying to find another BSS"); 1364 wpa_supplicant_req_scan(wpa_s, 0, 0); 1365 } 1366 } 1367 1368 #ifdef CONFIG_MBO 1369 vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC); 1370 if (vendor) 1371 wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]); 1372 #endif /* CONFIG_MBO */ 1373 1374 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) { 1375 unsigned int valid_ms; 1376 1377 wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available"); 1378 wnm_deallocate_memory(wpa_s); 1379 wpa_s->wnm_neighbor_report_elements = os_calloc( 1380 WNM_MAX_NEIGHBOR_REPORT, 1381 sizeof(struct neighbor_report)); 1382 if (wpa_s->wnm_neighbor_report_elements == NULL) 1383 return; 1384 1385 while (end - pos >= 2 && 1386 wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) 1387 { 1388 u8 tag = *pos++; 1389 u8 len = *pos++; 1390 1391 wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", 1392 tag); 1393 if (len > end - pos) { 1394 wpa_printf(MSG_DEBUG, "WNM: Truncated request"); 1395 return; 1396 } 1397 if (tag == WLAN_EID_NEIGHBOR_REPORT) { 1398 struct neighbor_report *rep; 1399 rep = &wpa_s->wnm_neighbor_report_elements[ 1400 wpa_s->wnm_num_neighbor_report]; 1401 wnm_parse_neighbor_report(wpa_s, pos, len, rep); 1402 wpa_s->wnm_num_neighbor_report++; 1403 #ifdef CONFIG_MBO 1404 if (wpa_s->wnm_mbo_trans_reason_present && 1405 wpa_s->wnm_num_neighbor_report == 1) { 1406 rep->is_first = 1; 1407 wpa_printf(MSG_DEBUG, 1408 "WNM: First transition candidate is " 1409 MACSTR, MAC2STR(rep->bssid)); 1410 } 1411 #endif /* CONFIG_MBO */ 1412 } 1413 1414 pos += len; 1415 } 1416 1417 if (!wpa_s->wnm_num_neighbor_report) { 1418 wpa_printf(MSG_DEBUG, 1419 "WNM: Candidate list included bit is set, but no candidates found"); 1420 wnm_send_bss_transition_mgmt_resp( 1421 wpa_s, wpa_s->wnm_dialog_token, 1422 WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES, 1423 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, 1424 NULL); 1425 return; 1426 } 1427 1428 wnm_sort_cand_list(wpa_s); 1429 wnm_dump_cand_list(wpa_s); 1430 valid_ms = valid_int * beacon_int * 128 / 125; 1431 wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms", 1432 valid_ms); 1433 os_get_reltime(&wpa_s->wnm_cand_valid_until); 1434 wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000; 1435 wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000; 1436 wpa_s->wnm_cand_valid_until.sec += 1437 wpa_s->wnm_cand_valid_until.usec / 1000000; 1438 wpa_s->wnm_cand_valid_until.usec %= 1000000; 1439 os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN); 1440 1441 /* 1442 * Fetch the latest scan results from the kernel and check for 1443 * candidates based on those results first. This can help in 1444 * finding more up-to-date information should the driver has 1445 * done some internal scanning operations after the last scan 1446 * result update in wpa_supplicant. 1447 */ 1448 if (wnm_fetch_scan_results(wpa_s) > 0) 1449 return; 1450 1451 /* 1452 * Try to use previously received scan results, if they are 1453 * recent enough to use for a connection. 1454 */ 1455 if (wpa_s->last_scan_res_used > 0) { 1456 struct os_reltime now; 1457 1458 os_get_reltime(&now); 1459 if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) { 1460 wpa_printf(MSG_DEBUG, 1461 "WNM: Try to use recent scan results"); 1462 if (wnm_scan_process(wpa_s, 0) > 0) 1463 return; 1464 wpa_printf(MSG_DEBUG, 1465 "WNM: No match in previous scan results - try a new scan"); 1466 } 1467 } 1468 1469 wnm_set_scan_freqs(wpa_s); 1470 if (wpa_s->wnm_num_neighbor_report == 1) { 1471 os_memcpy(wpa_s->next_scan_bssid, 1472 wpa_s->wnm_neighbor_report_elements[0].bssid, 1473 ETH_ALEN); 1474 wpa_printf(MSG_DEBUG, 1475 "WNM: Scan only for a specific BSSID since there is only a single candidate " 1476 MACSTR, MAC2STR(wpa_s->next_scan_bssid)); 1477 } 1478 wpa_supplicant_req_scan(wpa_s, 0, 0); 1479 } else if (reply) { 1480 enum bss_trans_mgmt_status_code status; 1481 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) 1482 status = WNM_BSS_TM_ACCEPT; 1483 else { 1484 wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates"); 1485 status = WNM_BSS_TM_REJECT_UNSPECIFIED; 1486 } 1487 wnm_send_bss_transition_mgmt_resp( 1488 wpa_s, wpa_s->wnm_dialog_token, status, 1489 MBO_TRANSITION_REJECT_REASON_UNSPECIFIED, 0, NULL); 1490 } 1491 } 1492 1493 1494 #define BTM_QUERY_MIN_SIZE 4 1495 1496 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, 1497 u8 query_reason, 1498 const char *btm_candidates, 1499 int cand_list) 1500 { 1501 struct wpabuf *buf; 1502 int ret; 1503 1504 wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to " 1505 MACSTR " query_reason=%u%s", 1506 MAC2STR(wpa_s->bssid), query_reason, 1507 cand_list ? " candidate list" : ""); 1508 1509 buf = wpabuf_alloc(BTM_QUERY_MIN_SIZE); 1510 if (!buf) 1511 return -1; 1512 1513 wpabuf_put_u8(buf, WLAN_ACTION_WNM); 1514 wpabuf_put_u8(buf, WNM_BSS_TRANS_MGMT_QUERY); 1515 wpabuf_put_u8(buf, 1); 1516 wpabuf_put_u8(buf, query_reason); 1517 1518 if (cand_list) 1519 wnm_add_cand_list(wpa_s, &buf); 1520 1521 if (btm_candidates) { 1522 const size_t max_len = 1000; 1523 1524 ret = wpabuf_resize(&buf, max_len); 1525 if (ret < 0) { 1526 wpabuf_free(buf); 1527 return ret; 1528 } 1529 1530 ret = ieee802_11_parse_candidate_list(btm_candidates, 1531 wpabuf_put(buf, 0), 1532 max_len); 1533 if (ret < 0) { 1534 wpabuf_free(buf); 1535 return ret; 1536 } 1537 1538 wpabuf_put(buf, ret); 1539 } 1540 1541 ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, 1542 wpa_s->own_addr, wpa_s->bssid, 1543 wpabuf_head_u8(buf), wpabuf_len(buf), 0); 1544 1545 wpabuf_free(buf); 1546 return ret; 1547 } 1548 1549 1550 static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s, 1551 const u8 *sa, const u8 *data, 1552 int len) 1553 { 1554 const u8 *pos, *end, *next; 1555 u8 ie, ie_len; 1556 1557 pos = data; 1558 end = data + len; 1559 1560 while (end - pos > 1) { 1561 ie = *pos++; 1562 ie_len = *pos++; 1563 wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u", 1564 ie, ie_len); 1565 if (ie_len > end - pos) { 1566 wpa_printf(MSG_DEBUG, "WNM: Not enough room for " 1567 "subelement"); 1568 break; 1569 } 1570 next = pos + ie_len; 1571 if (ie_len < 4) { 1572 pos = next; 1573 continue; 1574 } 1575 wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u", 1576 WPA_GET_BE24(pos), pos[3]); 1577 1578 #ifdef CONFIG_HS20 1579 if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 && 1580 WPA_GET_BE24(pos) == OUI_WFA && 1581 pos[3] == HS20_WNM_SUB_REM_NEEDED) { 1582 /* Subscription Remediation subelement */ 1583 const u8 *ie_end; 1584 u8 url_len; 1585 char *url; 1586 u8 osu_method; 1587 1588 wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation " 1589 "subelement"); 1590 ie_end = pos + ie_len; 1591 pos += 4; 1592 url_len = *pos++; 1593 if (url_len == 0) { 1594 wpa_printf(MSG_DEBUG, "WNM: No Server URL included"); 1595 url = NULL; 1596 osu_method = 1; 1597 } else { 1598 if (url_len + 1 > ie_end - pos) { 1599 wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)", 1600 url_len, 1601 (int) (ie_end - pos)); 1602 break; 1603 } 1604 url = os_malloc(url_len + 1); 1605 if (url == NULL) 1606 break; 1607 os_memcpy(url, pos, url_len); 1608 url[url_len] = '\0'; 1609 osu_method = pos[url_len]; 1610 } 1611 hs20_rx_subscription_remediation(wpa_s, url, 1612 osu_method); 1613 os_free(url); 1614 pos = next; 1615 continue; 1616 } 1617 1618 if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 && 1619 WPA_GET_BE24(pos) == OUI_WFA && 1620 pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) { 1621 const u8 *ie_end; 1622 u8 url_len; 1623 char *url; 1624 u8 code; 1625 u16 reauth_delay; 1626 1627 ie_end = pos + ie_len; 1628 pos += 4; 1629 code = *pos++; 1630 reauth_delay = WPA_GET_LE16(pos); 1631 pos += 2; 1632 url_len = *pos++; 1633 wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication " 1634 "Imminent - Reason Code %u " 1635 "Re-Auth Delay %u URL Length %u", 1636 code, reauth_delay, url_len); 1637 if (url_len > ie_end - pos) 1638 break; 1639 url = os_malloc(url_len + 1); 1640 if (url == NULL) 1641 break; 1642 os_memcpy(url, pos, url_len); 1643 url[url_len] = '\0'; 1644 hs20_rx_deauth_imminent_notice(wpa_s, code, 1645 reauth_delay, url); 1646 os_free(url); 1647 pos = next; 1648 continue; 1649 } 1650 1651 if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 && 1652 WPA_GET_BE24(pos) == OUI_WFA && 1653 pos[3] == HS20_WNM_T_C_ACCEPTANCE) { 1654 const u8 *ie_end; 1655 u8 url_len; 1656 char *url; 1657 1658 ie_end = pos + ie_len; 1659 pos += 4; 1660 url_len = *pos++; 1661 wpa_printf(MSG_DEBUG, 1662 "WNM: HS 2.0 Terms and Conditions Acceptance (URL Length %u)", 1663 url_len); 1664 if (url_len > ie_end - pos) 1665 break; 1666 url = os_malloc(url_len + 1); 1667 if (!url) 1668 break; 1669 os_memcpy(url, pos, url_len); 1670 url[url_len] = '\0'; 1671 hs20_rx_t_c_acceptance(wpa_s, url); 1672 os_free(url); 1673 pos = next; 1674 continue; 1675 } 1676 #endif /* CONFIG_HS20 */ 1677 1678 pos = next; 1679 } 1680 } 1681 1682 1683 static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s, 1684 const u8 *sa, const u8 *frm, int len) 1685 { 1686 const u8 *pos, *end; 1687 u8 dialog_token, type; 1688 1689 /* Dialog Token [1] | Type [1] | Subelements */ 1690 1691 if (len < 2 || sa == NULL) 1692 return; 1693 end = frm + len; 1694 pos = frm; 1695 dialog_token = *pos++; 1696 type = *pos++; 1697 1698 wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request " 1699 "(dialog_token %u type %u sa " MACSTR ")", 1700 dialog_token, type, MAC2STR(sa)); 1701 wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements", 1702 pos, end - pos); 1703 1704 if (wpa_s->wpa_state != WPA_COMPLETED || 1705 os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) { 1706 wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not " 1707 "from our AP - ignore it"); 1708 return; 1709 } 1710 1711 switch (type) { 1712 case 1: 1713 ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos); 1714 break; 1715 default: 1716 wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown " 1717 "WNM-Notification type %u", type); 1718 break; 1719 } 1720 } 1721 1722 1723 static void ieee802_11_rx_wnm_coloc_intf_req(struct wpa_supplicant *wpa_s, 1724 const u8 *sa, const u8 *frm, 1725 int len) 1726 { 1727 u8 dialog_token, req_info, auto_report, timeout; 1728 1729 if (!wpa_s->conf->coloc_intf_reporting) 1730 return; 1731 1732 /* Dialog Token [1] | Request Info [1] */ 1733 1734 if (len < 2) 1735 return; 1736 dialog_token = frm[0]; 1737 req_info = frm[1]; 1738 auto_report = req_info & 0x03; 1739 timeout = req_info >> 2; 1740 1741 wpa_dbg(wpa_s, MSG_DEBUG, 1742 "WNM: Received Collocated Interference Request (dialog_token %u auto_report %u timeout %u sa " MACSTR ")", 1743 dialog_token, auto_report, timeout, MAC2STR(sa)); 1744 1745 if (dialog_token == 0) 1746 return; /* only nonzero values are used for request */ 1747 1748 if (wpa_s->wpa_state != WPA_COMPLETED || 1749 os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) { 1750 wpa_dbg(wpa_s, MSG_DEBUG, 1751 "WNM: Collocated Interference Request frame not from current AP - ignore it"); 1752 return; 1753 } 1754 1755 wpa_msg(wpa_s, MSG_INFO, COLOC_INTF_REQ "%u %u %u", 1756 dialog_token, auto_report, timeout); 1757 wpa_s->coloc_intf_dialog_token = dialog_token; 1758 wpa_s->coloc_intf_auto_report = auto_report; 1759 wpa_s->coloc_intf_timeout = timeout; 1760 } 1761 1762 1763 void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, 1764 const struct ieee80211_mgmt *mgmt, size_t len) 1765 { 1766 const u8 *pos, *end; 1767 u8 act; 1768 1769 if (len < IEEE80211_HDRLEN + 2) 1770 return; 1771 1772 pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1; 1773 act = *pos++; 1774 end = ((const u8 *) mgmt) + len; 1775 1776 wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR, 1777 act, MAC2STR(mgmt->sa)); 1778 if (wpa_s->wpa_state < WPA_ASSOCIATED || 1779 os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) { 1780 wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action " 1781 "frame"); 1782 return; 1783 } 1784 1785 switch (act) { 1786 case WNM_BSS_TRANS_MGMT_REQ: 1787 ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end, 1788 !(mgmt->da[0] & 0x01)); 1789 break; 1790 case WNM_SLEEP_MODE_RESP: 1791 ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos); 1792 break; 1793 case WNM_NOTIFICATION_REQ: 1794 ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos); 1795 break; 1796 case WNM_COLLOCATED_INTERFERENCE_REQ: 1797 ieee802_11_rx_wnm_coloc_intf_req(wpa_s, mgmt->sa, pos, 1798 end - pos); 1799 break; 1800 default: 1801 wpa_printf(MSG_ERROR, "WNM: Unknown request"); 1802 break; 1803 } 1804 } 1805 1806 1807 int wnm_send_coloc_intf_report(struct wpa_supplicant *wpa_s, u8 dialog_token, 1808 const struct wpabuf *elems) 1809 { 1810 struct wpabuf *buf; 1811 int ret; 1812 1813 if (wpa_s->wpa_state < WPA_ASSOCIATED || !elems) 1814 return -1; 1815 1816 wpa_printf(MSG_DEBUG, "WNM: Send Collocated Interference Report to " 1817 MACSTR " (dialog token %u)", 1818 MAC2STR(wpa_s->bssid), dialog_token); 1819 1820 buf = wpabuf_alloc(3 + wpabuf_len(elems)); 1821 if (!buf) 1822 return -1; 1823 1824 wpabuf_put_u8(buf, WLAN_ACTION_WNM); 1825 wpabuf_put_u8(buf, WNM_COLLOCATED_INTERFERENCE_REPORT); 1826 wpabuf_put_u8(buf, dialog_token); 1827 wpabuf_put_buf(buf, elems); 1828 1829 ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, 1830 wpa_s->own_addr, wpa_s->bssid, 1831 wpabuf_head_u8(buf), wpabuf_len(buf), 0); 1832 wpabuf_free(buf); 1833 return ret; 1834 } 1835 1836 1837 void wnm_set_coloc_intf_elems(struct wpa_supplicant *wpa_s, 1838 struct wpabuf *elems) 1839 { 1840 wpabuf_free(wpa_s->coloc_intf_elems); 1841 if (elems && wpabuf_len(elems) == 0) { 1842 wpabuf_free(elems); 1843 elems = NULL; 1844 } 1845 wpa_s->coloc_intf_elems = elems; 1846 1847 if (wpa_s->conf->coloc_intf_reporting && wpa_s->coloc_intf_elems && 1848 wpa_s->coloc_intf_dialog_token && 1849 (wpa_s->coloc_intf_auto_report == 1 || 1850 wpa_s->coloc_intf_auto_report == 3)) { 1851 /* TODO: Check that there has not been less than 1852 * wpa_s->coloc_intf_timeout * 200 TU from the last report. 1853 */ 1854 wnm_send_coloc_intf_report(wpa_s, 1855 wpa_s->coloc_intf_dialog_token, 1856 wpa_s->coloc_intf_elems); 1857 } 1858 } 1859 1860 1861 void wnm_clear_coloc_intf_reporting(struct wpa_supplicant *wpa_s) 1862 { 1863 #ifdef CONFIG_WNM 1864 wpa_s->coloc_intf_dialog_token = 0; 1865 wpa_s->coloc_intf_auto_report = 0; 1866 #endif /* CONFIG_WNM */ 1867 } 1868