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 "wpa_supplicant_i.h" 17#include "driver_i.h" 18#include "scan.h" 19#include "ctrl_iface.h" 20#include "bss.h" 21#include "wnm_sta.h" 22#include "hs20_supplicant.h" 23 24#define MAX_TFS_IE_LEN 1024 25#define WNM_MAX_NEIGHBOR_REPORT 10 26 27 28/* get the TFS IE from driver */ 29static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf, 30 u16 *buf_len, enum wnm_oper oper) 31{ 32 wpa_printf(MSG_DEBUG, "%s: TFS get operation %d", __func__, oper); 33 34 return wpa_drv_wnm_oper(wpa_s, oper, wpa_s->bssid, buf, buf_len); 35} 36 37 38/* set the TFS IE to driver */ 39static int ieee80211_11_set_tfs_ie(struct wpa_supplicant *wpa_s, 40 const u8 *addr, u8 *buf, u16 *buf_len, 41 enum wnm_oper oper) 42{ 43 wpa_printf(MSG_DEBUG, "%s: TFS set operation %d", __func__, oper); 44 45 return wpa_drv_wnm_oper(wpa_s, oper, addr, buf, buf_len); 46} 47 48 49/* MLME-SLEEPMODE.request */ 50int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s, 51 u8 action, u16 intval, struct wpabuf *tfs_req) 52{ 53 struct ieee80211_mgmt *mgmt; 54 int res; 55 size_t len; 56 struct wnm_sleep_element *wnmsleep_ie; 57 u8 *wnmtfs_ie; 58 u8 wnmsleep_ie_len; 59 u16 wnmtfs_ie_len; /* possibly multiple IE(s) */ 60 enum wnm_oper tfs_oper = action == 0 ? WNM_SLEEP_TFS_REQ_IE_ADD : 61 WNM_SLEEP_TFS_REQ_IE_NONE; 62 63 wpa_printf(MSG_DEBUG, "WNM: Request to send WNM-Sleep Mode Request " 64 "action=%s to " MACSTR, 65 action == 0 ? "enter" : "exit", 66 MAC2STR(wpa_s->bssid)); 67 68 /* WNM-Sleep Mode IE */ 69 wnmsleep_ie_len = sizeof(struct wnm_sleep_element); 70 wnmsleep_ie = os_zalloc(sizeof(struct wnm_sleep_element)); 71 if (wnmsleep_ie == NULL) 72 return -1; 73 wnmsleep_ie->eid = WLAN_EID_WNMSLEEP; 74 wnmsleep_ie->len = wnmsleep_ie_len - 2; 75 wnmsleep_ie->action_type = action; 76 wnmsleep_ie->status = WNM_STATUS_SLEEP_ACCEPT; 77 wnmsleep_ie->intval = host_to_le16(intval); 78 wpa_hexdump(MSG_DEBUG, "WNM: WNM-Sleep Mode element", 79 (u8 *) wnmsleep_ie, wnmsleep_ie_len); 80 81 /* TFS IE(s) */ 82 if (tfs_req) { 83 wnmtfs_ie_len = wpabuf_len(tfs_req); 84 wnmtfs_ie = os_malloc(wnmtfs_ie_len); 85 if (wnmtfs_ie == NULL) { 86 os_free(wnmsleep_ie); 87 return -1; 88 } 89 os_memcpy(wnmtfs_ie, wpabuf_head(tfs_req), wnmtfs_ie_len); 90 } else { 91 wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN); 92 if (wnmtfs_ie == NULL) { 93 os_free(wnmsleep_ie); 94 return -1; 95 } 96 if (ieee80211_11_get_tfs_ie(wpa_s, wnmtfs_ie, &wnmtfs_ie_len, 97 tfs_oper)) { 98 wnmtfs_ie_len = 0; 99 os_free(wnmtfs_ie); 100 wnmtfs_ie = NULL; 101 } 102 } 103 wpa_hexdump(MSG_DEBUG, "WNM: TFS Request element", 104 (u8 *) wnmtfs_ie, wnmtfs_ie_len); 105 106 mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len + wnmtfs_ie_len); 107 if (mgmt == NULL) { 108 wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for " 109 "WNM-Sleep Request action frame"); 110 os_free(wnmsleep_ie); 111 os_free(wnmtfs_ie); 112 return -1; 113 } 114 115 os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); 116 os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); 117 os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); 118 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 119 WLAN_FC_STYPE_ACTION); 120 mgmt->u.action.category = WLAN_ACTION_WNM; 121 mgmt->u.action.u.wnm_sleep_req.action = WNM_SLEEP_MODE_REQ; 122 mgmt->u.action.u.wnm_sleep_req.dialogtoken = 1; 123 os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable, wnmsleep_ie, 124 wnmsleep_ie_len); 125 /* copy TFS IE here */ 126 if (wnmtfs_ie_len > 0) { 127 os_memcpy(mgmt->u.action.u.wnm_sleep_req.variable + 128 wnmsleep_ie_len, wnmtfs_ie, wnmtfs_ie_len); 129 } 130 131 len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_req) + wnmsleep_ie_len + 132 wnmtfs_ie_len; 133 134 res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, 135 wpa_s->own_addr, wpa_s->bssid, 136 &mgmt->u.action.category, len, 0); 137 if (res < 0) 138 wpa_printf(MSG_DEBUG, "Failed to send WNM-Sleep Request " 139 "(action=%d, intval=%d)", action, intval); 140 141 os_free(wnmsleep_ie); 142 os_free(wnmtfs_ie); 143 os_free(mgmt); 144 145 return res; 146} 147 148 149static void wnm_sleep_mode_enter_success(struct wpa_supplicant *wpa_s, 150 u8 *tfsresp_ie_start, 151 u8 *tfsresp_ie_end) 152{ 153 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_CONFIRM, 154 wpa_s->bssid, NULL, NULL); 155 /* remove GTK/IGTK ?? */ 156 157 /* set the TFS Resp IE(s) */ 158 if (tfsresp_ie_start && tfsresp_ie_end && 159 tfsresp_ie_end - tfsresp_ie_start >= 0) { 160 u16 tfsresp_ie_len; 161 tfsresp_ie_len = (tfsresp_ie_end + tfsresp_ie_end[1] + 2) - 162 tfsresp_ie_start; 163 wpa_printf(MSG_DEBUG, "TFS Resp IE(s) found"); 164 /* pass the TFS Resp IE(s) to driver for processing */ 165 if (ieee80211_11_set_tfs_ie(wpa_s, wpa_s->bssid, 166 tfsresp_ie_start, 167 &tfsresp_ie_len, 168 WNM_SLEEP_TFS_RESP_IE_SET)) 169 wpa_printf(MSG_DEBUG, "WNM: Fail to set TFS Resp IE"); 170 } 171} 172 173 174static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s, 175 const u8 *frm, u16 key_len_total) 176{ 177 u8 *ptr, *end; 178 u8 gtk_len; 179 180 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_CONFIRM, wpa_s->bssid, 181 NULL, NULL); 182 183 /* Install GTK/IGTK */ 184 185 /* point to key data field */ 186 ptr = (u8 *) frm + 1 + 2; 187 end = ptr + key_len_total; 188 wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total); 189 190 if (key_len_total && !wpa_sm_pmf_enabled(wpa_s->wpa)) { 191 wpa_msg(wpa_s, MSG_INFO, 192 "WNM: Ignore Key Data in WNM-Sleep Mode Response - PMF not enabled"); 193 return; 194 } 195 196 while (ptr + 1 < end) { 197 if (ptr + 2 + ptr[1] > end) { 198 wpa_printf(MSG_DEBUG, "WNM: Invalid Key Data element " 199 "length"); 200 if (end > ptr) { 201 wpa_hexdump(MSG_DEBUG, "WNM: Remaining data", 202 ptr, end - ptr); 203 } 204 break; 205 } 206 if (*ptr == WNM_SLEEP_SUBELEM_GTK) { 207 if (ptr[1] < 11 + 5) { 208 wpa_printf(MSG_DEBUG, "WNM: Too short GTK " 209 "subelem"); 210 break; 211 } 212 gtk_len = *(ptr + 4); 213 if (ptr[1] < 11 + gtk_len || 214 gtk_len < 5 || gtk_len > 32) { 215 wpa_printf(MSG_DEBUG, "WNM: Invalid GTK " 216 "subelem"); 217 break; 218 } 219 wpa_wnmsleep_install_key( 220 wpa_s->wpa, 221 WNM_SLEEP_SUBELEM_GTK, 222 ptr); 223 ptr += 13 + gtk_len; 224#ifdef CONFIG_IEEE80211W 225 } else if (*ptr == WNM_SLEEP_SUBELEM_IGTK) { 226 if (ptr[1] < 2 + 6 + WPA_IGTK_LEN) { 227 wpa_printf(MSG_DEBUG, "WNM: Too short IGTK " 228 "subelem"); 229 break; 230 } 231 wpa_wnmsleep_install_key(wpa_s->wpa, 232 WNM_SLEEP_SUBELEM_IGTK, ptr); 233 ptr += 10 + WPA_IGTK_LEN; 234#endif /* CONFIG_IEEE80211W */ 235 } else 236 break; /* skip the loop */ 237 } 238} 239 240 241static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s, 242 const u8 *frm, int len) 243{ 244 /* 245 * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data | 246 * WNM-Sleep Mode IE | TFS Response IE 247 */ 248 u8 *pos = (u8 *) frm; /* point to payload after the action field */ 249 u16 key_len_total; 250 struct wnm_sleep_element *wnmsleep_ie = NULL; 251 /* multiple TFS Resp IE (assuming consecutive) */ 252 u8 *tfsresp_ie_start = NULL; 253 u8 *tfsresp_ie_end = NULL; 254 size_t left; 255 256 if (len < 3) 257 return; 258 key_len_total = WPA_GET_LE16(frm + 1); 259 260 wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d", 261 frm[0], key_len_total); 262 left = len - 3; 263 if (key_len_total > left) { 264 wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field"); 265 return; 266 } 267 pos += 3 + key_len_total; 268 while (pos - frm < len) { 269 u8 ie_len = *(pos + 1); 270 if (pos + 2 + ie_len > frm + len) { 271 wpa_printf(MSG_INFO, "WNM: Invalid IE len %u", ie_len); 272 break; 273 } 274 wpa_hexdump(MSG_DEBUG, "WNM: Element", pos, 2 + ie_len); 275 if (*pos == WLAN_EID_WNMSLEEP) 276 wnmsleep_ie = (struct wnm_sleep_element *) pos; 277 else if (*pos == WLAN_EID_TFS_RESP) { 278 if (!tfsresp_ie_start) 279 tfsresp_ie_start = pos; 280 tfsresp_ie_end = pos; 281 } else 282 wpa_printf(MSG_DEBUG, "EID %d not recognized", *pos); 283 pos += ie_len + 2; 284 } 285 286 if (!wnmsleep_ie) { 287 wpa_printf(MSG_DEBUG, "No WNM-Sleep IE found"); 288 return; 289 } 290 291 if (wnmsleep_ie->status == WNM_STATUS_SLEEP_ACCEPT || 292 wnmsleep_ie->status == WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) { 293 wpa_printf(MSG_DEBUG, "Successfully recv WNM-Sleep Response " 294 "frame (action=%d, intval=%d)", 295 wnmsleep_ie->action_type, wnmsleep_ie->intval); 296 if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) { 297 wnm_sleep_mode_enter_success(wpa_s, tfsresp_ie_start, 298 tfsresp_ie_end); 299 } else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) { 300 wnm_sleep_mode_exit_success(wpa_s, frm, key_len_total); 301 } 302 } else { 303 wpa_printf(MSG_DEBUG, "Reject recv WNM-Sleep Response frame " 304 "(action=%d, intval=%d)", 305 wnmsleep_ie->action_type, wnmsleep_ie->intval); 306 if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER) 307 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_ENTER_FAIL, 308 wpa_s->bssid, NULL, NULL); 309 else if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) 310 wpa_drv_wnm_oper(wpa_s, WNM_SLEEP_EXIT_FAIL, 311 wpa_s->bssid, NULL, NULL); 312 } 313} 314 315 316void wnm_deallocate_memory(struct wpa_supplicant *wpa_s) 317{ 318 int i; 319 320 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { 321 os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot); 322 os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid); 323 } 324 325 wpa_s->wnm_num_neighbor_report = 0; 326 os_free(wpa_s->wnm_neighbor_report_elements); 327 wpa_s->wnm_neighbor_report_elements = NULL; 328} 329 330 331static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep, 332 u8 id, u8 elen, const u8 *pos) 333{ 334 switch (id) { 335 case WNM_NEIGHBOR_TSF: 336 if (elen < 2 + 2) { 337 wpa_printf(MSG_DEBUG, "WNM: Too short TSF"); 338 break; 339 } 340 rep->tsf_offset = WPA_GET_LE16(pos); 341 rep->beacon_int = WPA_GET_LE16(pos + 2); 342 rep->tsf_present = 1; 343 break; 344 case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING: 345 if (elen < 2) { 346 wpa_printf(MSG_DEBUG, "WNM: Too short condensed " 347 "country string"); 348 break; 349 } 350 os_memcpy(rep->country, pos, 2); 351 rep->country_present = 1; 352 break; 353 case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE: 354 if (elen < 1) { 355 wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition " 356 "candidate"); 357 break; 358 } 359 rep->preference = pos[0]; 360 rep->preference_present = 1; 361 break; 362 case WNM_NEIGHBOR_BSS_TERMINATION_DURATION: 363 rep->bss_term_tsf = WPA_GET_LE64(pos); 364 rep->bss_term_dur = WPA_GET_LE16(pos + 8); 365 rep->bss_term_present = 1; 366 break; 367 case WNM_NEIGHBOR_BEARING: 368 if (elen < 8) { 369 wpa_printf(MSG_DEBUG, "WNM: Too short neighbor " 370 "bearing"); 371 break; 372 } 373 rep->bearing = WPA_GET_LE16(pos); 374 rep->distance = WPA_GET_LE32(pos + 2); 375 rep->rel_height = WPA_GET_LE16(pos + 2 + 4); 376 rep->bearing_present = 1; 377 break; 378 case WNM_NEIGHBOR_MEASUREMENT_PILOT: 379 if (elen < 1) { 380 wpa_printf(MSG_DEBUG, "WNM: Too short measurement " 381 "pilot"); 382 break; 383 } 384 os_free(rep->meas_pilot); 385 rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot)); 386 if (rep->meas_pilot == NULL) 387 break; 388 rep->meas_pilot->measurement_pilot = pos[0]; 389 rep->meas_pilot->subelem_len = elen - 1; 390 os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1); 391 break; 392 case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES: 393 if (elen < 5) { 394 wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled " 395 "capabilities"); 396 break; 397 } 398 os_memcpy(rep->rm_capab, pos, 5); 399 rep->rm_capab_present = 1; 400 break; 401 case WNM_NEIGHBOR_MULTIPLE_BSSID: 402 if (elen < 1) { 403 wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID"); 404 break; 405 } 406 os_free(rep->mul_bssid); 407 rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid)); 408 if (rep->mul_bssid == NULL) 409 break; 410 rep->mul_bssid->max_bssid_indicator = pos[0]; 411 rep->mul_bssid->subelem_len = elen - 1; 412 os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1); 413 break; 414 } 415} 416 417 418static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan) 419{ 420 struct wpa_bss *bss = wpa_s->current_bss; 421 const char *country = NULL; 422 423 if (bss) { 424 const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY); 425 426 if (elem && elem[1] >= 2) 427 country = (const char *) (elem + 2); 428 } 429 430 return ieee80211_chan_to_freq(country, op_class, chan); 431} 432 433 434static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s, 435 const u8 *pos, u8 len, 436 struct neighbor_report *rep) 437{ 438 u8 left = len; 439 440 if (left < 13) { 441 wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report"); 442 return; 443 } 444 445 os_memcpy(rep->bssid, pos, ETH_ALEN); 446 rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN); 447 rep->regulatory_class = *(pos + 10); 448 rep->channel_number = *(pos + 11); 449 rep->phy_type = *(pos + 12); 450 451 pos += 13; 452 left -= 13; 453 454 while (left >= 2) { 455 u8 id, elen; 456 457 id = *pos++; 458 elen = *pos++; 459 wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen); 460 left -= 2; 461 if (elen > left) { 462 wpa_printf(MSG_DEBUG, 463 "WNM: Truncated neighbor report subelement"); 464 break; 465 } 466 wnm_parse_neighbor_report_elem(rep, id, elen, pos); 467 left -= elen; 468 pos += elen; 469 } 470 471 rep->freq = wnm_nei_get_chan(wpa_s, rep->regulatory_class, 472 rep->channel_number); 473} 474 475 476static struct wpa_bss * 477compare_scan_neighbor_results(struct wpa_supplicant *wpa_s) 478{ 479 480 u8 i; 481 struct wpa_bss *bss = wpa_s->current_bss; 482 struct wpa_bss *target; 483 484 if (!bss) 485 return 0; 486 487 wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d", 488 MAC2STR(wpa_s->bssid), bss->level); 489 490 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { 491 struct neighbor_report *nei; 492 493 nei = &wpa_s->wnm_neighbor_report_elements[i]; 494 if (nei->preference_present && nei->preference == 0) { 495 wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR, 496 MAC2STR(nei->bssid)); 497 continue; 498 } 499 500 target = wpa_bss_get_bssid(wpa_s, nei->bssid); 501 if (!target) { 502 wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR 503 " (pref %d) not found in scan results", 504 MAC2STR(nei->bssid), 505 nei->preference_present ? nei->preference : 506 -1); 507 continue; 508 } 509 510 if (bss->ssid_len != target->ssid_len || 511 os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) { 512 /* 513 * TODO: Could consider allowing transition to another 514 * ESS if PMF was enabled for the association. 515 */ 516 wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR 517 " (pref %d) in different ESS", 518 MAC2STR(nei->bssid), 519 nei->preference_present ? nei->preference : 520 -1); 521 continue; 522 } 523 524 if (target->level < bss->level && target->level < -80) { 525 wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR 526 " (pref %d) does not have sufficient signal level (%d)", 527 MAC2STR(nei->bssid), 528 nei->preference_present ? nei->preference : 529 -1, 530 target->level); 531 continue; 532 } 533 534 wpa_printf(MSG_DEBUG, 535 "WNM: Found an acceptable preferred transition candidate BSS " 536 MACSTR " (RSSI %d)", 537 MAC2STR(nei->bssid), target->level); 538 return target; 539 } 540 541 return NULL; 542} 543 544 545static void wnm_send_bss_transition_mgmt_resp( 546 struct wpa_supplicant *wpa_s, u8 dialog_token, 547 enum bss_trans_mgmt_status_code status, u8 delay, 548 const u8 *target_bssid) 549{ 550 u8 buf[1000], *pos; 551 struct ieee80211_mgmt *mgmt; 552 size_t len; 553 int res; 554 555 wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response " 556 "to " MACSTR " dialog_token=%u status=%u delay=%d", 557 MAC2STR(wpa_s->bssid), dialog_token, status, delay); 558 if (!wpa_s->current_bss) { 559 wpa_printf(MSG_DEBUG, 560 "WNM: Current BSS not known - drop response"); 561 return; 562 } 563 564 mgmt = (struct ieee80211_mgmt *) buf; 565 os_memset(&buf, 0, sizeof(buf)); 566 os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); 567 os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); 568 os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); 569 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 570 WLAN_FC_STYPE_ACTION); 571 mgmt->u.action.category = WLAN_ACTION_WNM; 572 mgmt->u.action.u.bss_tm_resp.action = WNM_BSS_TRANS_MGMT_RESP; 573 mgmt->u.action.u.bss_tm_resp.dialog_token = dialog_token; 574 mgmt->u.action.u.bss_tm_resp.status_code = status; 575 mgmt->u.action.u.bss_tm_resp.bss_termination_delay = delay; 576 pos = mgmt->u.action.u.bss_tm_resp.variable; 577 if (target_bssid) { 578 os_memcpy(pos, target_bssid, ETH_ALEN); 579 pos += ETH_ALEN; 580 } else if (status == WNM_BSS_TM_ACCEPT) { 581 /* 582 * P802.11-REVmc clarifies that the Target BSSID field is always 583 * present when status code is zero, so use a fake value here if 584 * no BSSID is yet known. 585 */ 586 os_memset(pos, 0, ETH_ALEN); 587 pos += ETH_ALEN; 588 } 589 590 len = pos - (u8 *) &mgmt->u.action.category; 591 592 res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, 593 wpa_s->own_addr, wpa_s->bssid, 594 &mgmt->u.action.category, len, 0); 595 if (res < 0) { 596 wpa_printf(MSG_DEBUG, 597 "WNM: Failed to send BSS Transition Management Response"); 598 } 599} 600 601 602int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail) 603{ 604 struct wpa_bss *bss; 605 struct wpa_ssid *ssid = wpa_s->current_ssid; 606 enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED; 607 608 if (!wpa_s->wnm_neighbor_report_elements) 609 return 0; 610 611 if (os_reltime_before(&wpa_s->wnm_cand_valid_until, 612 &wpa_s->scan_trigger_time)) { 613 wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it"); 614 wnm_deallocate_memory(wpa_s); 615 return 0; 616 } 617 618 if (!wpa_s->current_bss || 619 os_memcmp(wpa_s->wnm_cand_from_bss, wpa_s->current_bss->bssid, 620 ETH_ALEN) != 0) { 621 wpa_printf(MSG_DEBUG, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it"); 622 return 0; 623 } 624 625 /* Compare the Neighbor Report and scan results */ 626 bss = compare_scan_neighbor_results(wpa_s); 627 if (!bss) { 628 wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found"); 629 status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES; 630 goto send_bss_resp_fail; 631 } 632 633 /* Associate to the network */ 634 /* Send the BSS Management Response - Accept */ 635 if (wpa_s->wnm_reply) { 636 wpa_s->wnm_reply = 0; 637 wnm_send_bss_transition_mgmt_resp(wpa_s, 638 wpa_s->wnm_dialog_token, 639 WNM_BSS_TM_ACCEPT, 640 0, bss->bssid); 641 } 642 643 if (bss == wpa_s->current_bss) { 644 wpa_printf(MSG_DEBUG, 645 "WNM: Already associated with the preferred candidate"); 646 return 1; 647 } 648 649 wpa_s->reassociate = 1; 650 wpa_supplicant_connect(wpa_s, bss, ssid); 651 wnm_deallocate_memory(wpa_s); 652 return 1; 653 654send_bss_resp_fail: 655 if (!reply_on_fail) 656 return 0; 657 658 /* Send reject response for all the failures */ 659 660 if (wpa_s->wnm_reply) { 661 wpa_s->wnm_reply = 0; 662 wnm_send_bss_transition_mgmt_resp(wpa_s, 663 wpa_s->wnm_dialog_token, 664 status, 0, NULL); 665 } 666 wnm_deallocate_memory(wpa_s); 667 668 return 0; 669} 670 671 672static int cand_pref_compar(const void *a, const void *b) 673{ 674 const struct neighbor_report *aa = a; 675 const struct neighbor_report *bb = b; 676 677 if (!aa->preference_present && !bb->preference_present) 678 return 0; 679 if (!aa->preference_present) 680 return 1; 681 if (!bb->preference_present) 682 return -1; 683 if (bb->preference > aa->preference) 684 return 1; 685 if (bb->preference < aa->preference) 686 return -1; 687 return 0; 688} 689 690 691static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s) 692{ 693 if (!wpa_s->wnm_neighbor_report_elements) 694 return; 695 qsort(wpa_s->wnm_neighbor_report_elements, 696 wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report), 697 cand_pref_compar); 698} 699 700 701static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s) 702{ 703 unsigned int i; 704 705 wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List"); 706 if (!wpa_s->wnm_neighbor_report_elements) 707 return; 708 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { 709 struct neighbor_report *nei; 710 711 nei = &wpa_s->wnm_neighbor_report_elements[i]; 712 wpa_printf(MSG_DEBUG, "%u: " MACSTR 713 " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d", 714 i, MAC2STR(nei->bssid), nei->bssid_info, 715 nei->regulatory_class, 716 nei->channel_number, nei->phy_type, 717 nei->preference_present ? nei->preference : -1, 718 nei->freq); 719 } 720} 721 722 723static int chan_supported(struct wpa_supplicant *wpa_s, int freq) 724{ 725 unsigned int i; 726 727 for (i = 0; i < wpa_s->hw.num_modes; i++) { 728 struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i]; 729 int j; 730 731 for (j = 0; j < mode->num_channels; j++) { 732 struct hostapd_channel_data *chan; 733 734 chan = &mode->channels[j]; 735 if (chan->freq == freq && 736 !(chan->flag & HOSTAPD_CHAN_DISABLED)) 737 return 1; 738 } 739 } 740 741 return 0; 742} 743 744 745static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s) 746{ 747 int *freqs; 748 int num_freqs = 0; 749 unsigned int i; 750 751 if (!wpa_s->wnm_neighbor_report_elements) 752 return; 753 754 if (wpa_s->hw.modes == NULL) 755 return; 756 757 os_free(wpa_s->next_scan_freqs); 758 wpa_s->next_scan_freqs = NULL; 759 760 freqs = os_calloc(wpa_s->wnm_num_neighbor_report + 1, sizeof(int)); 761 if (freqs == NULL) 762 return; 763 764 for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) { 765 struct neighbor_report *nei; 766 767 nei = &wpa_s->wnm_neighbor_report_elements[i]; 768 if (nei->freq <= 0) { 769 wpa_printf(MSG_DEBUG, 770 "WNM: Unknown neighbor operating frequency for " 771 MACSTR " - scan all channels", 772 MAC2STR(nei->bssid)); 773 os_free(freqs); 774 return; 775 } 776 if (chan_supported(wpa_s, nei->freq)) 777 add_freq(freqs, &num_freqs, nei->freq); 778 } 779 780 if (num_freqs == 0) { 781 os_free(freqs); 782 return; 783 } 784 785 wpa_printf(MSG_DEBUG, 786 "WNM: Scan %d frequencies based on transition candidate list", 787 num_freqs); 788 wpa_s->next_scan_freqs = freqs; 789} 790 791 792static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s, 793 const u8 *pos, const u8 *end, 794 int reply) 795{ 796 unsigned int beacon_int; 797 u8 valid_int; 798 799 if (pos + 5 > end) 800 return; 801 802 if (wpa_s->current_bss) 803 beacon_int = wpa_s->current_bss->beacon_int; 804 else 805 beacon_int = 100; /* best guess */ 806 807 wpa_s->wnm_dialog_token = pos[0]; 808 wpa_s->wnm_mode = pos[1]; 809 wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2); 810 valid_int = pos[4]; 811 wpa_s->wnm_reply = reply; 812 813 wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: " 814 "dialog_token=%u request_mode=0x%x " 815 "disassoc_timer=%u validity_interval=%u", 816 wpa_s->wnm_dialog_token, wpa_s->wnm_mode, 817 wpa_s->wnm_dissoc_timer, valid_int); 818 819 pos += 5; 820 821 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) { 822 if (pos + 12 > end) { 823 wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request"); 824 return; 825 } 826 os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12); 827 pos += 12; /* BSS Termination Duration */ 828 } 829 830 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) { 831 char url[256]; 832 833 if (pos + 1 > end || pos + 1 + pos[0] > end) { 834 wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition " 835 "Management Request (URL)"); 836 return; 837 } 838 os_memcpy(url, pos + 1, pos[0]); 839 url[pos[0]] = '\0'; 840 pos += 1 + pos[0]; 841 842 wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s", 843 wpa_sm_pmf_enabled(wpa_s->wpa), 844 wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url); 845 } 846 847 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT) { 848 wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - " 849 "Disassociation Timer %u", wpa_s->wnm_dissoc_timer); 850 if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) { 851 /* TODO: mark current BSS less preferred for 852 * selection */ 853 wpa_printf(MSG_DEBUG, "Trying to find another BSS"); 854 wpa_supplicant_req_scan(wpa_s, 0, 0); 855 } 856 } 857 858 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) { 859 unsigned int valid_ms; 860 861 wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available"); 862 wnm_deallocate_memory(wpa_s); 863 wpa_s->wnm_neighbor_report_elements = os_calloc( 864 WNM_MAX_NEIGHBOR_REPORT, 865 sizeof(struct neighbor_report)); 866 if (wpa_s->wnm_neighbor_report_elements == NULL) 867 return; 868 869 while (pos + 2 <= end && 870 wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT) 871 { 872 u8 tag = *pos++; 873 u8 len = *pos++; 874 875 wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u", 876 tag); 877 if (pos + len > end) { 878 wpa_printf(MSG_DEBUG, "WNM: Truncated request"); 879 return; 880 } 881 if (tag == WLAN_EID_NEIGHBOR_REPORT) { 882 struct neighbor_report *rep; 883 rep = &wpa_s->wnm_neighbor_report_elements[ 884 wpa_s->wnm_num_neighbor_report]; 885 wnm_parse_neighbor_report(wpa_s, pos, len, rep); 886 } 887 888 pos += len; 889 wpa_s->wnm_num_neighbor_report++; 890 } 891 wnm_sort_cand_list(wpa_s); 892 wnm_dump_cand_list(wpa_s); 893 valid_ms = valid_int * beacon_int * 128 / 125; 894 wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms", 895 valid_ms); 896 os_get_reltime(&wpa_s->wnm_cand_valid_until); 897 wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000; 898 wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000; 899 wpa_s->wnm_cand_valid_until.sec += 900 wpa_s->wnm_cand_valid_until.usec / 1000000; 901 wpa_s->wnm_cand_valid_until.usec %= 1000000; 902 os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN); 903 904 if (wpa_s->last_scan_res_used > 0) { 905 struct os_reltime now; 906 907 os_get_reltime(&now); 908 if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) { 909 wpa_printf(MSG_DEBUG, 910 "WNM: Try to use recent scan results"); 911 if (wnm_scan_process(wpa_s, 0) > 0) 912 return; 913 wpa_printf(MSG_DEBUG, 914 "WNM: No match in previous scan results - try a new scan"); 915 } 916 } 917 918 wnm_set_scan_freqs(wpa_s); 919 wpa_supplicant_req_scan(wpa_s, 0, 0); 920 } else if (reply) { 921 enum bss_trans_mgmt_status_code status; 922 if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) 923 status = WNM_BSS_TM_ACCEPT; 924 else { 925 wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management Request did not include candidates"); 926 status = WNM_BSS_TM_REJECT_UNSPECIFIED; 927 } 928 wnm_send_bss_transition_mgmt_resp(wpa_s, 929 wpa_s->wnm_dialog_token, 930 status, 0, NULL); 931 } 932} 933 934 935int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s, 936 u8 query_reason) 937{ 938 u8 buf[1000], *pos; 939 struct ieee80211_mgmt *mgmt; 940 size_t len; 941 int ret; 942 943 wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to " 944 MACSTR " query_reason=%u", 945 MAC2STR(wpa_s->bssid), query_reason); 946 947 mgmt = (struct ieee80211_mgmt *) buf; 948 os_memset(&buf, 0, sizeof(buf)); 949 os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN); 950 os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN); 951 os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN); 952 mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, 953 WLAN_FC_STYPE_ACTION); 954 mgmt->u.action.category = WLAN_ACTION_WNM; 955 mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY; 956 mgmt->u.action.u.bss_tm_query.dialog_token = 1; 957 mgmt->u.action.u.bss_tm_query.query_reason = query_reason; 958 pos = mgmt->u.action.u.bss_tm_query.variable; 959 960 len = pos - (u8 *) &mgmt->u.action.category; 961 962 ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid, 963 wpa_s->own_addr, wpa_s->bssid, 964 &mgmt->u.action.category, len, 0); 965 966 return ret; 967} 968 969 970static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s, 971 const u8 *sa, const u8 *data, 972 int len) 973{ 974 const u8 *pos, *end, *next; 975 u8 ie, ie_len; 976 977 pos = data; 978 end = data + len; 979 980 while (pos + 1 < end) { 981 ie = *pos++; 982 ie_len = *pos++; 983 wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u", 984 ie, ie_len); 985 if (ie_len > end - pos) { 986 wpa_printf(MSG_DEBUG, "WNM: Not enough room for " 987 "subelement"); 988 break; 989 } 990 next = pos + ie_len; 991 if (ie_len < 4) { 992 pos = next; 993 continue; 994 } 995 wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u", 996 WPA_GET_BE24(pos), pos[3]); 997 998#ifdef CONFIG_HS20 999 if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 && 1000 WPA_GET_BE24(pos) == OUI_WFA && 1001 pos[3] == HS20_WNM_SUB_REM_NEEDED) { 1002 /* Subscription Remediation subelement */ 1003 const u8 *ie_end; 1004 u8 url_len; 1005 char *url; 1006 u8 osu_method; 1007 1008 wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation " 1009 "subelement"); 1010 ie_end = pos + ie_len; 1011 pos += 4; 1012 url_len = *pos++; 1013 if (url_len == 0) { 1014 wpa_printf(MSG_DEBUG, "WNM: No Server URL included"); 1015 url = NULL; 1016 osu_method = 1; 1017 } else { 1018 if (pos + url_len + 1 > ie_end) { 1019 wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)", 1020 url_len, 1021 (int) (ie_end - pos)); 1022 break; 1023 } 1024 url = os_malloc(url_len + 1); 1025 if (url == NULL) 1026 break; 1027 os_memcpy(url, pos, url_len); 1028 url[url_len] = '\0'; 1029 osu_method = pos[url_len]; 1030 } 1031 hs20_rx_subscription_remediation(wpa_s, url, 1032 osu_method); 1033 os_free(url); 1034 pos = next; 1035 continue; 1036 } 1037 1038 if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 && 1039 WPA_GET_BE24(pos) == OUI_WFA && 1040 pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) { 1041 const u8 *ie_end; 1042 u8 url_len; 1043 char *url; 1044 u8 code; 1045 u16 reauth_delay; 1046 1047 ie_end = pos + ie_len; 1048 pos += 4; 1049 code = *pos++; 1050 reauth_delay = WPA_GET_LE16(pos); 1051 pos += 2; 1052 url_len = *pos++; 1053 wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication " 1054 "Imminent - Reason Code %u " 1055 "Re-Auth Delay %u URL Length %u", 1056 code, reauth_delay, url_len); 1057 if (pos + url_len > ie_end) 1058 break; 1059 url = os_malloc(url_len + 1); 1060 if (url == NULL) 1061 break; 1062 os_memcpy(url, pos, url_len); 1063 url[url_len] = '\0'; 1064 hs20_rx_deauth_imminent_notice(wpa_s, code, 1065 reauth_delay, url); 1066 os_free(url); 1067 pos = next; 1068 continue; 1069 } 1070#endif /* CONFIG_HS20 */ 1071 1072 pos = next; 1073 } 1074} 1075 1076 1077static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s, 1078 const u8 *sa, const u8 *frm, int len) 1079{ 1080 const u8 *pos, *end; 1081 u8 dialog_token, type; 1082 1083 /* Dialog Token [1] | Type [1] | Subelements */ 1084 1085 if (len < 2 || sa == NULL) 1086 return; 1087 end = frm + len; 1088 pos = frm; 1089 dialog_token = *pos++; 1090 type = *pos++; 1091 1092 wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request " 1093 "(dialog_token %u type %u sa " MACSTR ")", 1094 dialog_token, type, MAC2STR(sa)); 1095 wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements", 1096 pos, end - pos); 1097 1098 if (wpa_s->wpa_state != WPA_COMPLETED || 1099 os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) { 1100 wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not " 1101 "from our AP - ignore it"); 1102 return; 1103 } 1104 1105 switch (type) { 1106 case 1: 1107 ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos); 1108 break; 1109 default: 1110 wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown " 1111 "WNM-Notification type %u", type); 1112 break; 1113 } 1114} 1115 1116 1117void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s, 1118 const struct ieee80211_mgmt *mgmt, size_t len) 1119{ 1120 const u8 *pos, *end; 1121 u8 act; 1122 1123 if (len < IEEE80211_HDRLEN + 2) 1124 return; 1125 1126 pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1; 1127 act = *pos++; 1128 end = ((const u8 *) mgmt) + len; 1129 1130 wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR, 1131 act, MAC2STR(mgmt->sa)); 1132 if (wpa_s->wpa_state < WPA_ASSOCIATED || 1133 os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) { 1134 wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action " 1135 "frame"); 1136 return; 1137 } 1138 1139 switch (act) { 1140 case WNM_BSS_TRANS_MGMT_REQ: 1141 ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end, 1142 !(mgmt->da[0] & 0x01)); 1143 break; 1144 case WNM_SLEEP_MODE_RESP: 1145 ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos); 1146 break; 1147 case WNM_NOTIFICATION_REQ: 1148 ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos); 1149 break; 1150 default: 1151 wpa_printf(MSG_ERROR, "WNM: Unknown request"); 1152 break; 1153 } 1154} 1155