1 /* 2 * BSS table 3 * Copyright (c) 2009-2010, Jouni Malinen <j (at) w1.fi> 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 "utils/eloop.h" 13 #include "common/ieee802_11_defs.h" 14 #include "drivers/driver.h" 15 #include "wpa_supplicant_i.h" 16 #include "config.h" 17 #include "notify.h" 18 #include "scan.h" 19 #include "bss.h" 20 21 22 /** 23 * WPA_BSS_EXPIRATION_PERIOD - Period of expiration run in seconds 24 */ 25 #define WPA_BSS_EXPIRATION_PERIOD 10 26 27 #define WPA_BSS_FREQ_CHANGED_FLAG BIT(0) 28 #define WPA_BSS_SIGNAL_CHANGED_FLAG BIT(1) 29 #define WPA_BSS_PRIVACY_CHANGED_FLAG BIT(2) 30 #define WPA_BSS_MODE_CHANGED_FLAG BIT(3) 31 #define WPA_BSS_WPAIE_CHANGED_FLAG BIT(4) 32 #define WPA_BSS_RSNIE_CHANGED_FLAG BIT(5) 33 #define WPA_BSS_WPS_CHANGED_FLAG BIT(6) 34 #define WPA_BSS_RATES_CHANGED_FLAG BIT(7) 35 #define WPA_BSS_IES_CHANGED_FLAG BIT(8) 36 37 38 static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) 39 { 40 dl_list_del(&bss->list); 41 dl_list_del(&bss->list_id); 42 wpa_s->num_bss--; 43 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR 44 " SSID '%s'", bss->id, MAC2STR(bss->bssid), 45 wpa_ssid_txt(bss->ssid, bss->ssid_len)); 46 wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id); 47 #ifdef CONFIG_INTERWORKING 48 wpabuf_free(bss->anqp_venue_name); 49 wpabuf_free(bss->anqp_network_auth_type); 50 wpabuf_free(bss->anqp_roaming_consortium); 51 wpabuf_free(bss->anqp_ip_addr_type_availability); 52 wpabuf_free(bss->anqp_nai_realm); 53 wpabuf_free(bss->anqp_3gpp); 54 wpabuf_free(bss->anqp_domain_name); 55 #endif /* CONFIG_INTERWORKING */ 56 os_free(bss); 57 } 58 59 60 struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid, 61 const u8 *ssid, size_t ssid_len) 62 { 63 struct wpa_bss *bss; 64 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { 65 if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 && 66 bss->ssid_len == ssid_len && 67 os_memcmp(bss->ssid, ssid, ssid_len) == 0) 68 return bss; 69 } 70 return NULL; 71 } 72 73 74 static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src) 75 { 76 os_time_t usec; 77 78 dst->flags = src->flags; 79 os_memcpy(dst->bssid, src->bssid, ETH_ALEN); 80 dst->freq = src->freq; 81 dst->beacon_int = src->beacon_int; 82 dst->caps = src->caps; 83 dst->qual = src->qual; 84 dst->noise = src->noise; 85 dst->level = src->level; 86 dst->tsf = src->tsf; 87 88 os_get_time(&dst->last_update); 89 dst->last_update.sec -= src->age / 1000; 90 usec = (src->age % 1000) * 1000; 91 if (dst->last_update.usec < usec) { 92 dst->last_update.sec--; 93 dst->last_update.usec += 1000000; 94 } 95 dst->last_update.usec -= usec; 96 } 97 98 99 static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) 100 { 101 struct wpa_ssid *ssid; 102 103 for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { 104 if (ssid->ssid == NULL || ssid->ssid_len == 0) 105 continue; 106 if (ssid->ssid_len == bss->ssid_len && 107 os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) == 0) 108 return 1; 109 } 110 111 return 0; 112 } 113 114 115 static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s) 116 { 117 struct wpa_bss *bss; 118 119 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { 120 if (!wpa_bss_known(wpa_s, bss)) { 121 wpa_bss_remove(wpa_s, bss); 122 return 0; 123 } 124 } 125 126 return -1; 127 } 128 129 130 static void wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s) 131 { 132 /* 133 * Remove the oldest entry that does not match with any configured 134 * network. 135 */ 136 if (wpa_bss_remove_oldest_unknown(wpa_s) == 0) 137 return; 138 139 /* 140 * Remove the oldest entry since no better candidate for removal was 141 * found. 142 */ 143 wpa_bss_remove(wpa_s, dl_list_first(&wpa_s->bss, 144 struct wpa_bss, list)); 145 } 146 147 148 static void wpa_bss_add(struct wpa_supplicant *wpa_s, 149 const u8 *ssid, size_t ssid_len, 150 struct wpa_scan_res *res) 151 { 152 struct wpa_bss *bss; 153 154 bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len); 155 if (bss == NULL) 156 return; 157 bss->id = wpa_s->bss_next_id++; 158 bss->last_update_idx = wpa_s->bss_update_idx; 159 wpa_bss_copy_res(bss, res); 160 os_memcpy(bss->ssid, ssid, ssid_len); 161 bss->ssid_len = ssid_len; 162 bss->ie_len = res->ie_len; 163 bss->beacon_ie_len = res->beacon_ie_len; 164 os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len); 165 166 dl_list_add_tail(&wpa_s->bss, &bss->list); 167 dl_list_add_tail(&wpa_s->bss_id, &bss->list_id); 168 wpa_s->num_bss++; 169 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR 170 " SSID '%s'", 171 bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len)); 172 wpas_notify_bss_added(wpa_s, bss->bssid, bss->id); 173 if (wpa_s->num_bss > wpa_s->conf->bss_max_count) 174 wpa_bss_remove_oldest(wpa_s); 175 } 176 177 178 static int are_ies_equal(const struct wpa_bss *old, 179 const struct wpa_scan_res *new, u32 ie) 180 { 181 const u8 *old_ie, *new_ie; 182 struct wpabuf *old_ie_buff = NULL; 183 struct wpabuf *new_ie_buff = NULL; 184 int new_ie_len, old_ie_len, ret, is_multi; 185 186 switch (ie) { 187 case WPA_IE_VENDOR_TYPE: 188 old_ie = wpa_bss_get_vendor_ie(old, ie); 189 new_ie = wpa_scan_get_vendor_ie(new, ie); 190 is_multi = 0; 191 break; 192 case WPS_IE_VENDOR_TYPE: 193 old_ie_buff = wpa_bss_get_vendor_ie_multi(old, ie); 194 new_ie_buff = wpa_scan_get_vendor_ie_multi(new, ie); 195 is_multi = 1; 196 break; 197 case WLAN_EID_RSN: 198 case WLAN_EID_SUPP_RATES: 199 case WLAN_EID_EXT_SUPP_RATES: 200 old_ie = wpa_bss_get_ie(old, ie); 201 new_ie = wpa_scan_get_ie(new, ie); 202 is_multi = 0; 203 break; 204 default: 205 wpa_printf(MSG_DEBUG, "bss: %s: cannot compare IEs", __func__); 206 return 0; 207 } 208 209 if (is_multi) { 210 /* in case of multiple IEs stored in buffer */ 211 old_ie = old_ie_buff ? wpabuf_head_u8(old_ie_buff) : NULL; 212 new_ie = new_ie_buff ? wpabuf_head_u8(new_ie_buff) : NULL; 213 old_ie_len = old_ie_buff ? wpabuf_len(old_ie_buff) : 0; 214 new_ie_len = new_ie_buff ? wpabuf_len(new_ie_buff) : 0; 215 } else { 216 /* in case of single IE */ 217 old_ie_len = old_ie ? old_ie[1] + 2 : 0; 218 new_ie_len = new_ie ? new_ie[1] + 2 : 0; 219 } 220 221 if (!old_ie || !new_ie) 222 ret = !old_ie && !new_ie; 223 else 224 ret = (old_ie_len == new_ie_len && 225 os_memcmp(old_ie, new_ie, old_ie_len) == 0); 226 227 wpabuf_free(old_ie_buff); 228 wpabuf_free(new_ie_buff); 229 230 return ret; 231 } 232 233 234 static u32 wpa_bss_compare_res(const struct wpa_bss *old, 235 const struct wpa_scan_res *new) 236 { 237 u32 changes = 0; 238 int caps_diff = old->caps ^ new->caps; 239 240 if (old->freq != new->freq) 241 changes |= WPA_BSS_FREQ_CHANGED_FLAG; 242 243 if (old->level != new->level) 244 changes |= WPA_BSS_SIGNAL_CHANGED_FLAG; 245 246 if (caps_diff & IEEE80211_CAP_PRIVACY) 247 changes |= WPA_BSS_PRIVACY_CHANGED_FLAG; 248 249 if (caps_diff & IEEE80211_CAP_IBSS) 250 changes |= WPA_BSS_MODE_CHANGED_FLAG; 251 252 if (old->ie_len == new->ie_len && 253 os_memcmp(old + 1, new + 1, old->ie_len) == 0) 254 return changes; 255 changes |= WPA_BSS_IES_CHANGED_FLAG; 256 257 if (!are_ies_equal(old, new, WPA_IE_VENDOR_TYPE)) 258 changes |= WPA_BSS_WPAIE_CHANGED_FLAG; 259 260 if (!are_ies_equal(old, new, WLAN_EID_RSN)) 261 changes |= WPA_BSS_RSNIE_CHANGED_FLAG; 262 263 if (!are_ies_equal(old, new, WPS_IE_VENDOR_TYPE)) 264 changes |= WPA_BSS_WPS_CHANGED_FLAG; 265 266 if (!are_ies_equal(old, new, WLAN_EID_SUPP_RATES) || 267 !are_ies_equal(old, new, WLAN_EID_EXT_SUPP_RATES)) 268 changes |= WPA_BSS_RATES_CHANGED_FLAG; 269 270 return changes; 271 } 272 273 274 static void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes, 275 const struct wpa_bss *bss) 276 { 277 if (changes & WPA_BSS_FREQ_CHANGED_FLAG) 278 wpas_notify_bss_freq_changed(wpa_s, bss->id); 279 280 if (changes & WPA_BSS_SIGNAL_CHANGED_FLAG) 281 wpas_notify_bss_signal_changed(wpa_s, bss->id); 282 283 if (changes & WPA_BSS_PRIVACY_CHANGED_FLAG) 284 wpas_notify_bss_privacy_changed(wpa_s, bss->id); 285 286 if (changes & WPA_BSS_MODE_CHANGED_FLAG) 287 wpas_notify_bss_mode_changed(wpa_s, bss->id); 288 289 if (changes & WPA_BSS_WPAIE_CHANGED_FLAG) 290 wpas_notify_bss_wpaie_changed(wpa_s, bss->id); 291 292 if (changes & WPA_BSS_RSNIE_CHANGED_FLAG) 293 wpas_notify_bss_rsnie_changed(wpa_s, bss->id); 294 295 if (changes & WPA_BSS_WPS_CHANGED_FLAG) 296 wpas_notify_bss_wps_changed(wpa_s, bss->id); 297 298 if (changes & WPA_BSS_IES_CHANGED_FLAG) 299 wpas_notify_bss_ies_changed(wpa_s, bss->id); 300 301 if (changes & WPA_BSS_RATES_CHANGED_FLAG) 302 wpas_notify_bss_rates_changed(wpa_s, bss->id); 303 } 304 305 306 static void wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, 307 struct wpa_scan_res *res) 308 { 309 u32 changes; 310 311 changes = wpa_bss_compare_res(bss, res); 312 bss->scan_miss_count = 0; 313 bss->last_update_idx = wpa_s->bss_update_idx; 314 wpa_bss_copy_res(bss, res); 315 /* Move the entry to the end of the list */ 316 dl_list_del(&bss->list); 317 if (bss->ie_len + bss->beacon_ie_len >= 318 res->ie_len + res->beacon_ie_len) { 319 os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len); 320 bss->ie_len = res->ie_len; 321 bss->beacon_ie_len = res->beacon_ie_len; 322 } else { 323 struct wpa_bss *nbss; 324 struct dl_list *prev = bss->list_id.prev; 325 dl_list_del(&bss->list_id); 326 nbss = os_realloc(bss, sizeof(*bss) + res->ie_len + 327 res->beacon_ie_len); 328 if (nbss) { 329 bss = nbss; 330 os_memcpy(bss + 1, res + 1, 331 res->ie_len + res->beacon_ie_len); 332 bss->ie_len = res->ie_len; 333 bss->beacon_ie_len = res->beacon_ie_len; 334 } 335 dl_list_add(prev, &bss->list_id); 336 } 337 dl_list_add_tail(&wpa_s->bss, &bss->list); 338 339 notify_bss_changes(wpa_s, changes, bss); 340 } 341 342 343 static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) 344 { 345 return bss == wpa_s->current_bss || 346 os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 || 347 os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0; 348 } 349 350 351 void wpa_bss_update_start(struct wpa_supplicant *wpa_s) 352 { 353 wpa_s->bss_update_idx++; 354 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u", 355 wpa_s->bss_update_idx); 356 } 357 358 359 void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, 360 struct wpa_scan_res *res) 361 { 362 const u8 *ssid, *p2p; 363 struct wpa_bss *bss; 364 365 ssid = wpa_scan_get_ie(res, WLAN_EID_SSID); 366 if (ssid == NULL) { 367 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for " 368 MACSTR, MAC2STR(res->bssid)); 369 return; 370 } 371 if (ssid[1] > 32) { 372 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for " 373 MACSTR, MAC2STR(res->bssid)); 374 return; 375 } 376 377 p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE); 378 if (p2p && ssid[1] == P2P_WILDCARD_SSID_LEN && 379 os_memcmp(ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0) 380 return; /* Skip P2P listen discovery results here */ 381 382 /* TODO: add option for ignoring BSSes we are not interested in 383 * (to save memory) */ 384 bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]); 385 if (bss == NULL) 386 wpa_bss_add(wpa_s, ssid + 2, ssid[1], res); 387 else 388 wpa_bss_update(wpa_s, bss, res); 389 } 390 391 392 static int wpa_bss_included_in_scan(const struct wpa_bss *bss, 393 const struct scan_info *info) 394 { 395 int found; 396 size_t i; 397 398 if (info == NULL) 399 return 1; 400 401 if (info->num_freqs) { 402 found = 0; 403 for (i = 0; i < info->num_freqs; i++) { 404 if (bss->freq == info->freqs[i]) { 405 found = 1; 406 break; 407 } 408 } 409 if (!found) 410 return 0; 411 } 412 413 if (info->num_ssids) { 414 found = 0; 415 for (i = 0; i < info->num_ssids; i++) { 416 const struct wpa_driver_scan_ssid *s = &info->ssids[i]; 417 if ((s->ssid == NULL || s->ssid_len == 0) || 418 (s->ssid_len == bss->ssid_len && 419 os_memcmp(s->ssid, bss->ssid, bss->ssid_len) == 420 0)) { 421 found = 1; 422 break; 423 } 424 } 425 if (!found) 426 return 0; 427 } 428 429 return 1; 430 } 431 432 433 void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info, 434 int new_scan) 435 { 436 struct wpa_bss *bss, *n; 437 438 if (!new_scan) 439 return; /* do not expire entries without new scan */ 440 441 dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) { 442 if (wpa_bss_in_use(wpa_s, bss)) 443 continue; 444 if (!wpa_bss_included_in_scan(bss, info)) 445 continue; /* expire only BSSes that were scanned */ 446 if (bss->last_update_idx < wpa_s->bss_update_idx) 447 bss->scan_miss_count++; 448 if (bss->scan_miss_count >= 449 wpa_s->conf->bss_expiration_scan_count) { 450 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Expire BSS %u due to " 451 "no match in scan", bss->id); 452 wpa_bss_remove(wpa_s, bss); 453 } 454 } 455 } 456 457 458 void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age) 459 { 460 struct wpa_bss *bss, *n; 461 struct os_time t; 462 463 if (dl_list_empty(&wpa_s->bss)) 464 return; 465 466 os_get_time(&t); 467 t.sec -= age; 468 469 dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) { 470 if (wpa_bss_in_use(wpa_s, bss)) 471 continue; 472 473 if (os_time_before(&bss->last_update, &t)) { 474 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Expire BSS %u due to " 475 "age", bss->id); 476 wpa_bss_remove(wpa_s, bss); 477 } else 478 break; 479 } 480 } 481 482 483 static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx) 484 { 485 struct wpa_supplicant *wpa_s = eloop_ctx; 486 487 wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age); 488 eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0, 489 wpa_bss_timeout, wpa_s, NULL); 490 } 491 492 493 int wpa_bss_init(struct wpa_supplicant *wpa_s) 494 { 495 dl_list_init(&wpa_s->bss); 496 dl_list_init(&wpa_s->bss_id); 497 eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0, 498 wpa_bss_timeout, wpa_s, NULL); 499 return 0; 500 } 501 502 503 void wpa_bss_flush(struct wpa_supplicant *wpa_s) 504 { 505 struct wpa_bss *bss, *n; 506 507 if (wpa_s->bss.next == NULL) 508 return; /* BSS table not yet initialized */ 509 510 dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) { 511 if (wpa_bss_in_use(wpa_s, bss)) 512 continue; 513 wpa_bss_remove(wpa_s, bss); 514 } 515 } 516 517 518 void wpa_bss_deinit(struct wpa_supplicant *wpa_s) 519 { 520 eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL); 521 wpa_bss_flush(wpa_s); 522 } 523 524 525 struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s, 526 const u8 *bssid) 527 { 528 struct wpa_bss *bss; 529 dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) { 530 if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0) 531 return bss; 532 } 533 return NULL; 534 } 535 536 537 #ifdef CONFIG_P2P 538 struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s, 539 const u8 *dev_addr) 540 { 541 struct wpa_bss *bss; 542 dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) { 543 u8 addr[ETH_ALEN]; 544 if (p2p_parse_dev_addr((const u8 *) (bss + 1), bss->ie_len, 545 addr) == 0 && 546 os_memcmp(addr, dev_addr, ETH_ALEN) == 0) 547 return bss; 548 } 549 return NULL; 550 } 551 #endif /* CONFIG_P2P */ 552 553 554 struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id) 555 { 556 struct wpa_bss *bss; 557 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { 558 if (bss->id == id) 559 return bss; 560 } 561 return NULL; 562 } 563 564 565 const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie) 566 { 567 const u8 *end, *pos; 568 569 pos = (const u8 *) (bss + 1); 570 end = pos + bss->ie_len; 571 572 while (pos + 1 < end) { 573 if (pos + 2 + pos[1] > end) 574 break; 575 if (pos[0] == ie) 576 return pos; 577 pos += 2 + pos[1]; 578 } 579 580 return NULL; 581 } 582 583 584 const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type) 585 { 586 const u8 *end, *pos; 587 588 pos = (const u8 *) (bss + 1); 589 end = pos + bss->ie_len; 590 591 while (pos + 1 < end) { 592 if (pos + 2 + pos[1] > end) 593 break; 594 if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && 595 vendor_type == WPA_GET_BE32(&pos[2])) 596 return pos; 597 pos += 2 + pos[1]; 598 } 599 600 return NULL; 601 } 602 603 604 struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss, 605 u32 vendor_type) 606 { 607 struct wpabuf *buf; 608 const u8 *end, *pos; 609 610 buf = wpabuf_alloc(bss->ie_len); 611 if (buf == NULL) 612 return NULL; 613 614 pos = (const u8 *) (bss + 1); 615 end = pos + bss->ie_len; 616 617 while (pos + 1 < end) { 618 if (pos + 2 + pos[1] > end) 619 break; 620 if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && 621 vendor_type == WPA_GET_BE32(&pos[2])) 622 wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4); 623 pos += 2 + pos[1]; 624 } 625 626 if (wpabuf_len(buf) == 0) { 627 wpabuf_free(buf); 628 buf = NULL; 629 } 630 631 return buf; 632 } 633 634 635 int wpa_bss_get_max_rate(const struct wpa_bss *bss) 636 { 637 int rate = 0; 638 const u8 *ie; 639 int i; 640 641 ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES); 642 for (i = 0; ie && i < ie[1]; i++) { 643 if ((ie[i + 2] & 0x7f) > rate) 644 rate = ie[i + 2] & 0x7f; 645 } 646 647 ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); 648 for (i = 0; ie && i < ie[1]; i++) { 649 if ((ie[i + 2] & 0x7f) > rate) 650 rate = ie[i + 2] & 0x7f; 651 } 652 653 return rate; 654 } 655 656 657 int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates) 658 { 659 const u8 *ie, *ie2; 660 int i, j; 661 unsigned int len; 662 u8 *r; 663 664 ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES); 665 ie2 = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); 666 667 len = (ie ? ie[1] : 0) + (ie2 ? ie2[1] : 0); 668 669 r = os_malloc(len); 670 if (!r) 671 return -1; 672 673 for (i = 0; ie && i < ie[1]; i++) 674 r[i] = ie[i + 2] & 0x7f; 675 676 for (j = 0; ie2 && j < ie2[1]; j++) 677 r[i + j] = ie2[j + 2] & 0x7f; 678 679 *rates = r; 680 return len; 681 } 682