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