blob: a3da86cae86a6e9d712993e43037925554f14a34 [file] [log] [blame]
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001/*
2 * BSS table
Hai Shalom74f70d42019-02-11 14:42:39 -08003 * Copyright (c) 2009-2019, Jouni Malinen <j@w1.fi>
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07004 *
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08005 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007 */
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"
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -070015#include "eap_peer/eap.h"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070016#include "wpa_supplicant_i.h"
17#include "config.h"
18#include "notify.h"
19#include "scan.h"
20#include "bss.h"
21
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -070022static void wpa_bss_set_hessid(struct wpa_bss *bss)
23{
24#ifdef CONFIG_INTERWORKING
25 const u8 *ie = wpa_bss_get_ie(bss, WLAN_EID_INTERWORKING);
26 if (ie == NULL || (ie[1] != 7 && ie[1] != 9)) {
27 os_memset(bss->hessid, 0, ETH_ALEN);
28 return;
29 }
30 if (ie[1] == 7)
31 os_memcpy(bss->hessid, ie + 3, ETH_ALEN);
32 else
33 os_memcpy(bss->hessid, ie + 5, ETH_ALEN);
34#endif /* CONFIG_INTERWORKING */
35}
36
37
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -080038/**
39 * wpa_bss_anqp_alloc - Allocate ANQP data structure for a BSS entry
40 * Returns: Allocated ANQP data structure or %NULL on failure
41 *
42 * The allocated ANQP data structure has its users count set to 1. It may be
43 * shared by multiple BSS entries and each shared entry is freed with
44 * wpa_bss_anqp_free().
45 */
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -070046struct wpa_bss_anqp * wpa_bss_anqp_alloc(void)
47{
48 struct wpa_bss_anqp *anqp;
49 anqp = os_zalloc(sizeof(*anqp));
50 if (anqp == NULL)
51 return NULL;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -080052#ifdef CONFIG_INTERWORKING
53 dl_list_init(&anqp->anqp_elems);
54#endif /* CONFIG_INTERWORKING */
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -070055 anqp->users = 1;
56 return anqp;
57}
58
59
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -080060/**
61 * wpa_bss_anqp_clone - Clone an ANQP data structure
62 * @anqp: ANQP data structure from wpa_bss_anqp_alloc()
63 * Returns: Cloned ANQP data structure or %NULL on failure
64 */
Dmitry Shmidtd5e49232012-12-03 15:08:10 -080065static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp)
66{
67 struct wpa_bss_anqp *n;
68
69 n = os_zalloc(sizeof(*n));
70 if (n == NULL)
71 return NULL;
72
73#define ANQP_DUP(f) if (anqp->f) n->f = wpabuf_dup(anqp->f)
74#ifdef CONFIG_INTERWORKING
Dmitry Shmidtd80a4012015-11-05 16:35:40 -080075 dl_list_init(&n->anqp_elems);
Dmitry Shmidt7f656022015-02-25 14:36:37 -080076 ANQP_DUP(capability_list);
Dmitry Shmidtd5e49232012-12-03 15:08:10 -080077 ANQP_DUP(venue_name);
78 ANQP_DUP(network_auth_type);
79 ANQP_DUP(roaming_consortium);
80 ANQP_DUP(ip_addr_type_availability);
81 ANQP_DUP(nai_realm);
82 ANQP_DUP(anqp_3gpp);
83 ANQP_DUP(domain_name);
Dmitry Shmidt29333592017-01-09 12:27:11 -080084 ANQP_DUP(fils_realm_info);
Dmitry Shmidtd5e49232012-12-03 15:08:10 -080085#endif /* CONFIG_INTERWORKING */
86#ifdef CONFIG_HS20
Dmitry Shmidt7f656022015-02-25 14:36:37 -080087 ANQP_DUP(hs20_capability_list);
Dmitry Shmidtd5e49232012-12-03 15:08:10 -080088 ANQP_DUP(hs20_operator_friendly_name);
89 ANQP_DUP(hs20_wan_metrics);
90 ANQP_DUP(hs20_connection_capability);
91 ANQP_DUP(hs20_operating_class);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -080092 ANQP_DUP(hs20_osu_providers_list);
Roshan Pius3a1667e2018-07-03 15:17:14 -070093 ANQP_DUP(hs20_operator_icon_metadata);
Hai Shalom39ba6fc2019-01-22 12:40:38 -080094 ANQP_DUP(hs20_osu_providers_nai_list);
Dmitry Shmidtd5e49232012-12-03 15:08:10 -080095#endif /* CONFIG_HS20 */
96#undef ANQP_DUP
97
98 return n;
99}
100
101
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800102/**
103 * wpa_bss_anqp_unshare_alloc - Unshare ANQP data (if shared) in a BSS entry
104 * @bss: BSS entry
105 * Returns: 0 on success, -1 on failure
106 *
107 * This function ensures the specific BSS entry has an ANQP data structure that
108 * is not shared with any other BSS entry.
109 */
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800110int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss)
111{
112 struct wpa_bss_anqp *anqp;
113
114 if (bss->anqp && bss->anqp->users > 1) {
115 /* allocated, but shared - clone an unshared copy */
116 anqp = wpa_bss_anqp_clone(bss->anqp);
117 if (anqp == NULL)
118 return -1;
119 anqp->users = 1;
120 bss->anqp->users--;
121 bss->anqp = anqp;
122 return 0;
123 }
124
125 if (bss->anqp)
126 return 0; /* already allocated and not shared */
127
128 /* not allocated - allocate a new storage area */
129 bss->anqp = wpa_bss_anqp_alloc();
130 return bss->anqp ? 0 : -1;
131}
132
133
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800134/**
135 * wpa_bss_anqp_free - Free an ANQP data structure
136 * @anqp: ANQP data structure from wpa_bss_anqp_alloc() or wpa_bss_anqp_clone()
137 */
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700138static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp)
139{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800140#ifdef CONFIG_INTERWORKING
141 struct wpa_bss_anqp_elem *elem;
142#endif /* CONFIG_INTERWORKING */
143
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700144 if (anqp == NULL)
145 return;
146
147 anqp->users--;
148 if (anqp->users > 0) {
149 /* Another BSS entry holds a pointer to this ANQP info */
150 return;
151 }
152
153#ifdef CONFIG_INTERWORKING
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800154 wpabuf_free(anqp->capability_list);
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700155 wpabuf_free(anqp->venue_name);
156 wpabuf_free(anqp->network_auth_type);
157 wpabuf_free(anqp->roaming_consortium);
158 wpabuf_free(anqp->ip_addr_type_availability);
159 wpabuf_free(anqp->nai_realm);
160 wpabuf_free(anqp->anqp_3gpp);
161 wpabuf_free(anqp->domain_name);
Dmitry Shmidt29333592017-01-09 12:27:11 -0800162 wpabuf_free(anqp->fils_realm_info);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800163
164 while ((elem = dl_list_first(&anqp->anqp_elems,
165 struct wpa_bss_anqp_elem, list))) {
166 dl_list_del(&elem->list);
167 wpabuf_free(elem->payload);
168 os_free(elem);
169 }
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700170#endif /* CONFIG_INTERWORKING */
171#ifdef CONFIG_HS20
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800172 wpabuf_free(anqp->hs20_capability_list);
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700173 wpabuf_free(anqp->hs20_operator_friendly_name);
174 wpabuf_free(anqp->hs20_wan_metrics);
175 wpabuf_free(anqp->hs20_connection_capability);
176 wpabuf_free(anqp->hs20_operating_class);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800177 wpabuf_free(anqp->hs20_osu_providers_list);
Roshan Pius3a1667e2018-07-03 15:17:14 -0700178 wpabuf_free(anqp->hs20_operator_icon_metadata);
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800179 wpabuf_free(anqp->hs20_osu_providers_nai_list);
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700180#endif /* CONFIG_HS20 */
181
182 os_free(anqp);
183}
184
185
Dmitry Shmidt2e425d62014-11-10 11:18:27 -0800186static void wpa_bss_update_pending_connect(struct wpa_supplicant *wpa_s,
187 struct wpa_bss *old_bss,
188 struct wpa_bss *new_bss)
189{
190 struct wpa_radio_work *work;
191 struct wpa_connect_work *cwork;
192
193 work = radio_work_pending(wpa_s, "sme-connect");
194 if (!work)
195 work = radio_work_pending(wpa_s, "connect");
196 if (!work)
197 return;
198
199 cwork = work->ctx;
200 if (cwork->bss != old_bss)
201 return;
202
203 wpa_printf(MSG_DEBUG,
204 "Update BSS pointer for the pending connect radio work");
205 cwork->bss = new_bss;
206 if (!new_bss)
207 cwork->bss_removed = 1;
208}
209
210
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -0800211void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
212 const char *reason)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700213{
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700214 if (wpa_s->last_scan_res) {
215 unsigned int i;
216 for (i = 0; i < wpa_s->last_scan_res_used; i++) {
217 if (wpa_s->last_scan_res[i] == bss) {
218 os_memmove(&wpa_s->last_scan_res[i],
219 &wpa_s->last_scan_res[i + 1],
220 (wpa_s->last_scan_res_used - i - 1)
221 * sizeof(struct wpa_bss *));
222 wpa_s->last_scan_res_used--;
223 break;
224 }
225 }
226 }
Dmitry Shmidt2e425d62014-11-10 11:18:27 -0800227 wpa_bss_update_pending_connect(wpa_s, bss, NULL);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700228 dl_list_del(&bss->list);
229 dl_list_del(&bss->list_id);
230 wpa_s->num_bss--;
231 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR
Dmitry Shmidt04949592012-07-19 12:16:46 -0700232 " SSID '%s' due to %s", bss->id, MAC2STR(bss->bssid),
233 wpa_ssid_txt(bss->ssid, bss->ssid_len), reason);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700234 wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id);
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700235 wpa_bss_anqp_free(bss->anqp);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700236 os_free(bss);
237}
238
239
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800240/**
241 * wpa_bss_get - Fetch a BSS table entry based on BSSID and SSID
242 * @wpa_s: Pointer to wpa_supplicant data
243 * @bssid: BSSID
244 * @ssid: SSID
245 * @ssid_len: Length of @ssid
246 * Returns: Pointer to the BSS entry or %NULL if not found
247 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700248struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
249 const u8 *ssid, size_t ssid_len)
250{
251 struct wpa_bss *bss;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700252 if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
253 return NULL;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700254 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
255 if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
256 bss->ssid_len == ssid_len &&
257 os_memcmp(bss->ssid, ssid, ssid_len) == 0)
258 return bss;
259 }
260 return NULL;
261}
262
263
Dmitry Shmidt29333592017-01-09 12:27:11 -0800264void calculate_update_time(const struct os_reltime *fetch_time,
265 unsigned int age_ms,
266 struct os_reltime *update_time)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700267{
268 os_time_t usec;
269
Dmitry Shmidt444d5672013-04-01 13:08:44 -0700270 update_time->sec = fetch_time->sec;
271 update_time->usec = fetch_time->usec;
272 update_time->sec -= age_ms / 1000;
273 usec = (age_ms % 1000) * 1000;
274 if (update_time->usec < usec) {
275 update_time->sec--;
276 update_time->usec += 1000000;
277 }
278 update_time->usec -= usec;
279}
280
281
282static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src,
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800283 struct os_reltime *fetch_time)
Dmitry Shmidt444d5672013-04-01 13:08:44 -0700284{
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700285 dst->flags = src->flags;
286 os_memcpy(dst->bssid, src->bssid, ETH_ALEN);
287 dst->freq = src->freq;
288 dst->beacon_int = src->beacon_int;
289 dst->caps = src->caps;
290 dst->qual = src->qual;
291 dst->noise = src->noise;
292 dst->level = src->level;
293 dst->tsf = src->tsf;
Sunil Ravia04bd252022-05-02 22:54:18 -0700294 dst->beacon_newer = src->beacon_newer;
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800295 dst->est_throughput = src->est_throughput;
296 dst->snr = src->snr;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700297
Dmitry Shmidt444d5672013-04-01 13:08:44 -0700298 calculate_update_time(fetch_time, src->age, &dst->last_update);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700299}
300
301
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -0700302static int wpa_bss_is_wps_candidate(struct wpa_supplicant *wpa_s,
303 struct wpa_bss *bss)
304{
305#ifdef CONFIG_WPS
306 struct wpa_ssid *ssid;
307 struct wpabuf *wps_ie;
308 int pbc = 0, ret;
309
310 wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
311 if (!wps_ie)
312 return 0;
313
314 if (wps_is_selected_pbc_registrar(wps_ie)) {
315 pbc = 1;
316 } else if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) {
317 wpabuf_free(wps_ie);
318 return 0;
319 }
320
321 for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
322 if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
323 continue;
324 if (ssid->ssid_len &&
325 (ssid->ssid_len != bss->ssid_len ||
326 os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) != 0))
327 continue;
328
329 if (pbc)
330 ret = eap_is_wps_pbc_enrollee(&ssid->eap);
331 else
332 ret = eap_is_wps_pin_enrollee(&ssid->eap);
333 wpabuf_free(wps_ie);
334 return ret;
335 }
336 wpabuf_free(wps_ie);
337#endif /* CONFIG_WPS */
338
339 return 0;
340}
341
342
Hai Shalom60840252021-02-19 19:02:11 -0800343static bool is_p2p_pending_bss(struct wpa_supplicant *wpa_s,
344 struct wpa_bss *bss)
345{
346#ifdef CONFIG_P2P
347 u8 addr[ETH_ALEN];
348
349 if (os_memcmp(bss->bssid, wpa_s->pending_join_iface_addr,
350 ETH_ALEN) == 0)
351 return true;
352 if (!is_zero_ether_addr(wpa_s->pending_join_dev_addr) &&
353 p2p_parse_dev_addr(wpa_bss_ie_ptr(bss), bss->ie_len, addr) == 0 &&
354 os_memcmp(addr, wpa_s->pending_join_dev_addr, ETH_ALEN) == 0)
355 return true;
356#endif /* CONFIG_P2P */
357 return false;
358}
359
360
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800361static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
362{
363 struct wpa_ssid *ssid;
364
Hai Shalom60840252021-02-19 19:02:11 -0800365 if (is_p2p_pending_bss(wpa_s, bss))
366 return 1;
367
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800368 for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
369 if (ssid->ssid == NULL || ssid->ssid_len == 0)
370 continue;
371 if (ssid->ssid_len == bss->ssid_len &&
372 os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) == 0)
373 return 1;
374 }
375
376 return 0;
377}
378
379
Dmitry Shmidt04949592012-07-19 12:16:46 -0700380static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
381{
Sunil Ravi89eba102022-09-13 21:04:37 -0700382 int i;
383
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800384 if (bss == wpa_s->current_bss)
385 return 1;
386
387 if (wpa_s->current_bss &&
388 (bss->ssid_len != wpa_s->current_bss->ssid_len ||
389 os_memcmp(bss->ssid, wpa_s->current_bss->ssid,
390 bss->ssid_len) != 0))
391 return 0; /* SSID has changed */
392
Sunil Ravi89eba102022-09-13 21:04:37 -0700393 if (!is_zero_ether_addr(bss->bssid) &&
394 (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
395 os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0))
396 return 1;
397
398 if (!wpa_s->valid_links)
399 return 0;
400
401 for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
402 if (!(wpa_s->valid_links & BIT(i)))
403 continue;
404
405 if (os_memcmp(bss->bssid, wpa_s->links[i].bssid, ETH_ALEN) == 0)
406 return 1;
407 }
408
409 return 0;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700410}
411
412
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800413static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s)
414{
415 struct wpa_bss *bss;
416
417 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -0700418 if (!wpa_bss_known(wpa_s, bss) &&
419 !wpa_bss_is_wps_candidate(wpa_s, bss)) {
Dmitry Shmidt04949592012-07-19 12:16:46 -0700420 wpa_bss_remove(wpa_s, bss, __func__);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800421 return 0;
422 }
423 }
424
425 return -1;
426}
427
428
Dmitry Shmidt04949592012-07-19 12:16:46 -0700429static int wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s)
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800430{
Dmitry Shmidt04949592012-07-19 12:16:46 -0700431 struct wpa_bss *bss;
432
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800433 /*
434 * Remove the oldest entry that does not match with any configured
435 * network.
436 */
437 if (wpa_bss_remove_oldest_unknown(wpa_s) == 0)
Dmitry Shmidt04949592012-07-19 12:16:46 -0700438 return 0;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800439
440 /*
Dmitry Shmidt04949592012-07-19 12:16:46 -0700441 * Remove the oldest entry that isn't currently in use.
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800442 */
Dmitry Shmidt04949592012-07-19 12:16:46 -0700443 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
444 if (!wpa_bss_in_use(wpa_s, bss)) {
445 wpa_bss_remove(wpa_s, bss, __func__);
446 return 0;
447 }
448 }
449
450 return -1;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800451}
452
453
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700454static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
455 const u8 *ssid, size_t ssid_len,
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800456 struct wpa_scan_res *res,
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800457 struct os_reltime *fetch_time)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700458{
459 struct wpa_bss *bss;
Sunil Ravi89eba102022-09-13 21:04:37 -0700460 char extra[100];
461 const u8 *ml_ie;
462 char *pos, *end;
463 int ret = 0;
464 const u8 *mld_addr;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700465
466 bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len);
467 if (bss == NULL)
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700468 return NULL;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700469 bss->id = wpa_s->bss_next_id++;
470 bss->last_update_idx = wpa_s->bss_update_idx;
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800471 wpa_bss_copy_res(bss, res, fetch_time);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700472 os_memcpy(bss->ssid, ssid, ssid_len);
473 bss->ssid_len = ssid_len;
474 bss->ie_len = res->ie_len;
475 bss->beacon_ie_len = res->beacon_ie_len;
Hai Shalom60840252021-02-19 19:02:11 -0800476 os_memcpy(bss->ies, res + 1, res->ie_len + res->beacon_ie_len);
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700477 wpa_bss_set_hessid(bss);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700478
Sunil Ravi89eba102022-09-13 21:04:37 -0700479 os_memset(bss->mld_addr, 0, ETH_ALEN);
480 ml_ie = wpa_scan_get_ml_ie(res, MULTI_LINK_CONTROL_TYPE_BASIC);
481 if (ml_ie) {
482 mld_addr = get_basic_mle_mld_addr(&ml_ie[3], ml_ie[1] - 1);
483 if (mld_addr)
484 os_memcpy(bss->mld_addr, mld_addr, ETH_ALEN);
485 }
486
Jouni Malinen7a6c8302013-09-27 15:47:09 +0300487 if (wpa_s->num_bss + 1 > wpa_s->conf->bss_max_count &&
488 wpa_bss_remove_oldest(wpa_s) != 0) {
489 wpa_printf(MSG_ERROR, "Increasing the MAX BSS count to %d "
490 "because all BSSes are in use. We should normally "
491 "not get here!", (int) wpa_s->num_bss + 1);
492 wpa_s->conf->bss_max_count = wpa_s->num_bss + 1;
493 }
494
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700495 dl_list_add_tail(&wpa_s->bss, &bss->list);
496 dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
497 wpa_s->num_bss++;
Sunil Ravi89eba102022-09-13 21:04:37 -0700498
499 extra[0] = '\0';
500 pos = extra;
501 end = pos + sizeof(extra);
Hai Shalom81f62d82019-07-22 12:10:00 -0700502 if (!is_zero_ether_addr(bss->hessid))
Sunil Ravi89eba102022-09-13 21:04:37 -0700503 ret = os_snprintf(pos, end - pos, " HESSID " MACSTR,
504 MAC2STR(bss->hessid));
505
506 if (!is_zero_ether_addr(bss->mld_addr) &&
507 !os_snprintf_error(end - pos, ret)) {
508 pos += ret;
509 ret = os_snprintf(pos, end - pos, " MLD ADDR " MACSTR,
510 MAC2STR(bss->mld_addr));
511 }
512
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700513 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR
Hai Shalom81f62d82019-07-22 12:10:00 -0700514 " SSID '%s' freq %d%s",
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800515 bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len),
Hai Shalom81f62d82019-07-22 12:10:00 -0700516 bss->freq, extra);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700517 wpas_notify_bss_added(wpa_s, bss->bssid, bss->id);
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700518 return bss;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700519}
520
521
522static int are_ies_equal(const struct wpa_bss *old,
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700523 const struct wpa_scan_res *new_res, u32 ie)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700524{
525 const u8 *old_ie, *new_ie;
526 struct wpabuf *old_ie_buff = NULL;
527 struct wpabuf *new_ie_buff = NULL;
528 int new_ie_len, old_ie_len, ret, is_multi;
529
530 switch (ie) {
531 case WPA_IE_VENDOR_TYPE:
532 old_ie = wpa_bss_get_vendor_ie(old, ie);
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700533 new_ie = wpa_scan_get_vendor_ie(new_res, ie);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700534 is_multi = 0;
535 break;
536 case WPS_IE_VENDOR_TYPE:
537 old_ie_buff = wpa_bss_get_vendor_ie_multi(old, ie);
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700538 new_ie_buff = wpa_scan_get_vendor_ie_multi(new_res, ie);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700539 is_multi = 1;
540 break;
541 case WLAN_EID_RSN:
542 case WLAN_EID_SUPP_RATES:
543 case WLAN_EID_EXT_SUPP_RATES:
544 old_ie = wpa_bss_get_ie(old, ie);
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700545 new_ie = wpa_scan_get_ie(new_res, ie);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700546 is_multi = 0;
547 break;
548 default:
549 wpa_printf(MSG_DEBUG, "bss: %s: cannot compare IEs", __func__);
550 return 0;
551 }
552
553 if (is_multi) {
554 /* in case of multiple IEs stored in buffer */
555 old_ie = old_ie_buff ? wpabuf_head_u8(old_ie_buff) : NULL;
556 new_ie = new_ie_buff ? wpabuf_head_u8(new_ie_buff) : NULL;
557 old_ie_len = old_ie_buff ? wpabuf_len(old_ie_buff) : 0;
558 new_ie_len = new_ie_buff ? wpabuf_len(new_ie_buff) : 0;
559 } else {
560 /* in case of single IE */
561 old_ie_len = old_ie ? old_ie[1] + 2 : 0;
562 new_ie_len = new_ie ? new_ie[1] + 2 : 0;
563 }
564
565 if (!old_ie || !new_ie)
566 ret = !old_ie && !new_ie;
567 else
568 ret = (old_ie_len == new_ie_len &&
569 os_memcmp(old_ie, new_ie, old_ie_len) == 0);
570
571 wpabuf_free(old_ie_buff);
572 wpabuf_free(new_ie_buff);
573
574 return ret;
575}
576
577
578static u32 wpa_bss_compare_res(const struct wpa_bss *old,
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700579 const struct wpa_scan_res *new_res)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700580{
581 u32 changes = 0;
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700582 int caps_diff = old->caps ^ new_res->caps;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700583
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700584 if (old->freq != new_res->freq)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700585 changes |= WPA_BSS_FREQ_CHANGED_FLAG;
586
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700587 if (old->level != new_res->level)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700588 changes |= WPA_BSS_SIGNAL_CHANGED_FLAG;
589
590 if (caps_diff & IEEE80211_CAP_PRIVACY)
591 changes |= WPA_BSS_PRIVACY_CHANGED_FLAG;
592
593 if (caps_diff & IEEE80211_CAP_IBSS)
594 changes |= WPA_BSS_MODE_CHANGED_FLAG;
595
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700596 if (old->ie_len == new_res->ie_len &&
Hai Shalom60840252021-02-19 19:02:11 -0800597 os_memcmp(wpa_bss_ie_ptr(old), new_res + 1, old->ie_len) == 0)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700598 return changes;
599 changes |= WPA_BSS_IES_CHANGED_FLAG;
600
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700601 if (!are_ies_equal(old, new_res, WPA_IE_VENDOR_TYPE))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700602 changes |= WPA_BSS_WPAIE_CHANGED_FLAG;
603
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700604 if (!are_ies_equal(old, new_res, WLAN_EID_RSN))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700605 changes |= WPA_BSS_RSNIE_CHANGED_FLAG;
606
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700607 if (!are_ies_equal(old, new_res, WPS_IE_VENDOR_TYPE))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700608 changes |= WPA_BSS_WPS_CHANGED_FLAG;
609
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700610 if (!are_ies_equal(old, new_res, WLAN_EID_SUPP_RATES) ||
611 !are_ies_equal(old, new_res, WLAN_EID_EXT_SUPP_RATES))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700612 changes |= WPA_BSS_RATES_CHANGED_FLAG;
613
614 return changes;
615}
616
617
Hai Shalom60840252021-02-19 19:02:11 -0800618void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes,
619 const struct wpa_bss *bss)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700620{
621 if (changes & WPA_BSS_FREQ_CHANGED_FLAG)
622 wpas_notify_bss_freq_changed(wpa_s, bss->id);
623
624 if (changes & WPA_BSS_SIGNAL_CHANGED_FLAG)
625 wpas_notify_bss_signal_changed(wpa_s, bss->id);
626
627 if (changes & WPA_BSS_PRIVACY_CHANGED_FLAG)
628 wpas_notify_bss_privacy_changed(wpa_s, bss->id);
629
630 if (changes & WPA_BSS_MODE_CHANGED_FLAG)
631 wpas_notify_bss_mode_changed(wpa_s, bss->id);
632
633 if (changes & WPA_BSS_WPAIE_CHANGED_FLAG)
634 wpas_notify_bss_wpaie_changed(wpa_s, bss->id);
635
636 if (changes & WPA_BSS_RSNIE_CHANGED_FLAG)
637 wpas_notify_bss_rsnie_changed(wpa_s, bss->id);
638
639 if (changes & WPA_BSS_WPS_CHANGED_FLAG)
640 wpas_notify_bss_wps_changed(wpa_s, bss->id);
641
642 if (changes & WPA_BSS_IES_CHANGED_FLAG)
643 wpas_notify_bss_ies_changed(wpa_s, bss->id);
644
645 if (changes & WPA_BSS_RATES_CHANGED_FLAG)
646 wpas_notify_bss_rates_changed(wpa_s, bss->id);
Dmitry Shmidt661b4f72014-09-29 14:58:27 -0700647
648 wpas_notify_bss_seen(wpa_s, bss->id);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700649}
650
651
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700652static struct wpa_bss *
653wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800654 struct wpa_scan_res *res, struct os_reltime *fetch_time)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700655{
656 u32 changes;
657
Dmitry Shmidtabb90a32016-12-05 15:34:39 -0800658 if (bss->last_update_idx == wpa_s->bss_update_idx) {
659 struct os_reltime update_time;
660
661 /*
662 * Some drivers (e.g., cfg80211) include multiple BSS entries
663 * for the same BSS if that BSS's channel changes. The BSS list
664 * implementation in wpa_supplicant does not do that and we need
665 * to filter out the obsolete results here to make sure only the
666 * most current BSS information remains in the table.
667 */
668 wpa_printf(MSG_DEBUG, "BSS: " MACSTR
669 " has multiple entries in the scan results - select the most current one",
670 MAC2STR(bss->bssid));
671 calculate_update_time(fetch_time, res->age, &update_time);
672 wpa_printf(MSG_DEBUG,
673 "Previous last_update: %u.%06u (freq %d%s)",
674 (unsigned int) bss->last_update.sec,
675 (unsigned int) bss->last_update.usec,
676 bss->freq,
677 (bss->flags & WPA_BSS_ASSOCIATED) ? " assoc" : "");
678 wpa_printf(MSG_DEBUG, "New last_update: %u.%06u (freq %d%s)",
679 (unsigned int) update_time.sec,
680 (unsigned int) update_time.usec,
681 res->freq,
682 (res->flags & WPA_SCAN_ASSOCIATED) ? " assoc" : "");
683 if ((bss->flags & WPA_BSS_ASSOCIATED) ||
684 (!(res->flags & WPA_SCAN_ASSOCIATED) &&
685 !os_reltime_before(&bss->last_update, &update_time))) {
686 wpa_printf(MSG_DEBUG,
687 "Ignore this BSS entry since the previous update looks more current");
688 return bss;
689 }
690 wpa_printf(MSG_DEBUG,
691 "Accept this BSS entry since it looks more current than the previous update");
692 }
693
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700694 changes = wpa_bss_compare_res(bss, res);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800695 if (changes & WPA_BSS_FREQ_CHANGED_FLAG)
696 wpa_printf(MSG_DEBUG, "BSS: " MACSTR " changed freq %d --> %d",
697 MAC2STR(bss->bssid), bss->freq, res->freq);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700698 bss->scan_miss_count = 0;
699 bss->last_update_idx = wpa_s->bss_update_idx;
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800700 wpa_bss_copy_res(bss, res, fetch_time);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700701 /* Move the entry to the end of the list */
702 dl_list_del(&bss->list);
Dmitry Shmidt96571392013-10-14 12:54:46 -0700703#ifdef CONFIG_P2P
704 if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
705 !wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE)) {
706 /*
707 * This can happen when non-P2P station interface runs a scan
708 * without P2P IE in the Probe Request frame. P2P GO would reply
709 * to that with a Probe Response that does not include P2P IE.
710 * Do not update the IEs in this BSS entry to avoid such loss of
711 * information that may be needed for P2P operations to
712 * determine group information.
713 */
714 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Do not update scan IEs for "
715 MACSTR " since that would remove P2P IE information",
716 MAC2STR(bss->bssid));
717 } else
718#endif /* CONFIG_P2P */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700719 if (bss->ie_len + bss->beacon_ie_len >=
720 res->ie_len + res->beacon_ie_len) {
Hai Shalom60840252021-02-19 19:02:11 -0800721 os_memcpy(bss->ies, res + 1, res->ie_len + res->beacon_ie_len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700722 bss->ie_len = res->ie_len;
723 bss->beacon_ie_len = res->beacon_ie_len;
724 } else {
725 struct wpa_bss *nbss;
726 struct dl_list *prev = bss->list_id.prev;
727 dl_list_del(&bss->list_id);
728 nbss = os_realloc(bss, sizeof(*bss) + res->ie_len +
729 res->beacon_ie_len);
730 if (nbss) {
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700731 unsigned int i;
732 for (i = 0; i < wpa_s->last_scan_res_used; i++) {
733 if (wpa_s->last_scan_res[i] == bss) {
734 wpa_s->last_scan_res[i] = nbss;
735 break;
736 }
737 }
Dmitry Shmidt04949592012-07-19 12:16:46 -0700738 if (wpa_s->current_bss == bss)
739 wpa_s->current_bss = nbss;
Dmitry Shmidt2e425d62014-11-10 11:18:27 -0800740 wpa_bss_update_pending_connect(wpa_s, bss, nbss);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700741 bss = nbss;
Hai Shalom60840252021-02-19 19:02:11 -0800742 os_memcpy(bss->ies, res + 1,
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700743 res->ie_len + res->beacon_ie_len);
744 bss->ie_len = res->ie_len;
745 bss->beacon_ie_len = res->beacon_ie_len;
746 }
747 dl_list_add(prev, &bss->list_id);
748 }
Sunil Ravi89eba102022-09-13 21:04:37 -0700749 if (changes & WPA_BSS_IES_CHANGED_FLAG) {
750 const u8 *ml_ie, *mld_addr;
751
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700752 wpa_bss_set_hessid(bss);
Sunil Ravi89eba102022-09-13 21:04:37 -0700753 os_memset(bss->mld_addr, 0, ETH_ALEN);
754 ml_ie = wpa_scan_get_ml_ie(res, MULTI_LINK_CONTROL_TYPE_BASIC);
755 if (ml_ie) {
756 mld_addr = get_basic_mle_mld_addr(&ml_ie[3],
757 ml_ie[1] - 1);
758 if (mld_addr)
759 os_memcpy(bss->mld_addr, mld_addr, ETH_ALEN);
760 }
761 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700762 dl_list_add_tail(&wpa_s->bss, &bss->list);
763
764 notify_bss_changes(wpa_s, changes, bss);
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700765
766 return bss;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700767}
768
769
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800770/**
771 * wpa_bss_update_start - Start a BSS table update from scan results
772 * @wpa_s: Pointer to wpa_supplicant data
773 *
774 * This function is called at the start of each BSS table update round for new
775 * scan results. The actual scan result entries are indicated with calls to
776 * wpa_bss_update_scan_res() and the update round is finished with a call to
777 * wpa_bss_update_end().
778 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700779void wpa_bss_update_start(struct wpa_supplicant *wpa_s)
780{
781 wpa_s->bss_update_idx++;
782 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u",
783 wpa_s->bss_update_idx);
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700784 wpa_s->last_scan_res_used = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700785}
786
787
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800788/**
789 * wpa_bss_update_scan_res - Update a BSS table entry based on a scan result
790 * @wpa_s: Pointer to wpa_supplicant data
791 * @res: Scan result
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800792 * @fetch_time: Time when the result was fetched from the driver
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800793 *
794 * This function updates a BSS table entry (or adds one) based on a scan result.
795 * This is called separately for each scan result between the calls to
796 * wpa_bss_update_start() and wpa_bss_update_end().
797 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700798void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800799 struct wpa_scan_res *res,
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800800 struct os_reltime *fetch_time)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700801{
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800802 const u8 *ssid, *p2p, *mesh;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700803 struct wpa_bss *bss;
804
Dmitry Shmidt444d5672013-04-01 13:08:44 -0700805 if (wpa_s->conf->ignore_old_scan_res) {
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800806 struct os_reltime update;
Dmitry Shmidt444d5672013-04-01 13:08:44 -0700807 calculate_update_time(fetch_time, res->age, &update);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800808 if (os_reltime_before(&update, &wpa_s->scan_trigger_time)) {
809 struct os_reltime age;
810 os_reltime_sub(&wpa_s->scan_trigger_time, &update,
811 &age);
Dmitry Shmidt444d5672013-04-01 13:08:44 -0700812 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Ignore driver BSS "
813 "table entry that is %u.%06u seconds older "
814 "than our scan trigger",
815 (unsigned int) age.sec,
816 (unsigned int) age.usec);
817 return;
818 }
819 }
820
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700821 ssid = wpa_scan_get_ie(res, WLAN_EID_SSID);
822 if (ssid == NULL) {
823 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for "
824 MACSTR, MAC2STR(res->bssid));
825 return;
826 }
Dmitry Shmidt9d9e6022015-04-23 10:34:55 -0700827 if (ssid[1] > SSID_MAX_LEN) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700828 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for "
829 MACSTR, MAC2STR(res->bssid));
830 return;
831 }
832
833 p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700834#ifdef CONFIG_P2P
835 if (p2p == NULL &&
836 wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
837 /*
838 * If it's a P2P specific interface, then don't update
839 * the scan result without a P2P IE.
840 */
841 wpa_printf(MSG_DEBUG, "BSS: No P2P IE - skipping BSS " MACSTR
842 " update for P2P interface", MAC2STR(res->bssid));
843 return;
844 }
845#endif /* CONFIG_P2P */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700846 if (p2p && ssid[1] == P2P_WILDCARD_SSID_LEN &&
847 os_memcmp(ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0)
848 return; /* Skip P2P listen discovery results here */
849
850 /* TODO: add option for ignoring BSSes we are not interested in
851 * (to save memory) */
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800852
853 mesh = wpa_scan_get_ie(res, WLAN_EID_MESH_ID);
Dmitry Shmidt9d9e6022015-04-23 10:34:55 -0700854 if (mesh && mesh[1] <= SSID_MAX_LEN)
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800855 ssid = mesh;
856
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700857 bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
858 if (bss == NULL)
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800859 bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res, fetch_time);
Dmitry Shmidt56052862013-10-04 10:23:25 -0700860 else {
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800861 bss = wpa_bss_update(wpa_s, bss, res, fetch_time);
Dmitry Shmidt56052862013-10-04 10:23:25 -0700862 if (wpa_s->last_scan_res) {
863 unsigned int i;
864 for (i = 0; i < wpa_s->last_scan_res_used; i++) {
865 if (bss == wpa_s->last_scan_res[i]) {
866 /* Already in the list */
867 return;
868 }
869 }
870 }
871 }
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700872
873 if (bss == NULL)
874 return;
875 if (wpa_s->last_scan_res_used >= wpa_s->last_scan_res_size) {
876 struct wpa_bss **n;
877 unsigned int siz;
878 if (wpa_s->last_scan_res_size == 0)
879 siz = 32;
880 else
881 siz = wpa_s->last_scan_res_size * 2;
882 n = os_realloc_array(wpa_s->last_scan_res, siz,
883 sizeof(struct wpa_bss *));
884 if (n == NULL)
885 return;
886 wpa_s->last_scan_res = n;
887 wpa_s->last_scan_res_size = siz;
888 }
889
Dmitry Shmidt7832adb2014-04-29 10:53:02 -0700890 if (wpa_s->last_scan_res)
891 wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700892}
893
894
895static int wpa_bss_included_in_scan(const struct wpa_bss *bss,
896 const struct scan_info *info)
897{
898 int found;
899 size_t i;
900
901 if (info == NULL)
902 return 1;
903
904 if (info->num_freqs) {
905 found = 0;
906 for (i = 0; i < info->num_freqs; i++) {
907 if (bss->freq == info->freqs[i]) {
908 found = 1;
909 break;
910 }
911 }
912 if (!found)
913 return 0;
914 }
915
916 if (info->num_ssids) {
917 found = 0;
918 for (i = 0; i < info->num_ssids; i++) {
919 const struct wpa_driver_scan_ssid *s = &info->ssids[i];
920 if ((s->ssid == NULL || s->ssid_len == 0) ||
921 (s->ssid_len == bss->ssid_len &&
922 os_memcmp(s->ssid, bss->ssid, bss->ssid_len) ==
923 0)) {
924 found = 1;
925 break;
926 }
927 }
928 if (!found)
929 return 0;
930 }
931
932 return 1;
933}
934
935
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800936/**
937 * wpa_bss_update_end - End a BSS table update from scan results
938 * @wpa_s: Pointer to wpa_supplicant data
939 * @info: Information about scan parameters
940 * @new_scan: Whether this update round was based on a new scan
941 *
942 * This function is called at the end of each BSS table update round for new
943 * scan results. The start of the update was indicated with a call to
944 * wpa_bss_update_start().
945 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700946void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
947 int new_scan)
948{
949 struct wpa_bss *bss, *n;
950
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800951 os_get_reltime(&wpa_s->last_scan);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800952 if ((info && info->aborted) || !new_scan)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700953 return; /* do not expire entries without new scan */
954
955 dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
956 if (wpa_bss_in_use(wpa_s, bss))
957 continue;
958 if (!wpa_bss_included_in_scan(bss, info))
959 continue; /* expire only BSSes that were scanned */
960 if (bss->last_update_idx < wpa_s->bss_update_idx)
961 bss->scan_miss_count++;
962 if (bss->scan_miss_count >=
963 wpa_s->conf->bss_expiration_scan_count) {
Dmitry Shmidt04949592012-07-19 12:16:46 -0700964 wpa_bss_remove(wpa_s, bss, "no match in scan");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700965 }
966 }
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700967
Hai Shalomfdcde762020-04-02 11:19:20 -0700968 wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%zu/%zu",
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800969 wpa_s->last_scan_res_used, wpa_s->last_scan_res_size);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700970}
971
972
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800973/**
974 * wpa_bss_flush_by_age - Flush old BSS entries
975 * @wpa_s: Pointer to wpa_supplicant data
976 * @age: Maximum entry age in seconds
977 *
978 * Remove BSS entries that have not been updated during the last @age seconds.
979 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700980void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age)
981{
982 struct wpa_bss *bss, *n;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800983 struct os_reltime t;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700984
985 if (dl_list_empty(&wpa_s->bss))
986 return;
987
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800988 os_get_reltime(&t);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700989 t.sec -= age;
990
991 dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
992 if (wpa_bss_in_use(wpa_s, bss))
993 continue;
994
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800995 if (os_reltime_before(&bss->last_update, &t)) {
Dmitry Shmidt04949592012-07-19 12:16:46 -0700996 wpa_bss_remove(wpa_s, bss, __func__);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700997 } else
998 break;
999 }
1000}
1001
1002
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001003/**
1004 * wpa_bss_init - Initialize BSS table
1005 * @wpa_s: Pointer to wpa_supplicant data
1006 * Returns: 0 on success, -1 on failure
1007 *
1008 * This prepares BSS table lists and timer for periodic updates. The BSS table
1009 * is deinitialized with wpa_bss_deinit() once not needed anymore.
1010 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001011int wpa_bss_init(struct wpa_supplicant *wpa_s)
1012{
1013 dl_list_init(&wpa_s->bss);
1014 dl_list_init(&wpa_s->bss_id);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001015 return 0;
1016}
1017
1018
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001019/**
1020 * wpa_bss_flush - Flush all unused BSS entries
1021 * @wpa_s: Pointer to wpa_supplicant data
1022 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001023void wpa_bss_flush(struct wpa_supplicant *wpa_s)
1024{
1025 struct wpa_bss *bss, *n;
1026
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001027 wpa_s->clear_driver_scan_cache = 1;
1028
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001029 if (wpa_s->bss.next == NULL)
1030 return; /* BSS table not yet initialized */
1031
1032 dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
1033 if (wpa_bss_in_use(wpa_s, bss))
1034 continue;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001035 wpa_bss_remove(wpa_s, bss, __func__);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001036 }
1037}
1038
1039
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001040/**
1041 * wpa_bss_deinit - Deinitialize BSS table
1042 * @wpa_s: Pointer to wpa_supplicant data
1043 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001044void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
1045{
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001046 wpa_bss_flush(wpa_s);
1047}
1048
1049
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001050/**
1051 * wpa_bss_get_bssid - Fetch a BSS table entry based on BSSID
1052 * @wpa_s: Pointer to wpa_supplicant data
1053 * @bssid: BSSID
1054 * Returns: Pointer to the BSS entry or %NULL if not found
1055 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001056struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
1057 const u8 *bssid)
1058{
1059 struct wpa_bss *bss;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001060 if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
1061 return NULL;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001062 dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
1063 if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
1064 return bss;
1065 }
1066 return NULL;
1067}
1068
1069
Dmitry Shmidt444d5672013-04-01 13:08:44 -07001070/**
1071 * wpa_bss_get_bssid_latest - Fetch the latest BSS table entry based on BSSID
1072 * @wpa_s: Pointer to wpa_supplicant data
1073 * @bssid: BSSID
1074 * Returns: Pointer to the BSS entry or %NULL if not found
1075 *
1076 * This function is like wpa_bss_get_bssid(), but full BSS table is iterated to
1077 * find the entry that has the most recent update. This can help in finding the
1078 * correct entry in cases where the SSID of the AP may have changed recently
1079 * (e.g., in WPS reconfiguration cases).
1080 */
1081struct wpa_bss * wpa_bss_get_bssid_latest(struct wpa_supplicant *wpa_s,
1082 const u8 *bssid)
1083{
1084 struct wpa_bss *bss, *found = NULL;
1085 if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
1086 return NULL;
1087 dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
1088 if (os_memcmp(bss->bssid, bssid, ETH_ALEN) != 0)
1089 continue;
1090 if (found == NULL ||
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001091 os_reltime_before(&found->last_update, &bss->last_update))
Dmitry Shmidt444d5672013-04-01 13:08:44 -07001092 found = bss;
1093 }
1094 return found;
1095}
1096
1097
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08001098#ifdef CONFIG_P2P
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001099/**
Hai Shalomc3565922019-10-28 11:58:20 -07001100 * wpa_bss_get_p2p_dev_addr - Fetch the latest BSS table entry based on P2P Device Addr
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001101 * @wpa_s: Pointer to wpa_supplicant data
1102 * @dev_addr: P2P Device Address of the GO
1103 * Returns: Pointer to the BSS entry or %NULL if not found
Hai Shalomc3565922019-10-28 11:58:20 -07001104 *
1105 * This function tries to find the entry that has the most recent update. This
1106 * can help in finding the correct entry in cases where the SSID of the P2P
1107 * Device may have changed recently.
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001108 */
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08001109struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s,
1110 const u8 *dev_addr)
1111{
Hai Shalomc3565922019-10-28 11:58:20 -07001112 struct wpa_bss *bss, *found = NULL;
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08001113 dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
1114 u8 addr[ETH_ALEN];
Hai Shalom60840252021-02-19 19:02:11 -08001115 if (p2p_parse_dev_addr(wpa_bss_ie_ptr(bss), bss->ie_len,
Hai Shalomc3565922019-10-28 11:58:20 -07001116 addr) != 0 ||
1117 os_memcmp(addr, dev_addr, ETH_ALEN) != 0)
1118 continue;
1119 if (!found ||
1120 os_reltime_before(&found->last_update, &bss->last_update))
1121 found = bss;
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08001122 }
Hai Shalomc3565922019-10-28 11:58:20 -07001123 return found;
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08001124}
1125#endif /* CONFIG_P2P */
1126
1127
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001128/**
1129 * wpa_bss_get_id - Fetch a BSS table entry based on identifier
1130 * @wpa_s: Pointer to wpa_supplicant data
1131 * @id: Unique identifier (struct wpa_bss::id) assigned for the entry
1132 * Returns: Pointer to the BSS entry or %NULL if not found
1133 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001134struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
1135{
1136 struct wpa_bss *bss;
1137 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
1138 if (bss->id == id)
1139 return bss;
1140 }
1141 return NULL;
1142}
1143
1144
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001145/**
Dmitry Shmidtf8623282013-02-20 14:34:59 -08001146 * wpa_bss_get_id_range - Fetch a BSS table entry based on identifier range
1147 * @wpa_s: Pointer to wpa_supplicant data
1148 * @idf: Smallest allowed identifier assigned for the entry
1149 * @idf: Largest allowed identifier assigned for the entry
1150 * Returns: Pointer to the BSS entry or %NULL if not found
1151 *
1152 * This function is similar to wpa_bss_get_id() but allows a BSS entry with the
1153 * smallest id value to be fetched within the specified range without the
1154 * caller having to know the exact id.
1155 */
1156struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s,
1157 unsigned int idf, unsigned int idl)
1158{
1159 struct wpa_bss *bss;
1160 dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
1161 if (bss->id >= idf && bss->id <= idl)
1162 return bss;
1163 }
1164 return NULL;
1165}
1166
1167
1168/**
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001169 * wpa_bss_get_ie - Fetch a specified information element from a BSS entry
1170 * @bss: BSS table entry
1171 * @ie: Information element identitifier (WLAN_EID_*)
1172 * Returns: Pointer to the information element (id field) or %NULL if not found
1173 *
1174 * This function returns the first matching information element in the BSS
1175 * entry.
1176 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001177const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie)
1178{
Hai Shalom60840252021-02-19 19:02:11 -08001179 return get_ie(wpa_bss_ie_ptr(bss), bss->ie_len, ie);
1180}
1181
1182
1183/**
1184 * wpa_bss_get_ie_ext - Fetch a specified extended IE from a BSS entry
1185 * @bss: BSS table entry
1186 * @ext: Information element extension identifier (WLAN_EID_EXT_*)
1187 * Returns: Pointer to the information element (id field) or %NULL if not found
1188 *
1189 * This function returns the first matching information element in the BSS
1190 * entry.
1191 */
1192const u8 * wpa_bss_get_ie_ext(const struct wpa_bss *bss, u8 ext)
1193{
1194 return get_ie_ext(wpa_bss_ie_ptr(bss), bss->ie_len, ext);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001195}
1196
1197
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001198/**
1199 * wpa_bss_get_vendor_ie - Fetch a vendor information element from a BSS entry
1200 * @bss: BSS table entry
1201 * @vendor_type: Vendor type (four octets starting the IE payload)
1202 * Returns: Pointer to the information element (id field) or %NULL if not found
1203 *
1204 * This function returns the first matching information element in the BSS
1205 * entry.
1206 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001207const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type)
1208{
Hai Shalom60840252021-02-19 19:02:11 -08001209 const u8 *ies;
1210 const struct element *elem;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001211
Hai Shalom60840252021-02-19 19:02:11 -08001212 ies = wpa_bss_ie_ptr(bss);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001213
Hai Shalom60840252021-02-19 19:02:11 -08001214 for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, bss->ie_len) {
1215 if (elem->datalen >= 4 &&
1216 vendor_type == WPA_GET_BE32(elem->data))
1217 return &elem->id;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001218 }
1219
1220 return NULL;
1221}
1222
1223
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001224/**
Dmitry Shmidt96571392013-10-14 12:54:46 -07001225 * wpa_bss_get_vendor_ie_beacon - Fetch a vendor information from a BSS entry
1226 * @bss: BSS table entry
1227 * @vendor_type: Vendor type (four octets starting the IE payload)
1228 * Returns: Pointer to the information element (id field) or %NULL if not found
1229 *
1230 * This function returns the first matching information element in the BSS
1231 * entry.
1232 *
1233 * This function is like wpa_bss_get_vendor_ie(), but uses IE buffer only
1234 * from Beacon frames instead of either Beacon or Probe Response frames.
1235 */
1236const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss,
1237 u32 vendor_type)
1238{
Hai Shalom60840252021-02-19 19:02:11 -08001239 const u8 *ies;
1240 const struct element *elem;
Dmitry Shmidt96571392013-10-14 12:54:46 -07001241
1242 if (bss->beacon_ie_len == 0)
1243 return NULL;
1244
Hai Shalom60840252021-02-19 19:02:11 -08001245 ies = wpa_bss_ie_ptr(bss);
1246 ies += bss->ie_len;
Dmitry Shmidt96571392013-10-14 12:54:46 -07001247
Hai Shalom60840252021-02-19 19:02:11 -08001248 for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies,
1249 bss->beacon_ie_len) {
1250 if (elem->datalen >= 4 &&
1251 vendor_type == WPA_GET_BE32(elem->data))
1252 return &elem->id;
Dmitry Shmidt96571392013-10-14 12:54:46 -07001253 }
1254
1255 return NULL;
1256}
1257
1258
1259/**
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001260 * wpa_bss_get_vendor_ie_multi - Fetch vendor IE data from a BSS entry
1261 * @bss: BSS table entry
1262 * @vendor_type: Vendor type (four octets starting the IE payload)
1263 * Returns: Pointer to the information element payload or %NULL if not found
1264 *
1265 * This function returns concatenated payload of possibly fragmented vendor
1266 * specific information elements in the BSS entry. The caller is responsible for
1267 * freeing the returned buffer.
1268 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001269struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
1270 u32 vendor_type)
1271{
1272 struct wpabuf *buf;
1273 const u8 *end, *pos;
1274
1275 buf = wpabuf_alloc(bss->ie_len);
1276 if (buf == NULL)
1277 return NULL;
1278
Hai Shalom60840252021-02-19 19:02:11 -08001279 pos = wpa_bss_ie_ptr(bss);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001280 end = pos + bss->ie_len;
1281
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001282 while (end - pos > 1) {
Hai Shalom60840252021-02-19 19:02:11 -08001283 u8 ie, len;
1284
1285 ie = pos[0];
1286 len = pos[1];
1287 if (len > end - pos - 2)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001288 break;
Hai Shalom60840252021-02-19 19:02:11 -08001289 pos += 2;
1290 if (ie == WLAN_EID_VENDOR_SPECIFIC && len >= 4 &&
1291 vendor_type == WPA_GET_BE32(pos))
1292 wpabuf_put_data(buf, pos + 4, len - 4);
1293 pos += len;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001294 }
1295
1296 if (wpabuf_len(buf) == 0) {
1297 wpabuf_free(buf);
1298 buf = NULL;
1299 }
1300
1301 return buf;
1302}
1303
1304
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001305/**
1306 * wpa_bss_get_vendor_ie_multi_beacon - Fetch vendor IE data from a BSS entry
1307 * @bss: BSS table entry
1308 * @vendor_type: Vendor type (four octets starting the IE payload)
1309 * Returns: Pointer to the information element payload or %NULL if not found
1310 *
1311 * This function returns concatenated payload of possibly fragmented vendor
1312 * specific information elements in the BSS entry. The caller is responsible for
1313 * freeing the returned buffer.
1314 *
1315 * This function is like wpa_bss_get_vendor_ie_multi(), but uses IE buffer only
1316 * from Beacon frames instead of either Beacon or Probe Response frames.
1317 */
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -07001318struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss,
1319 u32 vendor_type)
1320{
1321 struct wpabuf *buf;
1322 const u8 *end, *pos;
1323
1324 buf = wpabuf_alloc(bss->beacon_ie_len);
1325 if (buf == NULL)
1326 return NULL;
1327
Hai Shalom60840252021-02-19 19:02:11 -08001328 pos = wpa_bss_ie_ptr(bss);
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -07001329 pos += bss->ie_len;
1330 end = pos + bss->beacon_ie_len;
1331
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001332 while (end - pos > 1) {
Sunil8cd6f4d2022-06-28 18:40:46 +00001333 u8 id, len;
1334
1335 id = *pos++;
1336 len = *pos++;
1337 if (len > end - pos)
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -07001338 break;
Sunil8cd6f4d2022-06-28 18:40:46 +00001339 if (id == WLAN_EID_VENDOR_SPECIFIC && len >= 4 &&
1340 vendor_type == WPA_GET_BE32(pos))
1341 wpabuf_put_data(buf, pos + 4, len - 4);
1342 pos += len;
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -07001343 }
1344
1345 if (wpabuf_len(buf) == 0) {
1346 wpabuf_free(buf);
1347 buf = NULL;
1348 }
1349
1350 return buf;
1351}
1352
1353
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001354/**
1355 * wpa_bss_get_max_rate - Get maximum legacy TX rate supported in a BSS
1356 * @bss: BSS table entry
1357 * Returns: Maximum legacy rate in units of 500 kbps
1358 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001359int wpa_bss_get_max_rate(const struct wpa_bss *bss)
1360{
1361 int rate = 0;
1362 const u8 *ie;
1363 int i;
1364
1365 ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
1366 for (i = 0; ie && i < ie[1]; i++) {
1367 if ((ie[i + 2] & 0x7f) > rate)
1368 rate = ie[i + 2] & 0x7f;
1369 }
1370
1371 ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
1372 for (i = 0; ie && i < ie[1]; i++) {
1373 if ((ie[i + 2] & 0x7f) > rate)
1374 rate = ie[i + 2] & 0x7f;
1375 }
1376
1377 return rate;
1378}
1379
1380
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001381/**
1382 * wpa_bss_get_bit_rates - Get legacy TX rates supported in a BSS
1383 * @bss: BSS table entry
1384 * @rates: Buffer for returning a pointer to the rates list (units of 500 kbps)
1385 * Returns: number of legacy TX rates or -1 on failure
1386 *
1387 * The caller is responsible for freeing the returned buffer with os_free() in
1388 * case of success.
1389 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001390int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates)
1391{
1392 const u8 *ie, *ie2;
1393 int i, j;
1394 unsigned int len;
1395 u8 *r;
1396
1397 ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
1398 ie2 = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
1399
1400 len = (ie ? ie[1] : 0) + (ie2 ? ie2[1] : 0);
1401
1402 r = os_malloc(len);
1403 if (!r)
1404 return -1;
1405
1406 for (i = 0; ie && i < ie[1]; i++)
1407 r[i] = ie[i + 2] & 0x7f;
1408
1409 for (j = 0; ie2 && j < ie2[1]; j++)
1410 r[i + j] = ie2[j + 2] & 0x7f;
1411
1412 *rates = r;
1413 return len;
1414}
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001415
1416
1417#ifdef CONFIG_FILS
Hai Shalom60840252021-02-19 19:02:11 -08001418const u8 * wpa_bss_get_fils_cache_id(const struct wpa_bss *bss)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001419{
1420 const u8 *ie;
1421
1422 if (bss) {
1423 ie = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
1424 if (ie && ie[1] >= 4 && WPA_GET_LE16(ie + 2) & BIT(7))
1425 return ie + 4;
1426 }
1427
1428 return NULL;
1429}
1430#endif /* CONFIG_FILS */
Hai Shalom74f70d42019-02-11 14:42:39 -08001431
1432
1433int wpa_bss_ext_capab(const struct wpa_bss *bss, unsigned int capab)
1434{
Hai Shalom60840252021-02-19 19:02:11 -08001435 if (!bss)
1436 return 0;
Hai Shalom74f70d42019-02-11 14:42:39 -08001437 return ieee802_11_ext_capab(wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB),
1438 capab);
1439}