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