1214501Srpaulo/*
2214501Srpaulo * BSS table
3252726Srpaulo * Copyright (c) 2009-2012, Jouni Malinen <j@w1.fi>
4214501Srpaulo *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7214501Srpaulo */
8214501Srpaulo
9214501Srpaulo#include "utils/includes.h"
10214501Srpaulo
11214501Srpaulo#include "utils/common.h"
12214501Srpaulo#include "utils/eloop.h"
13214501Srpaulo#include "common/ieee802_11_defs.h"
14214501Srpaulo#include "drivers/driver.h"
15214501Srpaulo#include "wpa_supplicant_i.h"
16214501Srpaulo#include "config.h"
17214501Srpaulo#include "notify.h"
18214501Srpaulo#include "scan.h"
19214501Srpaulo#include "bss.h"
20214501Srpaulo
21214501Srpaulo
22214501Srpaulo/**
23214501Srpaulo * WPA_BSS_EXPIRATION_PERIOD - Period of expiration run in seconds
24214501Srpaulo */
25214501Srpaulo#define WPA_BSS_EXPIRATION_PERIOD 10
26214501Srpaulo
27214501Srpaulo#define WPA_BSS_FREQ_CHANGED_FLAG	BIT(0)
28214501Srpaulo#define WPA_BSS_SIGNAL_CHANGED_FLAG	BIT(1)
29214501Srpaulo#define WPA_BSS_PRIVACY_CHANGED_FLAG	BIT(2)
30214501Srpaulo#define WPA_BSS_MODE_CHANGED_FLAG	BIT(3)
31214501Srpaulo#define WPA_BSS_WPAIE_CHANGED_FLAG	BIT(4)
32214501Srpaulo#define WPA_BSS_RSNIE_CHANGED_FLAG	BIT(5)
33214501Srpaulo#define WPA_BSS_WPS_CHANGED_FLAG	BIT(6)
34214501Srpaulo#define WPA_BSS_RATES_CHANGED_FLAG	BIT(7)
35214501Srpaulo#define WPA_BSS_IES_CHANGED_FLAG	BIT(8)
36214501Srpaulo
37214501Srpaulo
38252726Srpaulostatic void wpa_bss_set_hessid(struct wpa_bss *bss)
39214501Srpaulo{
40252726Srpaulo#ifdef CONFIG_INTERWORKING
41252726Srpaulo	const u8 *ie = wpa_bss_get_ie(bss, WLAN_EID_INTERWORKING);
42252726Srpaulo	if (ie == NULL || (ie[1] != 7 && ie[1] != 9)) {
43252726Srpaulo		os_memset(bss->hessid, 0, ETH_ALEN);
44252726Srpaulo		return;
45252726Srpaulo	}
46252726Srpaulo	if (ie[1] == 7)
47252726Srpaulo		os_memcpy(bss->hessid, ie + 3, ETH_ALEN);
48252726Srpaulo	else
49252726Srpaulo		os_memcpy(bss->hessid, ie + 5, ETH_ALEN);
50252726Srpaulo#endif /* CONFIG_INTERWORKING */
51252726Srpaulo}
52252726Srpaulo
53252726Srpaulo
54252726Srpaulo/**
55252726Srpaulo * wpa_bss_anqp_alloc - Allocate ANQP data structure for a BSS entry
56252726Srpaulo * Returns: Allocated ANQP data structure or %NULL on failure
57252726Srpaulo *
58252726Srpaulo * The allocated ANQP data structure has its users count set to 1. It may be
59252726Srpaulo * shared by multiple BSS entries and each shared entry is freed with
60252726Srpaulo * wpa_bss_anqp_free().
61252726Srpaulo */
62252726Srpaulostruct wpa_bss_anqp * wpa_bss_anqp_alloc(void)
63252726Srpaulo{
64252726Srpaulo	struct wpa_bss_anqp *anqp;
65252726Srpaulo	anqp = os_zalloc(sizeof(*anqp));
66252726Srpaulo	if (anqp == NULL)
67252726Srpaulo		return NULL;
68252726Srpaulo	anqp->users = 1;
69252726Srpaulo	return anqp;
70252726Srpaulo}
71252726Srpaulo
72252726Srpaulo
73252726Srpaulo/**
74252726Srpaulo * wpa_bss_anqp_clone - Clone an ANQP data structure
75252726Srpaulo * @anqp: ANQP data structure from wpa_bss_anqp_alloc()
76252726Srpaulo * Returns: Cloned ANQP data structure or %NULL on failure
77252726Srpaulo */
78252726Srpaulostatic struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp)
79252726Srpaulo{
80252726Srpaulo	struct wpa_bss_anqp *n;
81252726Srpaulo
82252726Srpaulo	n = os_zalloc(sizeof(*n));
83252726Srpaulo	if (n == NULL)
84252726Srpaulo		return NULL;
85252726Srpaulo
86252726Srpaulo#define ANQP_DUP(f) if (anqp->f) n->f = wpabuf_dup(anqp->f)
87252726Srpaulo#ifdef CONFIG_INTERWORKING
88252726Srpaulo	ANQP_DUP(venue_name);
89252726Srpaulo	ANQP_DUP(network_auth_type);
90252726Srpaulo	ANQP_DUP(roaming_consortium);
91252726Srpaulo	ANQP_DUP(ip_addr_type_availability);
92252726Srpaulo	ANQP_DUP(nai_realm);
93252726Srpaulo	ANQP_DUP(anqp_3gpp);
94252726Srpaulo	ANQP_DUP(domain_name);
95252726Srpaulo#endif /* CONFIG_INTERWORKING */
96252726Srpaulo#ifdef CONFIG_HS20
97252726Srpaulo	ANQP_DUP(hs20_operator_friendly_name);
98252726Srpaulo	ANQP_DUP(hs20_wan_metrics);
99252726Srpaulo	ANQP_DUP(hs20_connection_capability);
100252726Srpaulo	ANQP_DUP(hs20_operating_class);
101252726Srpaulo#endif /* CONFIG_HS20 */
102252726Srpaulo#undef ANQP_DUP
103252726Srpaulo
104252726Srpaulo	return n;
105252726Srpaulo}
106252726Srpaulo
107252726Srpaulo
108252726Srpaulo/**
109252726Srpaulo * wpa_bss_anqp_unshare_alloc - Unshare ANQP data (if shared) in a BSS entry
110252726Srpaulo * @bss: BSS entry
111252726Srpaulo * Returns: 0 on success, -1 on failure
112252726Srpaulo *
113252726Srpaulo * This function ensures the specific BSS entry has an ANQP data structure that
114252726Srpaulo * is not shared with any other BSS entry.
115252726Srpaulo */
116252726Srpauloint wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss)
117252726Srpaulo{
118252726Srpaulo	struct wpa_bss_anqp *anqp;
119252726Srpaulo
120252726Srpaulo	if (bss->anqp && bss->anqp->users > 1) {
121252726Srpaulo		/* allocated, but shared - clone an unshared copy */
122252726Srpaulo		anqp = wpa_bss_anqp_clone(bss->anqp);
123252726Srpaulo		if (anqp == NULL)
124252726Srpaulo			return -1;
125252726Srpaulo		anqp->users = 1;
126252726Srpaulo		bss->anqp->users--;
127252726Srpaulo		bss->anqp = anqp;
128252726Srpaulo		return 0;
129252726Srpaulo	}
130252726Srpaulo
131252726Srpaulo	if (bss->anqp)
132252726Srpaulo		return 0; /* already allocated and not shared */
133252726Srpaulo
134252726Srpaulo	/* not allocated - allocate a new storage area */
135252726Srpaulo	bss->anqp = wpa_bss_anqp_alloc();
136252726Srpaulo	return bss->anqp ? 0 : -1;
137252726Srpaulo}
138252726Srpaulo
139252726Srpaulo
140252726Srpaulo/**
141252726Srpaulo * wpa_bss_anqp_free - Free an ANQP data structure
142252726Srpaulo * @anqp: ANQP data structure from wpa_bss_anqp_alloc() or wpa_bss_anqp_clone()
143252726Srpaulo */
144252726Srpaulostatic void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp)
145252726Srpaulo{
146252726Srpaulo	if (anqp == NULL)
147252726Srpaulo		return;
148252726Srpaulo
149252726Srpaulo	anqp->users--;
150252726Srpaulo	if (anqp->users > 0) {
151252726Srpaulo		/* Another BSS entry holds a pointer to this ANQP info */
152252726Srpaulo		return;
153252726Srpaulo	}
154252726Srpaulo
155252726Srpaulo#ifdef CONFIG_INTERWORKING
156252726Srpaulo	wpabuf_free(anqp->venue_name);
157252726Srpaulo	wpabuf_free(anqp->network_auth_type);
158252726Srpaulo	wpabuf_free(anqp->roaming_consortium);
159252726Srpaulo	wpabuf_free(anqp->ip_addr_type_availability);
160252726Srpaulo	wpabuf_free(anqp->nai_realm);
161252726Srpaulo	wpabuf_free(anqp->anqp_3gpp);
162252726Srpaulo	wpabuf_free(anqp->domain_name);
163252726Srpaulo#endif /* CONFIG_INTERWORKING */
164252726Srpaulo#ifdef CONFIG_HS20
165252726Srpaulo	wpabuf_free(anqp->hs20_operator_friendly_name);
166252726Srpaulo	wpabuf_free(anqp->hs20_wan_metrics);
167252726Srpaulo	wpabuf_free(anqp->hs20_connection_capability);
168252726Srpaulo	wpabuf_free(anqp->hs20_operating_class);
169252726Srpaulo#endif /* CONFIG_HS20 */
170252726Srpaulo
171252726Srpaulo	os_free(anqp);
172252726Srpaulo}
173252726Srpaulo
174252726Srpaulo
175252726Srpaulostatic void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
176252726Srpaulo			   const char *reason)
177252726Srpaulo{
178252726Srpaulo	if (wpa_s->last_scan_res) {
179252726Srpaulo		unsigned int i;
180252726Srpaulo		for (i = 0; i < wpa_s->last_scan_res_used; i++) {
181252726Srpaulo			if (wpa_s->last_scan_res[i] == bss) {
182252726Srpaulo				os_memmove(&wpa_s->last_scan_res[i],
183252726Srpaulo					   &wpa_s->last_scan_res[i + 1],
184252726Srpaulo					   (wpa_s->last_scan_res_used - i - 1)
185252726Srpaulo					   * sizeof(struct wpa_bss *));
186252726Srpaulo				wpa_s->last_scan_res_used--;
187252726Srpaulo				break;
188252726Srpaulo			}
189252726Srpaulo		}
190252726Srpaulo	}
191214501Srpaulo	dl_list_del(&bss->list);
192214501Srpaulo	dl_list_del(&bss->list_id);
193214501Srpaulo	wpa_s->num_bss--;
194252726Srpaulo	wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR
195252726Srpaulo		" SSID '%s' due to %s", bss->id, MAC2STR(bss->bssid),
196252726Srpaulo		wpa_ssid_txt(bss->ssid, bss->ssid_len), reason);
197214501Srpaulo	wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id);
198252726Srpaulo	wpa_bss_anqp_free(bss->anqp);
199214501Srpaulo	os_free(bss);
200214501Srpaulo}
201214501Srpaulo
202214501Srpaulo
203252726Srpaulo/**
204252726Srpaulo * wpa_bss_get - Fetch a BSS table entry based on BSSID and SSID
205252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data
206252726Srpaulo * @bssid: BSSID
207252726Srpaulo * @ssid: SSID
208252726Srpaulo * @ssid_len: Length of @ssid
209252726Srpaulo * Returns: Pointer to the BSS entry or %NULL if not found
210252726Srpaulo */
211214501Srpaulostruct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
212214501Srpaulo			     const u8 *ssid, size_t ssid_len)
213214501Srpaulo{
214214501Srpaulo	struct wpa_bss *bss;
215252726Srpaulo	if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
216252726Srpaulo		return NULL;
217214501Srpaulo	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
218214501Srpaulo		if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
219214501Srpaulo		    bss->ssid_len == ssid_len &&
220214501Srpaulo		    os_memcmp(bss->ssid, ssid, ssid_len) == 0)
221214501Srpaulo			return bss;
222214501Srpaulo	}
223214501Srpaulo	return NULL;
224214501Srpaulo}
225214501Srpaulo
226214501Srpaulo
227214501Srpaulostatic void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src)
228214501Srpaulo{
229214501Srpaulo	os_time_t usec;
230214501Srpaulo
231214501Srpaulo	dst->flags = src->flags;
232214501Srpaulo	os_memcpy(dst->bssid, src->bssid, ETH_ALEN);
233214501Srpaulo	dst->freq = src->freq;
234214501Srpaulo	dst->beacon_int = src->beacon_int;
235214501Srpaulo	dst->caps = src->caps;
236214501Srpaulo	dst->qual = src->qual;
237214501Srpaulo	dst->noise = src->noise;
238214501Srpaulo	dst->level = src->level;
239214501Srpaulo	dst->tsf = src->tsf;
240214501Srpaulo
241214501Srpaulo	os_get_time(&dst->last_update);
242214501Srpaulo	dst->last_update.sec -= src->age / 1000;
243214501Srpaulo	usec = (src->age % 1000) * 1000;
244214501Srpaulo	if (dst->last_update.usec < usec) {
245214501Srpaulo		dst->last_update.sec--;
246214501Srpaulo		dst->last_update.usec += 1000000;
247214501Srpaulo	}
248214501Srpaulo	dst->last_update.usec -= usec;
249214501Srpaulo}
250214501Srpaulo
251214501Srpaulo
252252726Srpaulostatic int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
253214501Srpaulo{
254252726Srpaulo	struct wpa_ssid *ssid;
255252726Srpaulo
256252726Srpaulo	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
257252726Srpaulo		if (ssid->ssid == NULL || ssid->ssid_len == 0)
258252726Srpaulo			continue;
259252726Srpaulo		if (ssid->ssid_len == bss->ssid_len &&
260252726Srpaulo		    os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) == 0)
261252726Srpaulo			return 1;
262252726Srpaulo	}
263252726Srpaulo
264252726Srpaulo	return 0;
265252726Srpaulo}
266252726Srpaulo
267252726Srpaulo
268252726Srpaulostatic int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
269252726Srpaulo{
270252726Srpaulo	return bss == wpa_s->current_bss ||
271252726Srpaulo		os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
272252726Srpaulo		os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0;
273252726Srpaulo}
274252726Srpaulo
275252726Srpaulo
276252726Srpaulostatic int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s)
277252726Srpaulo{
278214501Srpaulo	struct wpa_bss *bss;
279214501Srpaulo
280252726Srpaulo	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
281252726Srpaulo		if (!wpa_bss_known(wpa_s, bss)) {
282252726Srpaulo			wpa_bss_remove(wpa_s, bss, __func__);
283252726Srpaulo			return 0;
284252726Srpaulo		}
285252726Srpaulo	}
286252726Srpaulo
287252726Srpaulo	return -1;
288252726Srpaulo}
289252726Srpaulo
290252726Srpaulo
291252726Srpaulostatic int wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s)
292252726Srpaulo{
293252726Srpaulo	struct wpa_bss *bss;
294252726Srpaulo
295252726Srpaulo	/*
296252726Srpaulo	 * Remove the oldest entry that does not match with any configured
297252726Srpaulo	 * network.
298252726Srpaulo	 */
299252726Srpaulo	if (wpa_bss_remove_oldest_unknown(wpa_s) == 0)
300252726Srpaulo		return 0;
301252726Srpaulo
302252726Srpaulo	/*
303252726Srpaulo	 * Remove the oldest entry that isn't currently in use.
304252726Srpaulo	 */
305252726Srpaulo	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
306252726Srpaulo		if (!wpa_bss_in_use(wpa_s, bss)) {
307252726Srpaulo			wpa_bss_remove(wpa_s, bss, __func__);
308252726Srpaulo			return 0;
309252726Srpaulo		}
310252726Srpaulo	}
311252726Srpaulo
312252726Srpaulo	return -1;
313252726Srpaulo}
314252726Srpaulo
315252726Srpaulo
316252726Srpaulostatic struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
317252726Srpaulo				    const u8 *ssid, size_t ssid_len,
318252726Srpaulo				    struct wpa_scan_res *res)
319252726Srpaulo{
320252726Srpaulo	struct wpa_bss *bss;
321252726Srpaulo
322214501Srpaulo	bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len);
323214501Srpaulo	if (bss == NULL)
324252726Srpaulo		return NULL;
325214501Srpaulo	bss->id = wpa_s->bss_next_id++;
326214501Srpaulo	bss->last_update_idx = wpa_s->bss_update_idx;
327214501Srpaulo	wpa_bss_copy_res(bss, res);
328214501Srpaulo	os_memcpy(bss->ssid, ssid, ssid_len);
329214501Srpaulo	bss->ssid_len = ssid_len;
330214501Srpaulo	bss->ie_len = res->ie_len;
331214501Srpaulo	bss->beacon_ie_len = res->beacon_ie_len;
332214501Srpaulo	os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
333252726Srpaulo	wpa_bss_set_hessid(bss);
334214501Srpaulo
335214501Srpaulo	dl_list_add_tail(&wpa_s->bss, &bss->list);
336214501Srpaulo	dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
337214501Srpaulo	wpa_s->num_bss++;
338252726Srpaulo	wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR
339252726Srpaulo		" SSID '%s'",
340252726Srpaulo		bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len));
341214501Srpaulo	wpas_notify_bss_added(wpa_s, bss->bssid, bss->id);
342252726Srpaulo	if (wpa_s->num_bss > wpa_s->conf->bss_max_count &&
343252726Srpaulo	    wpa_bss_remove_oldest(wpa_s) != 0) {
344252726Srpaulo		wpa_printf(MSG_ERROR, "Increasing the MAX BSS count to %d "
345252726Srpaulo			   "because all BSSes are in use. We should normally "
346252726Srpaulo			   "not get here!", (int) wpa_s->num_bss);
347252726Srpaulo		wpa_s->conf->bss_max_count = wpa_s->num_bss;
348214501Srpaulo	}
349252726Srpaulo	return bss;
350214501Srpaulo}
351214501Srpaulo
352214501Srpaulo
353214501Srpaulostatic int are_ies_equal(const struct wpa_bss *old,
354214501Srpaulo			 const struct wpa_scan_res *new, u32 ie)
355214501Srpaulo{
356214501Srpaulo	const u8 *old_ie, *new_ie;
357214501Srpaulo	struct wpabuf *old_ie_buff = NULL;
358214501Srpaulo	struct wpabuf *new_ie_buff = NULL;
359214501Srpaulo	int new_ie_len, old_ie_len, ret, is_multi;
360214501Srpaulo
361214501Srpaulo	switch (ie) {
362214501Srpaulo	case WPA_IE_VENDOR_TYPE:
363214501Srpaulo		old_ie = wpa_bss_get_vendor_ie(old, ie);
364214501Srpaulo		new_ie = wpa_scan_get_vendor_ie(new, ie);
365214501Srpaulo		is_multi = 0;
366214501Srpaulo		break;
367214501Srpaulo	case WPS_IE_VENDOR_TYPE:
368214501Srpaulo		old_ie_buff = wpa_bss_get_vendor_ie_multi(old, ie);
369214501Srpaulo		new_ie_buff = wpa_scan_get_vendor_ie_multi(new, ie);
370214501Srpaulo		is_multi = 1;
371214501Srpaulo		break;
372214501Srpaulo	case WLAN_EID_RSN:
373214501Srpaulo	case WLAN_EID_SUPP_RATES:
374214501Srpaulo	case WLAN_EID_EXT_SUPP_RATES:
375214501Srpaulo		old_ie = wpa_bss_get_ie(old, ie);
376214501Srpaulo		new_ie = wpa_scan_get_ie(new, ie);
377214501Srpaulo		is_multi = 0;
378214501Srpaulo		break;
379214501Srpaulo	default:
380214501Srpaulo		wpa_printf(MSG_DEBUG, "bss: %s: cannot compare IEs", __func__);
381214501Srpaulo		return 0;
382214501Srpaulo	}
383214501Srpaulo
384214501Srpaulo	if (is_multi) {
385214501Srpaulo		/* in case of multiple IEs stored in buffer */
386214501Srpaulo		old_ie = old_ie_buff ? wpabuf_head_u8(old_ie_buff) : NULL;
387214501Srpaulo		new_ie = new_ie_buff ? wpabuf_head_u8(new_ie_buff) : NULL;
388214501Srpaulo		old_ie_len = old_ie_buff ? wpabuf_len(old_ie_buff) : 0;
389214501Srpaulo		new_ie_len = new_ie_buff ? wpabuf_len(new_ie_buff) : 0;
390214501Srpaulo	} else {
391214501Srpaulo		/* in case of single IE */
392214501Srpaulo		old_ie_len = old_ie ? old_ie[1] + 2 : 0;
393214501Srpaulo		new_ie_len = new_ie ? new_ie[1] + 2 : 0;
394214501Srpaulo	}
395214501Srpaulo
396252726Srpaulo	if (!old_ie || !new_ie)
397252726Srpaulo		ret = !old_ie && !new_ie;
398252726Srpaulo	else
399252726Srpaulo		ret = (old_ie_len == new_ie_len &&
400252726Srpaulo		       os_memcmp(old_ie, new_ie, old_ie_len) == 0);
401214501Srpaulo
402214501Srpaulo	wpabuf_free(old_ie_buff);
403214501Srpaulo	wpabuf_free(new_ie_buff);
404214501Srpaulo
405214501Srpaulo	return ret;
406214501Srpaulo}
407214501Srpaulo
408214501Srpaulo
409214501Srpaulostatic u32 wpa_bss_compare_res(const struct wpa_bss *old,
410214501Srpaulo			       const struct wpa_scan_res *new)
411214501Srpaulo{
412214501Srpaulo	u32 changes = 0;
413214501Srpaulo	int caps_diff = old->caps ^ new->caps;
414214501Srpaulo
415214501Srpaulo	if (old->freq != new->freq)
416214501Srpaulo		changes |= WPA_BSS_FREQ_CHANGED_FLAG;
417214501Srpaulo
418214501Srpaulo	if (old->level != new->level)
419214501Srpaulo		changes |= WPA_BSS_SIGNAL_CHANGED_FLAG;
420214501Srpaulo
421214501Srpaulo	if (caps_diff & IEEE80211_CAP_PRIVACY)
422214501Srpaulo		changes |= WPA_BSS_PRIVACY_CHANGED_FLAG;
423214501Srpaulo
424214501Srpaulo	if (caps_diff & IEEE80211_CAP_IBSS)
425214501Srpaulo		changes |= WPA_BSS_MODE_CHANGED_FLAG;
426214501Srpaulo
427214501Srpaulo	if (old->ie_len == new->ie_len &&
428214501Srpaulo	    os_memcmp(old + 1, new + 1, old->ie_len) == 0)
429214501Srpaulo		return changes;
430214501Srpaulo	changes |= WPA_BSS_IES_CHANGED_FLAG;
431214501Srpaulo
432214501Srpaulo	if (!are_ies_equal(old, new, WPA_IE_VENDOR_TYPE))
433214501Srpaulo		changes |= WPA_BSS_WPAIE_CHANGED_FLAG;
434214501Srpaulo
435214501Srpaulo	if (!are_ies_equal(old, new, WLAN_EID_RSN))
436214501Srpaulo		changes |= WPA_BSS_RSNIE_CHANGED_FLAG;
437214501Srpaulo
438214501Srpaulo	if (!are_ies_equal(old, new, WPS_IE_VENDOR_TYPE))
439214501Srpaulo		changes |= WPA_BSS_WPS_CHANGED_FLAG;
440214501Srpaulo
441214501Srpaulo	if (!are_ies_equal(old, new, WLAN_EID_SUPP_RATES) ||
442214501Srpaulo	    !are_ies_equal(old, new, WLAN_EID_EXT_SUPP_RATES))
443214501Srpaulo		changes |= WPA_BSS_RATES_CHANGED_FLAG;
444214501Srpaulo
445214501Srpaulo	return changes;
446214501Srpaulo}
447214501Srpaulo
448214501Srpaulo
449214501Srpaulostatic void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes,
450214501Srpaulo			       const struct wpa_bss *bss)
451214501Srpaulo{
452214501Srpaulo	if (changes & WPA_BSS_FREQ_CHANGED_FLAG)
453214501Srpaulo		wpas_notify_bss_freq_changed(wpa_s, bss->id);
454214501Srpaulo
455214501Srpaulo	if (changes & WPA_BSS_SIGNAL_CHANGED_FLAG)
456214501Srpaulo		wpas_notify_bss_signal_changed(wpa_s, bss->id);
457214501Srpaulo
458214501Srpaulo	if (changes & WPA_BSS_PRIVACY_CHANGED_FLAG)
459214501Srpaulo		wpas_notify_bss_privacy_changed(wpa_s, bss->id);
460214501Srpaulo
461214501Srpaulo	if (changes & WPA_BSS_MODE_CHANGED_FLAG)
462214501Srpaulo		wpas_notify_bss_mode_changed(wpa_s, bss->id);
463214501Srpaulo
464214501Srpaulo	if (changes & WPA_BSS_WPAIE_CHANGED_FLAG)
465214501Srpaulo		wpas_notify_bss_wpaie_changed(wpa_s, bss->id);
466214501Srpaulo
467214501Srpaulo	if (changes & WPA_BSS_RSNIE_CHANGED_FLAG)
468214501Srpaulo		wpas_notify_bss_rsnie_changed(wpa_s, bss->id);
469214501Srpaulo
470214501Srpaulo	if (changes & WPA_BSS_WPS_CHANGED_FLAG)
471214501Srpaulo		wpas_notify_bss_wps_changed(wpa_s, bss->id);
472214501Srpaulo
473214501Srpaulo	if (changes & WPA_BSS_IES_CHANGED_FLAG)
474214501Srpaulo		wpas_notify_bss_ies_changed(wpa_s, bss->id);
475214501Srpaulo
476214501Srpaulo	if (changes & WPA_BSS_RATES_CHANGED_FLAG)
477214501Srpaulo		wpas_notify_bss_rates_changed(wpa_s, bss->id);
478214501Srpaulo}
479214501Srpaulo
480214501Srpaulo
481252726Srpaulostatic struct wpa_bss *
482252726Srpaulowpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
483252726Srpaulo	       struct wpa_scan_res *res)
484214501Srpaulo{
485214501Srpaulo	u32 changes;
486214501Srpaulo
487214501Srpaulo	changes = wpa_bss_compare_res(bss, res);
488214501Srpaulo	bss->scan_miss_count = 0;
489214501Srpaulo	bss->last_update_idx = wpa_s->bss_update_idx;
490214501Srpaulo	wpa_bss_copy_res(bss, res);
491214501Srpaulo	/* Move the entry to the end of the list */
492214501Srpaulo	dl_list_del(&bss->list);
493214501Srpaulo	if (bss->ie_len + bss->beacon_ie_len >=
494214501Srpaulo	    res->ie_len + res->beacon_ie_len) {
495214501Srpaulo		os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
496214501Srpaulo		bss->ie_len = res->ie_len;
497214501Srpaulo		bss->beacon_ie_len = res->beacon_ie_len;
498214501Srpaulo	} else {
499214501Srpaulo		struct wpa_bss *nbss;
500214501Srpaulo		struct dl_list *prev = bss->list_id.prev;
501214501Srpaulo		dl_list_del(&bss->list_id);
502214501Srpaulo		nbss = os_realloc(bss, sizeof(*bss) + res->ie_len +
503214501Srpaulo				  res->beacon_ie_len);
504214501Srpaulo		if (nbss) {
505252726Srpaulo			unsigned int i;
506252726Srpaulo			for (i = 0; i < wpa_s->last_scan_res_used; i++) {
507252726Srpaulo				if (wpa_s->last_scan_res[i] == bss) {
508252726Srpaulo					wpa_s->last_scan_res[i] = nbss;
509252726Srpaulo					break;
510252726Srpaulo				}
511252726Srpaulo			}
512252726Srpaulo			if (wpa_s->current_bss == bss)
513252726Srpaulo				wpa_s->current_bss = nbss;
514214501Srpaulo			bss = nbss;
515214501Srpaulo			os_memcpy(bss + 1, res + 1,
516214501Srpaulo				  res->ie_len + res->beacon_ie_len);
517214501Srpaulo			bss->ie_len = res->ie_len;
518214501Srpaulo			bss->beacon_ie_len = res->beacon_ie_len;
519214501Srpaulo		}
520214501Srpaulo		dl_list_add(prev, &bss->list_id);
521214501Srpaulo	}
522252726Srpaulo	if (changes & WPA_BSS_IES_CHANGED_FLAG)
523252726Srpaulo		wpa_bss_set_hessid(bss);
524214501Srpaulo	dl_list_add_tail(&wpa_s->bss, &bss->list);
525214501Srpaulo
526214501Srpaulo	notify_bss_changes(wpa_s, changes, bss);
527214501Srpaulo
528252726Srpaulo	return bss;
529214501Srpaulo}
530214501Srpaulo
531214501Srpaulo
532252726Srpaulo/**
533252726Srpaulo * wpa_bss_update_start - Start a BSS table update from scan results
534252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data
535252726Srpaulo *
536252726Srpaulo * This function is called at the start of each BSS table update round for new
537252726Srpaulo * scan results. The actual scan result entries are indicated with calls to
538252726Srpaulo * wpa_bss_update_scan_res() and the update round is finished with a call to
539252726Srpaulo * wpa_bss_update_end().
540252726Srpaulo */
541214501Srpaulovoid wpa_bss_update_start(struct wpa_supplicant *wpa_s)
542214501Srpaulo{
543214501Srpaulo	wpa_s->bss_update_idx++;
544252726Srpaulo	wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u",
545252726Srpaulo		wpa_s->bss_update_idx);
546252726Srpaulo	wpa_s->last_scan_res_used = 0;
547214501Srpaulo}
548214501Srpaulo
549214501Srpaulo
550252726Srpaulo/**
551252726Srpaulo * wpa_bss_update_scan_res - Update a BSS table entry based on a scan result
552252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data
553252726Srpaulo * @res: Scan result
554252726Srpaulo *
555252726Srpaulo * This function updates a BSS table entry (or adds one) based on a scan result.
556252726Srpaulo * This is called separately for each scan result between the calls to
557252726Srpaulo * wpa_bss_update_start() and wpa_bss_update_end().
558252726Srpaulo */
559214501Srpaulovoid wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
560214501Srpaulo			     struct wpa_scan_res *res)
561214501Srpaulo{
562252726Srpaulo	const u8 *ssid, *p2p;
563214501Srpaulo	struct wpa_bss *bss;
564214501Srpaulo
565214501Srpaulo	ssid = wpa_scan_get_ie(res, WLAN_EID_SSID);
566214501Srpaulo	if (ssid == NULL) {
567252726Srpaulo		wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for "
568252726Srpaulo			MACSTR, MAC2STR(res->bssid));
569214501Srpaulo		return;
570214501Srpaulo	}
571214501Srpaulo	if (ssid[1] > 32) {
572252726Srpaulo		wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for "
573252726Srpaulo			MACSTR, MAC2STR(res->bssid));
574214501Srpaulo		return;
575214501Srpaulo	}
576214501Srpaulo
577252726Srpaulo	p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE);
578252726Srpaulo#ifdef CONFIG_P2P
579252726Srpaulo	if (p2p == NULL &&
580252726Srpaulo	    wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
581252726Srpaulo		/*
582252726Srpaulo		 * If it's a P2P specific interface, then don't update
583252726Srpaulo		 * the scan result without a P2P IE.
584252726Srpaulo		 */
585252726Srpaulo		wpa_printf(MSG_DEBUG, "BSS: No P2P IE - skipping BSS " MACSTR
586252726Srpaulo			   " update for P2P interface", MAC2STR(res->bssid));
587252726Srpaulo		return;
588252726Srpaulo	}
589252726Srpaulo#endif /* CONFIG_P2P */
590252726Srpaulo	if (p2p && ssid[1] == P2P_WILDCARD_SSID_LEN &&
591252726Srpaulo	    os_memcmp(ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0)
592252726Srpaulo		return; /* Skip P2P listen discovery results here */
593252726Srpaulo
594214501Srpaulo	/* TODO: add option for ignoring BSSes we are not interested in
595214501Srpaulo	 * (to save memory) */
596214501Srpaulo	bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
597214501Srpaulo	if (bss == NULL)
598252726Srpaulo		bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res);
599214501Srpaulo	else
600252726Srpaulo		bss = wpa_bss_update(wpa_s, bss, res);
601252726Srpaulo
602252726Srpaulo	if (bss == NULL)
603252726Srpaulo		return;
604252726Srpaulo	if (wpa_s->last_scan_res_used >= wpa_s->last_scan_res_size) {
605252726Srpaulo		struct wpa_bss **n;
606252726Srpaulo		unsigned int siz;
607252726Srpaulo		if (wpa_s->last_scan_res_size == 0)
608252726Srpaulo			siz = 32;
609252726Srpaulo		else
610252726Srpaulo			siz = wpa_s->last_scan_res_size * 2;
611252726Srpaulo		n = os_realloc_array(wpa_s->last_scan_res, siz,
612252726Srpaulo				     sizeof(struct wpa_bss *));
613252726Srpaulo		if (n == NULL)
614252726Srpaulo			return;
615252726Srpaulo		wpa_s->last_scan_res = n;
616252726Srpaulo		wpa_s->last_scan_res_size = siz;
617252726Srpaulo	}
618252726Srpaulo
619252726Srpaulo	wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss;
620214501Srpaulo}
621214501Srpaulo
622214501Srpaulo
623214501Srpaulostatic int wpa_bss_included_in_scan(const struct wpa_bss *bss,
624214501Srpaulo				    const struct scan_info *info)
625214501Srpaulo{
626214501Srpaulo	int found;
627214501Srpaulo	size_t i;
628214501Srpaulo
629214501Srpaulo	if (info == NULL)
630214501Srpaulo		return 1;
631214501Srpaulo
632214501Srpaulo	if (info->num_freqs) {
633214501Srpaulo		found = 0;
634214501Srpaulo		for (i = 0; i < info->num_freqs; i++) {
635214501Srpaulo			if (bss->freq == info->freqs[i]) {
636214501Srpaulo				found = 1;
637214501Srpaulo				break;
638214501Srpaulo			}
639214501Srpaulo		}
640214501Srpaulo		if (!found)
641214501Srpaulo			return 0;
642214501Srpaulo	}
643214501Srpaulo
644214501Srpaulo	if (info->num_ssids) {
645214501Srpaulo		found = 0;
646214501Srpaulo		for (i = 0; i < info->num_ssids; i++) {
647214501Srpaulo			const struct wpa_driver_scan_ssid *s = &info->ssids[i];
648214501Srpaulo			if ((s->ssid == NULL || s->ssid_len == 0) ||
649214501Srpaulo			    (s->ssid_len == bss->ssid_len &&
650214501Srpaulo			     os_memcmp(s->ssid, bss->ssid, bss->ssid_len) ==
651214501Srpaulo			     0)) {
652214501Srpaulo				found = 1;
653214501Srpaulo				break;
654214501Srpaulo			}
655214501Srpaulo		}
656214501Srpaulo		if (!found)
657214501Srpaulo			return 0;
658214501Srpaulo	}
659214501Srpaulo
660214501Srpaulo	return 1;
661214501Srpaulo}
662214501Srpaulo
663214501Srpaulo
664252726Srpaulo/**
665252726Srpaulo * wpa_bss_update_end - End a BSS table update from scan results
666252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data
667252726Srpaulo * @info: Information about scan parameters
668252726Srpaulo * @new_scan: Whether this update round was based on a new scan
669252726Srpaulo *
670252726Srpaulo * This function is called at the end of each BSS table update round for new
671252726Srpaulo * scan results. The start of the update was indicated with a call to
672252726Srpaulo * wpa_bss_update_start().
673252726Srpaulo */
674214501Srpaulovoid wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
675214501Srpaulo			int new_scan)
676214501Srpaulo{
677214501Srpaulo	struct wpa_bss *bss, *n;
678214501Srpaulo
679252726Srpaulo	wpa_s->last_scan_full = 0;
680252726Srpaulo	os_get_time(&wpa_s->last_scan);
681214501Srpaulo	if (!new_scan)
682214501Srpaulo		return; /* do not expire entries without new scan */
683214501Srpaulo
684252726Srpaulo	if (info && !info->aborted && !info->freqs) {
685252726Srpaulo		size_t i;
686252726Srpaulo		if (info->num_ssids == 0) {
687252726Srpaulo			wpa_s->last_scan_full = 1;
688252726Srpaulo		} else {
689252726Srpaulo			for (i = 0; i < info->num_ssids; i++) {
690252726Srpaulo				if (info->ssids[i].ssid == NULL ||
691252726Srpaulo				    info->ssids[i].ssid_len == 0) {
692252726Srpaulo					wpa_s->last_scan_full = 1;
693252726Srpaulo					break;
694252726Srpaulo				}
695252726Srpaulo			}
696252726Srpaulo		}
697252726Srpaulo	}
698252726Srpaulo
699214501Srpaulo	dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
700214501Srpaulo		if (wpa_bss_in_use(wpa_s, bss))
701214501Srpaulo			continue;
702214501Srpaulo		if (!wpa_bss_included_in_scan(bss, info))
703214501Srpaulo			continue; /* expire only BSSes that were scanned */
704214501Srpaulo		if (bss->last_update_idx < wpa_s->bss_update_idx)
705214501Srpaulo			bss->scan_miss_count++;
706252726Srpaulo		if (bss->scan_miss_count >=
707252726Srpaulo		    wpa_s->conf->bss_expiration_scan_count) {
708252726Srpaulo			wpa_bss_remove(wpa_s, bss, "no match in scan");
709214501Srpaulo		}
710214501Srpaulo	}
711252726Srpaulo
712252726Srpaulo	wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%u/%u "
713252726Srpaulo		   "last_scan_full=%d",
714252726Srpaulo		   wpa_s->last_scan_res_used, wpa_s->last_scan_res_size,
715252726Srpaulo		   wpa_s->last_scan_full);
716214501Srpaulo}
717214501Srpaulo
718214501Srpaulo
719252726Srpaulo/**
720252726Srpaulo * wpa_bss_flush_by_age - Flush old BSS entries
721252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data
722252726Srpaulo * @age: Maximum entry age in seconds
723252726Srpaulo *
724252726Srpaulo * Remove BSS entries that have not been updated during the last @age seconds.
725252726Srpaulo */
726252726Srpaulovoid wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age)
727214501Srpaulo{
728214501Srpaulo	struct wpa_bss *bss, *n;
729214501Srpaulo	struct os_time t;
730214501Srpaulo
731214501Srpaulo	if (dl_list_empty(&wpa_s->bss))
732214501Srpaulo		return;
733214501Srpaulo
734214501Srpaulo	os_get_time(&t);
735252726Srpaulo	t.sec -= age;
736214501Srpaulo
737214501Srpaulo	dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
738214501Srpaulo		if (wpa_bss_in_use(wpa_s, bss))
739214501Srpaulo			continue;
740214501Srpaulo
741214501Srpaulo		if (os_time_before(&bss->last_update, &t)) {
742252726Srpaulo			wpa_bss_remove(wpa_s, bss, __func__);
743214501Srpaulo		} else
744214501Srpaulo			break;
745214501Srpaulo	}
746252726Srpaulo}
747252726Srpaulo
748252726Srpaulo
749252726Srpaulostatic void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx)
750252726Srpaulo{
751252726Srpaulo	struct wpa_supplicant *wpa_s = eloop_ctx;
752252726Srpaulo
753252726Srpaulo	wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age);
754214501Srpaulo	eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0,
755214501Srpaulo			       wpa_bss_timeout, wpa_s, NULL);
756214501Srpaulo}
757214501Srpaulo
758214501Srpaulo
759252726Srpaulo/**
760252726Srpaulo * wpa_bss_init - Initialize BSS table
761252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data
762252726Srpaulo * Returns: 0 on success, -1 on failure
763252726Srpaulo *
764252726Srpaulo * This prepares BSS table lists and timer for periodic updates. The BSS table
765252726Srpaulo * is deinitialized with wpa_bss_deinit() once not needed anymore.
766252726Srpaulo */
767214501Srpauloint wpa_bss_init(struct wpa_supplicant *wpa_s)
768214501Srpaulo{
769214501Srpaulo	dl_list_init(&wpa_s->bss);
770214501Srpaulo	dl_list_init(&wpa_s->bss_id);
771214501Srpaulo	eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0,
772214501Srpaulo			       wpa_bss_timeout, wpa_s, NULL);
773214501Srpaulo	return 0;
774214501Srpaulo}
775214501Srpaulo
776214501Srpaulo
777252726Srpaulo/**
778252726Srpaulo * wpa_bss_flush - Flush all unused BSS entries
779252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data
780252726Srpaulo */
781252726Srpaulovoid wpa_bss_flush(struct wpa_supplicant *wpa_s)
782214501Srpaulo{
783214501Srpaulo	struct wpa_bss *bss, *n;
784252726Srpaulo
785214501Srpaulo	if (wpa_s->bss.next == NULL)
786214501Srpaulo		return; /* BSS table not yet initialized */
787252726Srpaulo
788252726Srpaulo	dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
789252726Srpaulo		if (wpa_bss_in_use(wpa_s, bss))
790252726Srpaulo			continue;
791252726Srpaulo		wpa_bss_remove(wpa_s, bss, __func__);
792252726Srpaulo	}
793214501Srpaulo}
794214501Srpaulo
795214501Srpaulo
796252726Srpaulo/**
797252726Srpaulo * wpa_bss_deinit - Deinitialize BSS table
798252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data
799252726Srpaulo */
800252726Srpaulovoid wpa_bss_deinit(struct wpa_supplicant *wpa_s)
801252726Srpaulo{
802252726Srpaulo	eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL);
803252726Srpaulo	wpa_bss_flush(wpa_s);
804252726Srpaulo}
805252726Srpaulo
806252726Srpaulo
807252726Srpaulo/**
808252726Srpaulo * wpa_bss_get_bssid - Fetch a BSS table entry based on BSSID
809252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data
810252726Srpaulo * @bssid: BSSID
811252726Srpaulo * Returns: Pointer to the BSS entry or %NULL if not found
812252726Srpaulo */
813214501Srpaulostruct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
814214501Srpaulo				   const u8 *bssid)
815214501Srpaulo{
816214501Srpaulo	struct wpa_bss *bss;
817252726Srpaulo	if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
818252726Srpaulo		return NULL;
819252726Srpaulo	dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
820214501Srpaulo		if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
821214501Srpaulo			return bss;
822214501Srpaulo	}
823214501Srpaulo	return NULL;
824214501Srpaulo}
825214501Srpaulo
826214501Srpaulo
827252726Srpaulo#ifdef CONFIG_P2P
828252726Srpaulo/**
829252726Srpaulo * wpa_bss_get_p2p_dev_addr - Fetch a BSS table entry based on P2P Device Addr
830252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data
831252726Srpaulo * @dev_addr: P2P Device Address of the GO
832252726Srpaulo * Returns: Pointer to the BSS entry or %NULL if not found
833252726Srpaulo */
834252726Srpaulostruct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s,
835252726Srpaulo					  const u8 *dev_addr)
836252726Srpaulo{
837252726Srpaulo	struct wpa_bss *bss;
838252726Srpaulo	dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
839252726Srpaulo		u8 addr[ETH_ALEN];
840252726Srpaulo		if (p2p_parse_dev_addr((const u8 *) (bss + 1), bss->ie_len,
841252726Srpaulo				       addr) == 0 &&
842252726Srpaulo		    os_memcmp(addr, dev_addr, ETH_ALEN) == 0)
843252726Srpaulo			return bss;
844252726Srpaulo	}
845252726Srpaulo	return NULL;
846252726Srpaulo}
847252726Srpaulo#endif /* CONFIG_P2P */
848252726Srpaulo
849252726Srpaulo
850252726Srpaulo/**
851252726Srpaulo * wpa_bss_get_id - Fetch a BSS table entry based on identifier
852252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data
853252726Srpaulo * @id: Unique identifier (struct wpa_bss::id) assigned for the entry
854252726Srpaulo * Returns: Pointer to the BSS entry or %NULL if not found
855252726Srpaulo */
856214501Srpaulostruct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
857214501Srpaulo{
858214501Srpaulo	struct wpa_bss *bss;
859214501Srpaulo	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
860214501Srpaulo		if (bss->id == id)
861214501Srpaulo			return bss;
862214501Srpaulo	}
863214501Srpaulo	return NULL;
864214501Srpaulo}
865214501Srpaulo
866214501Srpaulo
867252726Srpaulo/**
868252726Srpaulo * wpa_bss_get_ie - Fetch a specified information element from a BSS entry
869252726Srpaulo * @bss: BSS table entry
870252726Srpaulo * @ie: Information element identitifier (WLAN_EID_*)
871252726Srpaulo * Returns: Pointer to the information element (id field) or %NULL if not found
872252726Srpaulo *
873252726Srpaulo * This function returns the first matching information element in the BSS
874252726Srpaulo * entry.
875252726Srpaulo */
876214501Srpauloconst u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie)
877214501Srpaulo{
878214501Srpaulo	const u8 *end, *pos;
879214501Srpaulo
880214501Srpaulo	pos = (const u8 *) (bss + 1);
881214501Srpaulo	end = pos + bss->ie_len;
882214501Srpaulo
883214501Srpaulo	while (pos + 1 < end) {
884214501Srpaulo		if (pos + 2 + pos[1] > end)
885214501Srpaulo			break;
886214501Srpaulo		if (pos[0] == ie)
887214501Srpaulo			return pos;
888214501Srpaulo		pos += 2 + pos[1];
889214501Srpaulo	}
890214501Srpaulo
891214501Srpaulo	return NULL;
892214501Srpaulo}
893214501Srpaulo
894214501Srpaulo
895252726Srpaulo/**
896252726Srpaulo * wpa_bss_get_vendor_ie - Fetch a vendor information element from a BSS entry
897252726Srpaulo * @bss: BSS table entry
898252726Srpaulo * @vendor_type: Vendor type (four octets starting the IE payload)
899252726Srpaulo * Returns: Pointer to the information element (id field) or %NULL if not found
900252726Srpaulo *
901252726Srpaulo * This function returns the first matching information element in the BSS
902252726Srpaulo * entry.
903252726Srpaulo */
904214501Srpauloconst u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type)
905214501Srpaulo{
906214501Srpaulo	const u8 *end, *pos;
907214501Srpaulo
908214501Srpaulo	pos = (const u8 *) (bss + 1);
909214501Srpaulo	end = pos + bss->ie_len;
910214501Srpaulo
911214501Srpaulo	while (pos + 1 < end) {
912214501Srpaulo		if (pos + 2 + pos[1] > end)
913214501Srpaulo			break;
914214501Srpaulo		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
915214501Srpaulo		    vendor_type == WPA_GET_BE32(&pos[2]))
916214501Srpaulo			return pos;
917214501Srpaulo		pos += 2 + pos[1];
918214501Srpaulo	}
919214501Srpaulo
920214501Srpaulo	return NULL;
921214501Srpaulo}
922214501Srpaulo
923214501Srpaulo
924252726Srpaulo/**
925252726Srpaulo * wpa_bss_get_vendor_ie_multi - Fetch vendor IE data from a BSS entry
926252726Srpaulo * @bss: BSS table entry
927252726Srpaulo * @vendor_type: Vendor type (four octets starting the IE payload)
928252726Srpaulo * Returns: Pointer to the information element payload or %NULL if not found
929252726Srpaulo *
930252726Srpaulo * This function returns concatenated payload of possibly fragmented vendor
931252726Srpaulo * specific information elements in the BSS entry. The caller is responsible for
932252726Srpaulo * freeing the returned buffer.
933252726Srpaulo */
934214501Srpaulostruct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
935214501Srpaulo					    u32 vendor_type)
936214501Srpaulo{
937214501Srpaulo	struct wpabuf *buf;
938214501Srpaulo	const u8 *end, *pos;
939214501Srpaulo
940214501Srpaulo	buf = wpabuf_alloc(bss->ie_len);
941214501Srpaulo	if (buf == NULL)
942214501Srpaulo		return NULL;
943214501Srpaulo
944214501Srpaulo	pos = (const u8 *) (bss + 1);
945214501Srpaulo	end = pos + bss->ie_len;
946214501Srpaulo
947214501Srpaulo	while (pos + 1 < end) {
948214501Srpaulo		if (pos + 2 + pos[1] > end)
949214501Srpaulo			break;
950214501Srpaulo		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
951214501Srpaulo		    vendor_type == WPA_GET_BE32(&pos[2]))
952214501Srpaulo			wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
953214501Srpaulo		pos += 2 + pos[1];
954214501Srpaulo	}
955214501Srpaulo
956214501Srpaulo	if (wpabuf_len(buf) == 0) {
957214501Srpaulo		wpabuf_free(buf);
958214501Srpaulo		buf = NULL;
959214501Srpaulo	}
960214501Srpaulo
961214501Srpaulo	return buf;
962214501Srpaulo}
963214501Srpaulo
964214501Srpaulo
965252726Srpaulo/**
966252726Srpaulo * wpa_bss_get_vendor_ie_multi_beacon - Fetch vendor IE data from a BSS entry
967252726Srpaulo * @bss: BSS table entry
968252726Srpaulo * @vendor_type: Vendor type (four octets starting the IE payload)
969252726Srpaulo * Returns: Pointer to the information element payload or %NULL if not found
970252726Srpaulo *
971252726Srpaulo * This function returns concatenated payload of possibly fragmented vendor
972252726Srpaulo * specific information elements in the BSS entry. The caller is responsible for
973252726Srpaulo * freeing the returned buffer.
974252726Srpaulo *
975252726Srpaulo * This function is like wpa_bss_get_vendor_ie_multi(), but uses IE buffer only
976252726Srpaulo * from Beacon frames instead of either Beacon or Probe Response frames.
977252726Srpaulo */
978252726Srpaulostruct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss,
979252726Srpaulo						   u32 vendor_type)
980252726Srpaulo{
981252726Srpaulo	struct wpabuf *buf;
982252726Srpaulo	const u8 *end, *pos;
983252726Srpaulo
984252726Srpaulo	buf = wpabuf_alloc(bss->beacon_ie_len);
985252726Srpaulo	if (buf == NULL)
986252726Srpaulo		return NULL;
987252726Srpaulo
988252726Srpaulo	pos = (const u8 *) (bss + 1);
989252726Srpaulo	pos += bss->ie_len;
990252726Srpaulo	end = pos + bss->beacon_ie_len;
991252726Srpaulo
992252726Srpaulo	while (pos + 1 < end) {
993252726Srpaulo		if (pos + 2 + pos[1] > end)
994252726Srpaulo			break;
995252726Srpaulo		if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
996252726Srpaulo		    vendor_type == WPA_GET_BE32(&pos[2]))
997252726Srpaulo			wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
998252726Srpaulo		pos += 2 + pos[1];
999252726Srpaulo	}
1000252726Srpaulo
1001252726Srpaulo	if (wpabuf_len(buf) == 0) {
1002252726Srpaulo		wpabuf_free(buf);
1003252726Srpaulo		buf = NULL;
1004252726Srpaulo	}
1005252726Srpaulo
1006252726Srpaulo	return buf;
1007252726Srpaulo}
1008252726Srpaulo
1009252726Srpaulo
1010252726Srpaulo/**
1011252726Srpaulo * wpa_bss_get_max_rate - Get maximum legacy TX rate supported in a BSS
1012252726Srpaulo * @bss: BSS table entry
1013252726Srpaulo * Returns: Maximum legacy rate in units of 500 kbps
1014252726Srpaulo */
1015214501Srpauloint wpa_bss_get_max_rate(const struct wpa_bss *bss)
1016214501Srpaulo{
1017214501Srpaulo	int rate = 0;
1018214501Srpaulo	const u8 *ie;
1019214501Srpaulo	int i;
1020214501Srpaulo
1021214501Srpaulo	ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
1022214501Srpaulo	for (i = 0; ie && i < ie[1]; i++) {
1023214501Srpaulo		if ((ie[i + 2] & 0x7f) > rate)
1024214501Srpaulo			rate = ie[i + 2] & 0x7f;
1025214501Srpaulo	}
1026214501Srpaulo
1027214501Srpaulo	ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
1028214501Srpaulo	for (i = 0; ie && i < ie[1]; i++) {
1029214501Srpaulo		if ((ie[i + 2] & 0x7f) > rate)
1030214501Srpaulo			rate = ie[i + 2] & 0x7f;
1031214501Srpaulo	}
1032214501Srpaulo
1033214501Srpaulo	return rate;
1034214501Srpaulo}
1035214501Srpaulo
1036214501Srpaulo
1037252726Srpaulo/**
1038252726Srpaulo * wpa_bss_get_bit_rates - Get legacy TX rates supported in a BSS
1039252726Srpaulo * @bss: BSS table entry
1040252726Srpaulo * @rates: Buffer for returning a pointer to the rates list (units of 500 kbps)
1041252726Srpaulo * Returns: number of legacy TX rates or -1 on failure
1042252726Srpaulo *
1043252726Srpaulo * The caller is responsible for freeing the returned buffer with os_free() in
1044252726Srpaulo * case of success.
1045252726Srpaulo */
1046214501Srpauloint wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates)
1047214501Srpaulo{
1048214501Srpaulo	const u8 *ie, *ie2;
1049214501Srpaulo	int i, j;
1050214501Srpaulo	unsigned int len;
1051214501Srpaulo	u8 *r;
1052214501Srpaulo
1053214501Srpaulo	ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
1054214501Srpaulo	ie2 = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
1055214501Srpaulo
1056214501Srpaulo	len = (ie ? ie[1] : 0) + (ie2 ? ie2[1] : 0);
1057214501Srpaulo
1058214501Srpaulo	r = os_malloc(len);
1059214501Srpaulo	if (!r)
1060214501Srpaulo		return -1;
1061214501Srpaulo
1062214501Srpaulo	for (i = 0; ie && i < ie[1]; i++)
1063214501Srpaulo		r[i] = ie[i + 2] & 0x7f;
1064214501Srpaulo
1065214501Srpaulo	for (j = 0; ie2 && j < ie2[1]; j++)
1066214501Srpaulo		r[i + j] = ie2[j + 2] & 0x7f;
1067214501Srpaulo
1068214501Srpaulo	*rates = r;
1069214501Srpaulo	return len;
1070214501Srpaulo}
1071