blob: 10ebab0207a85341a10c17369a6e15bed94293c8 [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"
Sunil Ravib0ac25f2024-07-12 01:42:03 +000016#include "rsn_supp/wpa.h"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070017#include "wpa_supplicant_i.h"
18#include "config.h"
19#include "notify.h"
20#include "scan.h"
Sunil Ravib0ac25f2024-07-12 01:42:03 +000021#include "bssid_ignore.h"
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070022#include "bss.h"
23
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -070024static void wpa_bss_set_hessid(struct wpa_bss *bss)
25{
26#ifdef CONFIG_INTERWORKING
27 const u8 *ie = wpa_bss_get_ie(bss, WLAN_EID_INTERWORKING);
28 if (ie == NULL || (ie[1] != 7 && ie[1] != 9)) {
29 os_memset(bss->hessid, 0, ETH_ALEN);
30 return;
31 }
32 if (ie[1] == 7)
33 os_memcpy(bss->hessid, ie + 3, ETH_ALEN);
34 else
35 os_memcpy(bss->hessid, ie + 5, ETH_ALEN);
36#endif /* CONFIG_INTERWORKING */
37}
38
39
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -080040/**
41 * wpa_bss_anqp_alloc - Allocate ANQP data structure for a BSS entry
42 * Returns: Allocated ANQP data structure or %NULL on failure
43 *
44 * The allocated ANQP data structure has its users count set to 1. It may be
45 * shared by multiple BSS entries and each shared entry is freed with
46 * wpa_bss_anqp_free().
47 */
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -070048struct wpa_bss_anqp * wpa_bss_anqp_alloc(void)
49{
50 struct wpa_bss_anqp *anqp;
51 anqp = os_zalloc(sizeof(*anqp));
52 if (anqp == NULL)
53 return NULL;
Dmitry Shmidtd80a4012015-11-05 16:35:40 -080054#ifdef CONFIG_INTERWORKING
55 dl_list_init(&anqp->anqp_elems);
56#endif /* CONFIG_INTERWORKING */
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -070057 anqp->users = 1;
58 return anqp;
59}
60
61
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -080062/**
63 * wpa_bss_anqp_clone - Clone an ANQP data structure
64 * @anqp: ANQP data structure from wpa_bss_anqp_alloc()
65 * Returns: Cloned ANQP data structure or %NULL on failure
66 */
Dmitry Shmidtd5e49232012-12-03 15:08:10 -080067static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp)
68{
69 struct wpa_bss_anqp *n;
70
71 n = os_zalloc(sizeof(*n));
72 if (n == NULL)
73 return NULL;
74
75#define ANQP_DUP(f) if (anqp->f) n->f = wpabuf_dup(anqp->f)
76#ifdef CONFIG_INTERWORKING
Dmitry Shmidtd80a4012015-11-05 16:35:40 -080077 dl_list_init(&n->anqp_elems);
Dmitry Shmidt7f656022015-02-25 14:36:37 -080078 ANQP_DUP(capability_list);
Dmitry Shmidtd5e49232012-12-03 15:08:10 -080079 ANQP_DUP(venue_name);
80 ANQP_DUP(network_auth_type);
81 ANQP_DUP(roaming_consortium);
82 ANQP_DUP(ip_addr_type_availability);
83 ANQP_DUP(nai_realm);
84 ANQP_DUP(anqp_3gpp);
85 ANQP_DUP(domain_name);
Dmitry Shmidt29333592017-01-09 12:27:11 -080086 ANQP_DUP(fils_realm_info);
Dmitry Shmidtd5e49232012-12-03 15:08:10 -080087#endif /* CONFIG_INTERWORKING */
88#ifdef CONFIG_HS20
Dmitry Shmidt7f656022015-02-25 14:36:37 -080089 ANQP_DUP(hs20_capability_list);
Dmitry Shmidtd5e49232012-12-03 15:08:10 -080090 ANQP_DUP(hs20_operator_friendly_name);
91 ANQP_DUP(hs20_wan_metrics);
92 ANQP_DUP(hs20_connection_capability);
93 ANQP_DUP(hs20_operating_class);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -080094 ANQP_DUP(hs20_osu_providers_list);
Roshan Pius3a1667e2018-07-03 15:17:14 -070095 ANQP_DUP(hs20_operator_icon_metadata);
Hai Shalom39ba6fc2019-01-22 12:40:38 -080096 ANQP_DUP(hs20_osu_providers_nai_list);
Dmitry Shmidtd5e49232012-12-03 15:08:10 -080097#endif /* CONFIG_HS20 */
98#undef ANQP_DUP
99
100 return n;
101}
102
103
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800104/**
105 * wpa_bss_anqp_unshare_alloc - Unshare ANQP data (if shared) in a BSS entry
106 * @bss: BSS entry
107 * Returns: 0 on success, -1 on failure
108 *
109 * This function ensures the specific BSS entry has an ANQP data structure that
110 * is not shared with any other BSS entry.
111 */
Dmitry Shmidtd5e49232012-12-03 15:08:10 -0800112int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss)
113{
114 struct wpa_bss_anqp *anqp;
115
116 if (bss->anqp && bss->anqp->users > 1) {
117 /* allocated, but shared - clone an unshared copy */
118 anqp = wpa_bss_anqp_clone(bss->anqp);
119 if (anqp == NULL)
120 return -1;
121 anqp->users = 1;
122 bss->anqp->users--;
123 bss->anqp = anqp;
124 return 0;
125 }
126
127 if (bss->anqp)
128 return 0; /* already allocated and not shared */
129
130 /* not allocated - allocate a new storage area */
131 bss->anqp = wpa_bss_anqp_alloc();
132 return bss->anqp ? 0 : -1;
133}
134
135
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800136/**
137 * wpa_bss_anqp_free - Free an ANQP data structure
138 * @anqp: ANQP data structure from wpa_bss_anqp_alloc() or wpa_bss_anqp_clone()
139 */
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700140static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp)
141{
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800142#ifdef CONFIG_INTERWORKING
143 struct wpa_bss_anqp_elem *elem;
144#endif /* CONFIG_INTERWORKING */
145
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700146 if (anqp == NULL)
147 return;
148
149 anqp->users--;
150 if (anqp->users > 0) {
151 /* Another BSS entry holds a pointer to this ANQP info */
152 return;
153 }
154
155#ifdef CONFIG_INTERWORKING
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800156 wpabuf_free(anqp->capability_list);
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700157 wpabuf_free(anqp->venue_name);
158 wpabuf_free(anqp->network_auth_type);
159 wpabuf_free(anqp->roaming_consortium);
160 wpabuf_free(anqp->ip_addr_type_availability);
161 wpabuf_free(anqp->nai_realm);
162 wpabuf_free(anqp->anqp_3gpp);
163 wpabuf_free(anqp->domain_name);
Dmitry Shmidt29333592017-01-09 12:27:11 -0800164 wpabuf_free(anqp->fils_realm_info);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800165
166 while ((elem = dl_list_first(&anqp->anqp_elems,
167 struct wpa_bss_anqp_elem, list))) {
168 dl_list_del(&elem->list);
169 wpabuf_free(elem->payload);
170 os_free(elem);
171 }
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700172#endif /* CONFIG_INTERWORKING */
173#ifdef CONFIG_HS20
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800174 wpabuf_free(anqp->hs20_capability_list);
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700175 wpabuf_free(anqp->hs20_operator_friendly_name);
176 wpabuf_free(anqp->hs20_wan_metrics);
177 wpabuf_free(anqp->hs20_connection_capability);
178 wpabuf_free(anqp->hs20_operating_class);
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800179 wpabuf_free(anqp->hs20_osu_providers_list);
Roshan Pius3a1667e2018-07-03 15:17:14 -0700180 wpabuf_free(anqp->hs20_operator_icon_metadata);
Hai Shalom39ba6fc2019-01-22 12:40:38 -0800181 wpabuf_free(anqp->hs20_osu_providers_nai_list);
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700182#endif /* CONFIG_HS20 */
183
184 os_free(anqp);
185}
186
187
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000188static struct wpa_connect_work *
189wpa_bss_check_pending_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
Dmitry Shmidt2e425d62014-11-10 11:18:27 -0800190{
191 struct wpa_radio_work *work;
192 struct wpa_connect_work *cwork;
193
194 work = radio_work_pending(wpa_s, "sme-connect");
195 if (!work)
196 work = radio_work_pending(wpa_s, "connect");
197 if (!work)
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000198 return NULL;
Dmitry Shmidt2e425d62014-11-10 11:18:27 -0800199
200 cwork = work->ctx;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000201 if (cwork->bss != bss)
202 return NULL;
Dmitry Shmidt2e425d62014-11-10 11:18:27 -0800203
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000204 return cwork;
205}
206
207
208static void wpa_bss_update_pending_connect(struct wpa_connect_work *cwork,
209 struct wpa_bss *new_bss)
210{
Dmitry Shmidt2e425d62014-11-10 11:18:27 -0800211 wpa_printf(MSG_DEBUG,
212 "Update BSS pointer for the pending connect radio work");
213 cwork->bss = new_bss;
214 if (!new_bss)
215 cwork->bss_removed = 1;
216}
217
218
Dmitry Shmidt9839ecd2016-11-07 11:05:47 -0800219void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
220 const char *reason)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700221{
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000222 struct wpa_connect_work *cwork;
223
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700224 if (wpa_s->last_scan_res) {
225 unsigned int i;
226 for (i = 0; i < wpa_s->last_scan_res_used; i++) {
227 if (wpa_s->last_scan_res[i] == bss) {
228 os_memmove(&wpa_s->last_scan_res[i],
229 &wpa_s->last_scan_res[i + 1],
230 (wpa_s->last_scan_res_used - i - 1)
231 * sizeof(struct wpa_bss *));
232 wpa_s->last_scan_res_used--;
233 break;
234 }
235 }
236 }
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000237 cwork = wpa_bss_check_pending_connect(wpa_s, bss);
238 if (cwork)
239 wpa_bss_update_pending_connect(cwork, NULL);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700240 dl_list_del(&bss->list);
241 dl_list_del(&bss->list_id);
242 wpa_s->num_bss--;
243 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR
Dmitry Shmidt04949592012-07-19 12:16:46 -0700244 " SSID '%s' due to %s", bss->id, MAC2STR(bss->bssid),
245 wpa_ssid_txt(bss->ssid, bss->ssid_len), reason);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700246 wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id);
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700247 wpa_bss_anqp_free(bss->anqp);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700248 os_free(bss);
249}
250
251
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800252/**
253 * wpa_bss_get - Fetch a BSS table entry based on BSSID and SSID
254 * @wpa_s: Pointer to wpa_supplicant data
Matthew Wangdcf19452022-11-07 20:42:52 -0800255 * @bssid: BSSID, or %NULL to match any BSSID
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800256 * @ssid: SSID
257 * @ssid_len: Length of @ssid
258 * Returns: Pointer to the BSS entry or %NULL if not found
259 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700260struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
261 const u8 *ssid, size_t ssid_len)
262{
263 struct wpa_bss *bss;
Matthew Wangafc981e2023-03-17 21:30:12 +0000264
265 if (bssid && !wpa_supplicant_filter_bssid_match(wpa_s, bssid))
Dmitry Shmidt04949592012-07-19 12:16:46 -0700266 return NULL;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700267 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000268 if ((!bssid || ether_addr_equal(bss->bssid, bssid)) &&
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700269 bss->ssid_len == ssid_len &&
270 os_memcmp(bss->ssid, ssid, ssid_len) == 0)
271 return bss;
272 }
273 return NULL;
274}
275
Sunil Ravic0f5d412024-09-11 22:12:49 +0000276/**
277 * wpa_bss_get_connection - Fetch a BSS table entry based on BSSID and SSID.
278 * @wpa_s: Pointer to wpa_supplicant data
279 * @bssid: BSSID, or %NULL to match any BSSID
280 * @ssid: SSID
281 * @ssid_len: Length of @ssid
282 * Returns: Pointer to the BSS entry or %NULL if not found
283 *
284 * This function is similar to wpa_bss_get() but it will also return OWE
285 * transition mode encrypted networks for which transition-element matches
286 * @ssid.
287 */
288struct wpa_bss * wpa_bss_get_connection(struct wpa_supplicant *wpa_s,
289 const u8 *bssid,
290 const u8 *ssid, size_t ssid_len)
291{
292 struct wpa_bss *bss;
293#ifdef CONFIG_OWE
294 const u8 *owe, *owe_bssid, *owe_ssid;
295 size_t owe_ssid_len;
296#endif /* CONFIG_OWE */
297
298 if (bssid && !wpa_supplicant_filter_bssid_match(wpa_s, bssid))
299 return NULL;
300 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
301 if (bssid && !ether_addr_equal(bss->bssid, bssid))
302 continue;
303
304 if (bss->ssid_len == ssid_len &&
305 os_memcmp(bss->ssid, ssid, ssid_len) == 0)
306 return bss;
307
308#ifdef CONFIG_OWE
309 /* Check if OWE transition mode element is present and matches
310 * the SSID */
311 owe = wpa_bss_get_vendor_ie(bss, OWE_IE_VENDOR_TYPE);
312 if (!owe)
313 continue;
314
315 if (wpas_get_owe_trans_network(owe, &owe_bssid, &owe_ssid,
316 &owe_ssid_len))
317 continue;
318
Sunil Ravi79e6c4f2025-01-04 00:47:06 +0000319 if (bss->ssid_len &&
320 owe_ssid_len == ssid_len &&
Sunil Ravic0f5d412024-09-11 22:12:49 +0000321 os_memcmp(owe_ssid, ssid, ssid_len) == 0)
322 return bss;
323#endif /* CONFIG_OWE */
324 }
325 return NULL;
326}
327
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700328
Dmitry Shmidt29333592017-01-09 12:27:11 -0800329void calculate_update_time(const struct os_reltime *fetch_time,
330 unsigned int age_ms,
331 struct os_reltime *update_time)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700332{
333 os_time_t usec;
334
Dmitry Shmidt444d5672013-04-01 13:08:44 -0700335 update_time->sec = fetch_time->sec;
336 update_time->usec = fetch_time->usec;
337 update_time->sec -= age_ms / 1000;
338 usec = (age_ms % 1000) * 1000;
339 if (update_time->usec < usec) {
340 update_time->sec--;
341 update_time->usec += 1000000;
342 }
343 update_time->usec -= usec;
344}
345
346
347static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src,
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800348 struct os_reltime *fetch_time)
Dmitry Shmidt444d5672013-04-01 13:08:44 -0700349{
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700350 dst->flags = src->flags;
351 os_memcpy(dst->bssid, src->bssid, ETH_ALEN);
352 dst->freq = src->freq;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000353 dst->max_cw = src->max_cw;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700354 dst->beacon_int = src->beacon_int;
355 dst->caps = src->caps;
356 dst->qual = src->qual;
357 dst->noise = src->noise;
358 dst->level = src->level;
359 dst->tsf = src->tsf;
Sunil Ravia04bd252022-05-02 22:54:18 -0700360 dst->beacon_newer = src->beacon_newer;
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800361 dst->est_throughput = src->est_throughput;
362 dst->snr = src->snr;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700363
Dmitry Shmidt444d5672013-04-01 13:08:44 -0700364 calculate_update_time(fetch_time, src->age, &dst->last_update);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700365}
366
367
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -0700368static int wpa_bss_is_wps_candidate(struct wpa_supplicant *wpa_s,
369 struct wpa_bss *bss)
370{
371#ifdef CONFIG_WPS
372 struct wpa_ssid *ssid;
373 struct wpabuf *wps_ie;
374 int pbc = 0, ret;
375
376 wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
377 if (!wps_ie)
378 return 0;
379
380 if (wps_is_selected_pbc_registrar(wps_ie)) {
381 pbc = 1;
382 } else if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) {
383 wpabuf_free(wps_ie);
384 return 0;
385 }
386
387 for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
388 if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
389 continue;
390 if (ssid->ssid_len &&
391 (ssid->ssid_len != bss->ssid_len ||
392 os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) != 0))
393 continue;
394
395 if (pbc)
396 ret = eap_is_wps_pbc_enrollee(&ssid->eap);
397 else
398 ret = eap_is_wps_pin_enrollee(&ssid->eap);
399 wpabuf_free(wps_ie);
400 return ret;
401 }
402 wpabuf_free(wps_ie);
403#endif /* CONFIG_WPS */
404
405 return 0;
406}
407
408
Hai Shalom60840252021-02-19 19:02:11 -0800409static bool is_p2p_pending_bss(struct wpa_supplicant *wpa_s,
410 struct wpa_bss *bss)
411{
412#ifdef CONFIG_P2P
413 u8 addr[ETH_ALEN];
414
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000415 if (ether_addr_equal(bss->bssid, wpa_s->pending_join_iface_addr))
Hai Shalom60840252021-02-19 19:02:11 -0800416 return true;
417 if (!is_zero_ether_addr(wpa_s->pending_join_dev_addr) &&
418 p2p_parse_dev_addr(wpa_bss_ie_ptr(bss), bss->ie_len, addr) == 0 &&
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000419 ether_addr_equal(addr, wpa_s->pending_join_dev_addr))
Hai Shalom60840252021-02-19 19:02:11 -0800420 return true;
421#endif /* CONFIG_P2P */
422 return false;
423}
424
425
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800426static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
427{
428 struct wpa_ssid *ssid;
429
Hai Shalom60840252021-02-19 19:02:11 -0800430 if (is_p2p_pending_bss(wpa_s, bss))
431 return 1;
432
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800433 for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
434 if (ssid->ssid == NULL || ssid->ssid_len == 0)
435 continue;
436 if (ssid->ssid_len == bss->ssid_len &&
437 os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) == 0)
438 return 1;
439 }
440
441 return 0;
442}
443
444
Dmitry Shmidt04949592012-07-19 12:16:46 -0700445static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
446{
Sunil Ravi89eba102022-09-13 21:04:37 -0700447 int i;
448
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800449 if (bss == wpa_s->current_bss)
450 return 1;
451
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000452 if (bss == wpa_s->ml_connect_probe_bss)
453 return 1;
454
Sunil Ravi99c035e2024-07-12 01:42:03 +0000455#ifdef CONFIG_WNM
456 if (bss == wpa_s->wnm_target_bss)
457 return 1;
458#endif /* CONFIG_WNM */
459
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800460 if (wpa_s->current_bss &&
461 (bss->ssid_len != wpa_s->current_bss->ssid_len ||
462 os_memcmp(bss->ssid, wpa_s->current_bss->ssid,
463 bss->ssid_len) != 0))
464 return 0; /* SSID has changed */
465
Sunil Ravi89eba102022-09-13 21:04:37 -0700466 if (!is_zero_ether_addr(bss->bssid) &&
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000467 (ether_addr_equal(bss->bssid, wpa_s->bssid) ||
468 ether_addr_equal(bss->bssid, wpa_s->pending_bssid)))
Sunil Ravi89eba102022-09-13 21:04:37 -0700469 return 1;
470
471 if (!wpa_s->valid_links)
472 return 0;
473
Sunil Ravi99c035e2024-07-12 01:42:03 +0000474 for_each_link(wpa_s->valid_links, i) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000475 if (ether_addr_equal(bss->bssid, wpa_s->links[i].bssid))
Sunil Ravi89eba102022-09-13 21:04:37 -0700476 return 1;
477 }
478
479 return 0;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700480}
481
482
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800483static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s)
484{
485 struct wpa_bss *bss;
486
487 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -0700488 if (!wpa_bss_known(wpa_s, bss) &&
489 !wpa_bss_is_wps_candidate(wpa_s, bss)) {
Dmitry Shmidt04949592012-07-19 12:16:46 -0700490 wpa_bss_remove(wpa_s, bss, __func__);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800491 return 0;
492 }
493 }
494
495 return -1;
496}
497
498
Dmitry Shmidt04949592012-07-19 12:16:46 -0700499static int wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s)
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800500{
Dmitry Shmidt04949592012-07-19 12:16:46 -0700501 struct wpa_bss *bss;
502
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800503 /*
504 * Remove the oldest entry that does not match with any configured
505 * network.
506 */
507 if (wpa_bss_remove_oldest_unknown(wpa_s) == 0)
Dmitry Shmidt04949592012-07-19 12:16:46 -0700508 return 0;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800509
510 /*
Dmitry Shmidt04949592012-07-19 12:16:46 -0700511 * Remove the oldest entry that isn't currently in use.
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800512 */
Dmitry Shmidt04949592012-07-19 12:16:46 -0700513 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
514 if (!wpa_bss_in_use(wpa_s, bss)) {
515 wpa_bss_remove(wpa_s, bss, __func__);
516 return 0;
517 }
518 }
519
520 return -1;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800521}
522
523
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700524static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
525 const u8 *ssid, size_t ssid_len,
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800526 struct wpa_scan_res *res,
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800527 struct os_reltime *fetch_time)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700528{
529 struct wpa_bss *bss;
Sunil Ravi89eba102022-09-13 21:04:37 -0700530 char extra[100];
531 const u8 *ml_ie;
532 char *pos, *end;
533 int ret = 0;
534 const u8 *mld_addr;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700535
536 bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len);
537 if (bss == NULL)
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700538 return NULL;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700539 bss->id = wpa_s->bss_next_id++;
540 bss->last_update_idx = wpa_s->bss_update_idx;
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800541 wpa_bss_copy_res(bss, res, fetch_time);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700542 os_memcpy(bss->ssid, ssid, ssid_len);
543 bss->ssid_len = ssid_len;
544 bss->ie_len = res->ie_len;
545 bss->beacon_ie_len = res->beacon_ie_len;
Hai Shalom60840252021-02-19 19:02:11 -0800546 os_memcpy(bss->ies, res + 1, res->ie_len + res->beacon_ie_len);
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700547 wpa_bss_set_hessid(bss);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700548
Sunil Ravi89eba102022-09-13 21:04:37 -0700549 os_memset(bss->mld_addr, 0, ETH_ALEN);
550 ml_ie = wpa_scan_get_ml_ie(res, MULTI_LINK_CONTROL_TYPE_BASIC);
551 if (ml_ie) {
552 mld_addr = get_basic_mle_mld_addr(&ml_ie[3], ml_ie[1] - 1);
553 if (mld_addr)
554 os_memcpy(bss->mld_addr, mld_addr, ETH_ALEN);
555 }
556
Jouni Malinen7a6c8302013-09-27 15:47:09 +0300557 if (wpa_s->num_bss + 1 > wpa_s->conf->bss_max_count &&
558 wpa_bss_remove_oldest(wpa_s) != 0) {
559 wpa_printf(MSG_ERROR, "Increasing the MAX BSS count to %d "
560 "because all BSSes are in use. We should normally "
561 "not get here!", (int) wpa_s->num_bss + 1);
562 wpa_s->conf->bss_max_count = wpa_s->num_bss + 1;
563 }
564
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700565 dl_list_add_tail(&wpa_s->bss, &bss->list);
566 dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
567 wpa_s->num_bss++;
Sunil Ravi89eba102022-09-13 21:04:37 -0700568
569 extra[0] = '\0';
570 pos = extra;
571 end = pos + sizeof(extra);
Hai Shalom81f62d82019-07-22 12:10:00 -0700572 if (!is_zero_ether_addr(bss->hessid))
Sunil Ravi89eba102022-09-13 21:04:37 -0700573 ret = os_snprintf(pos, end - pos, " HESSID " MACSTR,
574 MAC2STR(bss->hessid));
575
576 if (!is_zero_ether_addr(bss->mld_addr) &&
577 !os_snprintf_error(end - pos, ret)) {
578 pos += ret;
579 ret = os_snprintf(pos, end - pos, " MLD ADDR " MACSTR,
580 MAC2STR(bss->mld_addr));
581 }
582
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700583 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR
Hai Shalom81f62d82019-07-22 12:10:00 -0700584 " SSID '%s' freq %d%s",
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800585 bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len),
Hai Shalom81f62d82019-07-22 12:10:00 -0700586 bss->freq, extra);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700587 wpas_notify_bss_added(wpa_s, bss->bssid, bss->id);
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700588 return bss;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700589}
590
591
592static int are_ies_equal(const struct wpa_bss *old,
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700593 const struct wpa_scan_res *new_res, u32 ie)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700594{
595 const u8 *old_ie, *new_ie;
596 struct wpabuf *old_ie_buff = NULL;
597 struct wpabuf *new_ie_buff = NULL;
598 int new_ie_len, old_ie_len, ret, is_multi;
599
600 switch (ie) {
601 case WPA_IE_VENDOR_TYPE:
602 old_ie = wpa_bss_get_vendor_ie(old, ie);
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700603 new_ie = wpa_scan_get_vendor_ie(new_res, ie);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700604 is_multi = 0;
605 break;
606 case WPS_IE_VENDOR_TYPE:
607 old_ie_buff = wpa_bss_get_vendor_ie_multi(old, ie);
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700608 new_ie_buff = wpa_scan_get_vendor_ie_multi(new_res, ie);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700609 is_multi = 1;
610 break;
611 case WLAN_EID_RSN:
612 case WLAN_EID_SUPP_RATES:
613 case WLAN_EID_EXT_SUPP_RATES:
614 old_ie = wpa_bss_get_ie(old, ie);
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700615 new_ie = wpa_scan_get_ie(new_res, ie);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700616 is_multi = 0;
617 break;
618 default:
619 wpa_printf(MSG_DEBUG, "bss: %s: cannot compare IEs", __func__);
620 return 0;
621 }
622
623 if (is_multi) {
624 /* in case of multiple IEs stored in buffer */
625 old_ie = old_ie_buff ? wpabuf_head_u8(old_ie_buff) : NULL;
626 new_ie = new_ie_buff ? wpabuf_head_u8(new_ie_buff) : NULL;
627 old_ie_len = old_ie_buff ? wpabuf_len(old_ie_buff) : 0;
628 new_ie_len = new_ie_buff ? wpabuf_len(new_ie_buff) : 0;
629 } else {
630 /* in case of single IE */
631 old_ie_len = old_ie ? old_ie[1] + 2 : 0;
632 new_ie_len = new_ie ? new_ie[1] + 2 : 0;
633 }
634
635 if (!old_ie || !new_ie)
636 ret = !old_ie && !new_ie;
637 else
638 ret = (old_ie_len == new_ie_len &&
639 os_memcmp(old_ie, new_ie, old_ie_len) == 0);
640
641 wpabuf_free(old_ie_buff);
642 wpabuf_free(new_ie_buff);
643
644 return ret;
645}
646
647
648static u32 wpa_bss_compare_res(const struct wpa_bss *old,
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700649 const struct wpa_scan_res *new_res)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700650{
651 u32 changes = 0;
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700652 int caps_diff = old->caps ^ new_res->caps;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700653
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700654 if (old->freq != new_res->freq)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700655 changes |= WPA_BSS_FREQ_CHANGED_FLAG;
656
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700657 if (old->level != new_res->level)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700658 changes |= WPA_BSS_SIGNAL_CHANGED_FLAG;
659
660 if (caps_diff & IEEE80211_CAP_PRIVACY)
661 changes |= WPA_BSS_PRIVACY_CHANGED_FLAG;
662
663 if (caps_diff & IEEE80211_CAP_IBSS)
664 changes |= WPA_BSS_MODE_CHANGED_FLAG;
665
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700666 if (old->ie_len == new_res->ie_len &&
Hai Shalom60840252021-02-19 19:02:11 -0800667 os_memcmp(wpa_bss_ie_ptr(old), new_res + 1, old->ie_len) == 0)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700668 return changes;
669 changes |= WPA_BSS_IES_CHANGED_FLAG;
670
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700671 if (!are_ies_equal(old, new_res, WPA_IE_VENDOR_TYPE))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700672 changes |= WPA_BSS_WPAIE_CHANGED_FLAG;
673
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700674 if (!are_ies_equal(old, new_res, WLAN_EID_RSN))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700675 changes |= WPA_BSS_RSNIE_CHANGED_FLAG;
676
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700677 if (!are_ies_equal(old, new_res, WPS_IE_VENDOR_TYPE))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700678 changes |= WPA_BSS_WPS_CHANGED_FLAG;
679
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700680 if (!are_ies_equal(old, new_res, WLAN_EID_SUPP_RATES) ||
681 !are_ies_equal(old, new_res, WLAN_EID_EXT_SUPP_RATES))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700682 changes |= WPA_BSS_RATES_CHANGED_FLAG;
683
684 return changes;
685}
686
687
Hai Shalom60840252021-02-19 19:02:11 -0800688void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes,
689 const struct wpa_bss *bss)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700690{
691 if (changes & WPA_BSS_FREQ_CHANGED_FLAG)
692 wpas_notify_bss_freq_changed(wpa_s, bss->id);
693
694 if (changes & WPA_BSS_SIGNAL_CHANGED_FLAG)
695 wpas_notify_bss_signal_changed(wpa_s, bss->id);
696
697 if (changes & WPA_BSS_PRIVACY_CHANGED_FLAG)
698 wpas_notify_bss_privacy_changed(wpa_s, bss->id);
699
700 if (changes & WPA_BSS_MODE_CHANGED_FLAG)
701 wpas_notify_bss_mode_changed(wpa_s, bss->id);
702
703 if (changes & WPA_BSS_WPAIE_CHANGED_FLAG)
704 wpas_notify_bss_wpaie_changed(wpa_s, bss->id);
705
706 if (changes & WPA_BSS_RSNIE_CHANGED_FLAG)
707 wpas_notify_bss_rsnie_changed(wpa_s, bss->id);
708
709 if (changes & WPA_BSS_WPS_CHANGED_FLAG)
710 wpas_notify_bss_wps_changed(wpa_s, bss->id);
711
712 if (changes & WPA_BSS_IES_CHANGED_FLAG)
713 wpas_notify_bss_ies_changed(wpa_s, bss->id);
714
715 if (changes & WPA_BSS_RATES_CHANGED_FLAG)
716 wpas_notify_bss_rates_changed(wpa_s, bss->id);
Dmitry Shmidt661b4f72014-09-29 14:58:27 -0700717
718 wpas_notify_bss_seen(wpa_s, bss->id);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700719}
720
721
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700722static struct wpa_bss *
723wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800724 struct wpa_scan_res *res, struct os_reltime *fetch_time)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700725{
726 u32 changes;
727
Dmitry Shmidtabb90a32016-12-05 15:34:39 -0800728 if (bss->last_update_idx == wpa_s->bss_update_idx) {
729 struct os_reltime update_time;
730
731 /*
732 * Some drivers (e.g., cfg80211) include multiple BSS entries
733 * for the same BSS if that BSS's channel changes. The BSS list
734 * implementation in wpa_supplicant does not do that and we need
735 * to filter out the obsolete results here to make sure only the
736 * most current BSS information remains in the table.
737 */
738 wpa_printf(MSG_DEBUG, "BSS: " MACSTR
739 " has multiple entries in the scan results - select the most current one",
740 MAC2STR(bss->bssid));
741 calculate_update_time(fetch_time, res->age, &update_time);
742 wpa_printf(MSG_DEBUG,
743 "Previous last_update: %u.%06u (freq %d%s)",
744 (unsigned int) bss->last_update.sec,
745 (unsigned int) bss->last_update.usec,
746 bss->freq,
747 (bss->flags & WPA_BSS_ASSOCIATED) ? " assoc" : "");
748 wpa_printf(MSG_DEBUG, "New last_update: %u.%06u (freq %d%s)",
749 (unsigned int) update_time.sec,
750 (unsigned int) update_time.usec,
751 res->freq,
752 (res->flags & WPA_SCAN_ASSOCIATED) ? " assoc" : "");
753 if ((bss->flags & WPA_BSS_ASSOCIATED) ||
754 (!(res->flags & WPA_SCAN_ASSOCIATED) &&
755 !os_reltime_before(&bss->last_update, &update_time))) {
756 wpa_printf(MSG_DEBUG,
757 "Ignore this BSS entry since the previous update looks more current");
758 return bss;
759 }
760 wpa_printf(MSG_DEBUG,
761 "Accept this BSS entry since it looks more current than the previous update");
762 }
763
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700764 changes = wpa_bss_compare_res(bss, res);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800765 if (changes & WPA_BSS_FREQ_CHANGED_FLAG)
766 wpa_printf(MSG_DEBUG, "BSS: " MACSTR " changed freq %d --> %d",
767 MAC2STR(bss->bssid), bss->freq, res->freq);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700768 bss->scan_miss_count = 0;
769 bss->last_update_idx = wpa_s->bss_update_idx;
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800770 wpa_bss_copy_res(bss, res, fetch_time);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700771 /* Move the entry to the end of the list */
772 dl_list_del(&bss->list);
Dmitry Shmidt96571392013-10-14 12:54:46 -0700773#ifdef CONFIG_P2P
774 if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000775 !wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE) &&
776 !(changes & WPA_BSS_FREQ_CHANGED_FLAG)) {
Dmitry Shmidt96571392013-10-14 12:54:46 -0700777 /*
778 * This can happen when non-P2P station interface runs a scan
779 * without P2P IE in the Probe Request frame. P2P GO would reply
780 * to that with a Probe Response that does not include P2P IE.
781 * Do not update the IEs in this BSS entry to avoid such loss of
782 * information that may be needed for P2P operations to
783 * determine group information.
784 */
785 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Do not update scan IEs for "
786 MACSTR " since that would remove P2P IE information",
787 MAC2STR(bss->bssid));
788 } else
789#endif /* CONFIG_P2P */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700790 if (bss->ie_len + bss->beacon_ie_len >=
791 res->ie_len + res->beacon_ie_len) {
Hai Shalom60840252021-02-19 19:02:11 -0800792 os_memcpy(bss->ies, res + 1, res->ie_len + res->beacon_ie_len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700793 bss->ie_len = res->ie_len;
794 bss->beacon_ie_len = res->beacon_ie_len;
795 } else {
796 struct wpa_bss *nbss;
797 struct dl_list *prev = bss->list_id.prev;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000798 struct wpa_connect_work *cwork;
799 unsigned int i;
800 bool update_current_bss = wpa_s->current_bss == bss;
801 bool update_ml_probe_bss = wpa_s->ml_connect_probe_bss == bss;
802
803 cwork = wpa_bss_check_pending_connect(wpa_s, bss);
804
805 for (i = 0; i < wpa_s->last_scan_res_used; i++) {
806 if (wpa_s->last_scan_res[i] == bss)
807 break;
808 }
809
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700810 dl_list_del(&bss->list_id);
811 nbss = os_realloc(bss, sizeof(*bss) + res->ie_len +
812 res->beacon_ie_len);
813 if (nbss) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000814 if (i != wpa_s->last_scan_res_used)
815 wpa_s->last_scan_res[i] = nbss;
816
817 if (update_current_bss)
Dmitry Shmidt04949592012-07-19 12:16:46 -0700818 wpa_s->current_bss = nbss;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000819
820 if (update_ml_probe_bss)
821 wpa_s->ml_connect_probe_bss = nbss;
822
823 if (cwork)
824 wpa_bss_update_pending_connect(cwork, nbss);
825
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700826 bss = nbss;
Hai Shalom60840252021-02-19 19:02:11 -0800827 os_memcpy(bss->ies, res + 1,
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700828 res->ie_len + res->beacon_ie_len);
829 bss->ie_len = res->ie_len;
830 bss->beacon_ie_len = res->beacon_ie_len;
831 }
832 dl_list_add(prev, &bss->list_id);
833 }
Sunil Ravi89eba102022-09-13 21:04:37 -0700834 if (changes & WPA_BSS_IES_CHANGED_FLAG) {
835 const u8 *ml_ie, *mld_addr;
836
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700837 wpa_bss_set_hessid(bss);
Sunil Ravi89eba102022-09-13 21:04:37 -0700838 os_memset(bss->mld_addr, 0, ETH_ALEN);
839 ml_ie = wpa_scan_get_ml_ie(res, MULTI_LINK_CONTROL_TYPE_BASIC);
840 if (ml_ie) {
841 mld_addr = get_basic_mle_mld_addr(&ml_ie[3],
842 ml_ie[1] - 1);
843 if (mld_addr)
844 os_memcpy(bss->mld_addr, mld_addr, ETH_ALEN);
845 }
846 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700847 dl_list_add_tail(&wpa_s->bss, &bss->list);
848
849 notify_bss_changes(wpa_s, changes, bss);
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700850
851 return bss;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700852}
853
854
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800855/**
856 * wpa_bss_update_start - Start a BSS table update from scan results
857 * @wpa_s: Pointer to wpa_supplicant data
858 *
859 * This function is called at the start of each BSS table update round for new
860 * scan results. The actual scan result entries are indicated with calls to
861 * wpa_bss_update_scan_res() and the update round is finished with a call to
862 * wpa_bss_update_end().
863 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700864void wpa_bss_update_start(struct wpa_supplicant *wpa_s)
865{
866 wpa_s->bss_update_idx++;
867 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u",
868 wpa_s->bss_update_idx);
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700869 wpa_s->last_scan_res_used = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700870}
871
872
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800873/**
874 * wpa_bss_update_scan_res - Update a BSS table entry based on a scan result
875 * @wpa_s: Pointer to wpa_supplicant data
876 * @res: Scan result
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800877 * @fetch_time: Time when the result was fetched from the driver
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800878 *
879 * This function updates a BSS table entry (or adds one) based on a scan result.
880 * This is called separately for each scan result between the calls to
881 * wpa_bss_update_start() and wpa_bss_update_end().
882 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700883void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800884 struct wpa_scan_res *res,
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800885 struct os_reltime *fetch_time)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700886{
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800887 const u8 *ssid, *p2p, *mesh;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700888 struct wpa_bss *bss;
889
Dmitry Shmidt444d5672013-04-01 13:08:44 -0700890 if (wpa_s->conf->ignore_old_scan_res) {
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800891 struct os_reltime update;
Dmitry Shmidt444d5672013-04-01 13:08:44 -0700892 calculate_update_time(fetch_time, res->age, &update);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800893 if (os_reltime_before(&update, &wpa_s->scan_trigger_time)) {
894 struct os_reltime age;
895 os_reltime_sub(&wpa_s->scan_trigger_time, &update,
896 &age);
Dmitry Shmidt444d5672013-04-01 13:08:44 -0700897 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Ignore driver BSS "
898 "table entry that is %u.%06u seconds older "
899 "than our scan trigger",
900 (unsigned int) age.sec,
901 (unsigned int) age.usec);
902 return;
903 }
904 }
905
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700906 ssid = wpa_scan_get_ie(res, WLAN_EID_SSID);
907 if (ssid == NULL) {
908 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for "
909 MACSTR, MAC2STR(res->bssid));
910 return;
911 }
Dmitry Shmidt9d9e6022015-04-23 10:34:55 -0700912 if (ssid[1] > SSID_MAX_LEN) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700913 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for "
914 MACSTR, MAC2STR(res->bssid));
915 return;
916 }
917
918 p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700919#ifdef CONFIG_P2P
920 if (p2p == NULL &&
921 wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
922 /*
923 * If it's a P2P specific interface, then don't update
924 * the scan result without a P2P IE.
925 */
926 wpa_printf(MSG_DEBUG, "BSS: No P2P IE - skipping BSS " MACSTR
927 " update for P2P interface", MAC2STR(res->bssid));
928 return;
929 }
930#endif /* CONFIG_P2P */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700931 if (p2p && ssid[1] == P2P_WILDCARD_SSID_LEN &&
932 os_memcmp(ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0)
933 return; /* Skip P2P listen discovery results here */
934
935 /* TODO: add option for ignoring BSSes we are not interested in
936 * (to save memory) */
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800937
938 mesh = wpa_scan_get_ie(res, WLAN_EID_MESH_ID);
Dmitry Shmidt9d9e6022015-04-23 10:34:55 -0700939 if (mesh && mesh[1] <= SSID_MAX_LEN)
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800940 ssid = mesh;
941
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700942 bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
943 if (bss == NULL)
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800944 bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res, fetch_time);
Dmitry Shmidt56052862013-10-04 10:23:25 -0700945 else {
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800946 bss = wpa_bss_update(wpa_s, bss, res, fetch_time);
Dmitry Shmidt56052862013-10-04 10:23:25 -0700947 if (wpa_s->last_scan_res) {
948 unsigned int i;
949 for (i = 0; i < wpa_s->last_scan_res_used; i++) {
950 if (bss == wpa_s->last_scan_res[i]) {
951 /* Already in the list */
952 return;
953 }
954 }
955 }
956 }
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700957
958 if (bss == NULL)
959 return;
960 if (wpa_s->last_scan_res_used >= wpa_s->last_scan_res_size) {
961 struct wpa_bss **n;
962 unsigned int siz;
963 if (wpa_s->last_scan_res_size == 0)
964 siz = 32;
965 else
966 siz = wpa_s->last_scan_res_size * 2;
967 n = os_realloc_array(wpa_s->last_scan_res, siz,
968 sizeof(struct wpa_bss *));
969 if (n == NULL)
970 return;
971 wpa_s->last_scan_res = n;
972 wpa_s->last_scan_res_size = siz;
973 }
974
Dmitry Shmidt7832adb2014-04-29 10:53:02 -0700975 if (wpa_s->last_scan_res)
976 wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700977}
978
979
980static int wpa_bss_included_in_scan(const struct wpa_bss *bss,
981 const struct scan_info *info)
982{
983 int found;
984 size_t i;
985
986 if (info == NULL)
987 return 1;
988
989 if (info->num_freqs) {
990 found = 0;
991 for (i = 0; i < info->num_freqs; i++) {
992 if (bss->freq == info->freqs[i]) {
993 found = 1;
994 break;
995 }
996 }
997 if (!found)
998 return 0;
999 }
1000
1001 if (info->num_ssids) {
1002 found = 0;
1003 for (i = 0; i < info->num_ssids; i++) {
1004 const struct wpa_driver_scan_ssid *s = &info->ssids[i];
1005 if ((s->ssid == NULL || s->ssid_len == 0) ||
1006 (s->ssid_len == bss->ssid_len &&
1007 os_memcmp(s->ssid, bss->ssid, bss->ssid_len) ==
1008 0)) {
1009 found = 1;
1010 break;
1011 }
1012 }
1013 if (!found)
1014 return 0;
1015 }
1016
1017 return 1;
1018}
1019
1020
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001021/**
1022 * wpa_bss_update_end - End a BSS table update from scan results
1023 * @wpa_s: Pointer to wpa_supplicant data
1024 * @info: Information about scan parameters
1025 * @new_scan: Whether this update round was based on a new scan
1026 *
1027 * This function is called at the end of each BSS table update round for new
1028 * scan results. The start of the update was indicated with a call to
1029 * wpa_bss_update_start().
1030 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001031void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
1032 int new_scan)
1033{
1034 struct wpa_bss *bss, *n;
1035
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001036 os_get_reltime(&wpa_s->last_scan);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001037 if ((info && info->aborted) || !new_scan)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001038 return; /* do not expire entries without new scan */
1039
1040 dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
1041 if (wpa_bss_in_use(wpa_s, bss))
1042 continue;
1043 if (!wpa_bss_included_in_scan(bss, info))
1044 continue; /* expire only BSSes that were scanned */
1045 if (bss->last_update_idx < wpa_s->bss_update_idx)
1046 bss->scan_miss_count++;
1047 if (bss->scan_miss_count >=
1048 wpa_s->conf->bss_expiration_scan_count) {
Dmitry Shmidt04949592012-07-19 12:16:46 -07001049 wpa_bss_remove(wpa_s, bss, "no match in scan");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001050 }
1051 }
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -07001052
Hai Shalomfdcde762020-04-02 11:19:20 -07001053 wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%zu/%zu",
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001054 wpa_s->last_scan_res_used, wpa_s->last_scan_res_size);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001055}
1056
1057
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001058/**
1059 * wpa_bss_flush_by_age - Flush old BSS entries
1060 * @wpa_s: Pointer to wpa_supplicant data
1061 * @age: Maximum entry age in seconds
1062 *
1063 * Remove BSS entries that have not been updated during the last @age seconds.
1064 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001065void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age)
1066{
1067 struct wpa_bss *bss, *n;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001068 struct os_reltime t;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001069
1070 if (dl_list_empty(&wpa_s->bss))
1071 return;
1072
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001073 os_get_reltime(&t);
Sunil Raviaf8751c2023-03-29 11:35:17 -07001074
1075 if (t.sec < age)
1076 return; /* avoid underflow; there can be no older entries */
1077
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001078 t.sec -= age;
1079
1080 dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
1081 if (wpa_bss_in_use(wpa_s, bss))
1082 continue;
1083
Sunil Ravi38ad1ed2023-01-17 23:58:31 +00001084 if (wpa_s->reassoc_same_ess &&
1085 wpa_s->wpa_state != WPA_COMPLETED &&
1086 wpa_s->last_ssid &&
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00001087 wpa_s->last_ssid->ssid &&
Sunil Ravi38ad1ed2023-01-17 23:58:31 +00001088 bss->ssid_len == wpa_s->last_ssid->ssid_len &&
1089 os_memcmp(bss->ssid, wpa_s->last_ssid->ssid,
1090 bss->ssid_len) == 0)
1091 continue;
1092
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001093 if (os_reltime_before(&bss->last_update, &t)) {
Dmitry Shmidt04949592012-07-19 12:16:46 -07001094 wpa_bss_remove(wpa_s, bss, __func__);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001095 } else
1096 break;
1097 }
1098}
1099
1100
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001101/**
1102 * wpa_bss_init - Initialize BSS table
1103 * @wpa_s: Pointer to wpa_supplicant data
1104 * Returns: 0 on success, -1 on failure
1105 *
1106 * This prepares BSS table lists and timer for periodic updates. The BSS table
1107 * is deinitialized with wpa_bss_deinit() once not needed anymore.
1108 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001109int wpa_bss_init(struct wpa_supplicant *wpa_s)
1110{
1111 dl_list_init(&wpa_s->bss);
1112 dl_list_init(&wpa_s->bss_id);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001113 return 0;
1114}
1115
1116
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001117/**
1118 * wpa_bss_flush - Flush all unused BSS entries
1119 * @wpa_s: Pointer to wpa_supplicant data
1120 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001121void wpa_bss_flush(struct wpa_supplicant *wpa_s)
1122{
1123 struct wpa_bss *bss, *n;
1124
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001125 wpa_s->clear_driver_scan_cache = 1;
1126
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001127 if (wpa_s->bss.next == NULL)
1128 return; /* BSS table not yet initialized */
1129
1130 dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
1131 if (wpa_bss_in_use(wpa_s, bss))
1132 continue;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001133 wpa_bss_remove(wpa_s, bss, __func__);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001134 }
1135}
1136
1137
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001138/**
1139 * wpa_bss_deinit - Deinitialize BSS table
1140 * @wpa_s: Pointer to wpa_supplicant data
1141 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001142void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
1143{
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001144 wpa_bss_flush(wpa_s);
1145}
1146
1147
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001148/**
1149 * wpa_bss_get_bssid - Fetch a BSS table entry based on BSSID
1150 * @wpa_s: Pointer to wpa_supplicant data
1151 * @bssid: BSSID
1152 * Returns: Pointer to the BSS entry or %NULL if not found
1153 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001154struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
1155 const u8 *bssid)
1156{
1157 struct wpa_bss *bss;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001158 if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
1159 return NULL;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001160 dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001161 if (ether_addr_equal(bss->bssid, bssid))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001162 return bss;
1163 }
1164 return NULL;
1165}
1166
1167
Dmitry Shmidt444d5672013-04-01 13:08:44 -07001168/**
1169 * wpa_bss_get_bssid_latest - Fetch the latest BSS table entry based on BSSID
1170 * @wpa_s: Pointer to wpa_supplicant data
1171 * @bssid: BSSID
1172 * Returns: Pointer to the BSS entry or %NULL if not found
1173 *
1174 * This function is like wpa_bss_get_bssid(), but full BSS table is iterated to
1175 * find the entry that has the most recent update. This can help in finding the
1176 * correct entry in cases where the SSID of the AP may have changed recently
1177 * (e.g., in WPS reconfiguration cases).
1178 */
1179struct wpa_bss * wpa_bss_get_bssid_latest(struct wpa_supplicant *wpa_s,
1180 const u8 *bssid)
1181{
1182 struct wpa_bss *bss, *found = NULL;
1183 if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
1184 return NULL;
1185 dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001186 if (!ether_addr_equal(bss->bssid, bssid))
Dmitry Shmidt444d5672013-04-01 13:08:44 -07001187 continue;
1188 if (found == NULL ||
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001189 os_reltime_before(&found->last_update, &bss->last_update))
Dmitry Shmidt444d5672013-04-01 13:08:44 -07001190 found = bss;
1191 }
1192 return found;
1193}
1194
1195
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08001196#ifdef CONFIG_P2P
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001197/**
Hai Shalomc3565922019-10-28 11:58:20 -07001198 * wpa_bss_get_p2p_dev_addr - Fetch the latest BSS table entry based on P2P Device Addr
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001199 * @wpa_s: Pointer to wpa_supplicant data
1200 * @dev_addr: P2P Device Address of the GO
1201 * Returns: Pointer to the BSS entry or %NULL if not found
Hai Shalomc3565922019-10-28 11:58:20 -07001202 *
1203 * This function tries to find the entry that has the most recent update. This
1204 * can help in finding the correct entry in cases where the SSID of the P2P
1205 * Device may have changed recently.
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001206 */
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08001207struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s,
1208 const u8 *dev_addr)
1209{
Hai Shalomc3565922019-10-28 11:58:20 -07001210 struct wpa_bss *bss, *found = NULL;
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08001211 dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
1212 u8 addr[ETH_ALEN];
Hai Shalom60840252021-02-19 19:02:11 -08001213 if (p2p_parse_dev_addr(wpa_bss_ie_ptr(bss), bss->ie_len,
Hai Shalomc3565922019-10-28 11:58:20 -07001214 addr) != 0 ||
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001215 !ether_addr_equal(addr, dev_addr))
Hai Shalomc3565922019-10-28 11:58:20 -07001216 continue;
1217 if (!found ||
1218 os_reltime_before(&found->last_update, &bss->last_update))
1219 found = bss;
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08001220 }
Hai Shalomc3565922019-10-28 11:58:20 -07001221 return found;
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08001222}
1223#endif /* CONFIG_P2P */
1224
1225
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001226/**
1227 * wpa_bss_get_id - Fetch a BSS table entry based on identifier
1228 * @wpa_s: Pointer to wpa_supplicant data
1229 * @id: Unique identifier (struct wpa_bss::id) assigned for the entry
1230 * Returns: Pointer to the BSS entry or %NULL if not found
1231 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001232struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
1233{
1234 struct wpa_bss *bss;
1235 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
1236 if (bss->id == id)
1237 return bss;
1238 }
1239 return NULL;
1240}
1241
1242
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001243/**
Dmitry Shmidtf8623282013-02-20 14:34:59 -08001244 * wpa_bss_get_id_range - Fetch a BSS table entry based on identifier range
1245 * @wpa_s: Pointer to wpa_supplicant data
1246 * @idf: Smallest allowed identifier assigned for the entry
1247 * @idf: Largest allowed identifier assigned for the entry
1248 * Returns: Pointer to the BSS entry or %NULL if not found
1249 *
1250 * This function is similar to wpa_bss_get_id() but allows a BSS entry with the
1251 * smallest id value to be fetched within the specified range without the
1252 * caller having to know the exact id.
1253 */
1254struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s,
1255 unsigned int idf, unsigned int idl)
1256{
1257 struct wpa_bss *bss;
1258 dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
1259 if (bss->id >= idf && bss->id <= idl)
1260 return bss;
1261 }
1262 return NULL;
1263}
1264
1265
1266/**
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001267 * wpa_bss_get_ie - Fetch a specified information element from a BSS entry
1268 * @bss: BSS table entry
1269 * @ie: Information element identitifier (WLAN_EID_*)
1270 * Returns: Pointer to the information element (id field) or %NULL if not found
1271 *
1272 * This function returns the first matching information element in the BSS
1273 * entry.
1274 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001275const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie)
1276{
Hai Shalom60840252021-02-19 19:02:11 -08001277 return get_ie(wpa_bss_ie_ptr(bss), bss->ie_len, ie);
1278}
1279
1280
1281/**
Sunil Ravi7f769292024-07-23 22:21:32 +00001282 * wpa_bss_get_ie_beacon - Fetch a specified information element from a BSS entry
1283 * @bss: BSS table entry
1284 * @ie: Information element identitifier (WLAN_EID_*)
1285 * Returns: Pointer to the information element (id field) or %NULL if not found
1286 *
1287 * This function returns the first matching information element in the BSS
1288 * entry.
1289 *
1290 * This function is like wpa_bss_get_ie(), but uses IE buffer only from Beacon
1291 * frames instead of either Beacon or Probe Response frames.
1292 */
1293const u8 * wpa_bss_get_ie_beacon(const struct wpa_bss *bss, u8 ie)
1294{
1295 const u8 *ies;
1296
1297 if (bss->beacon_ie_len == 0)
1298 return NULL;
1299
1300 ies = wpa_bss_ie_ptr(bss);
1301 ies += bss->ie_len;
1302 return get_ie(ies, bss->beacon_ie_len, ie);
1303}
1304
1305
1306/**
Hai Shalom60840252021-02-19 19:02:11 -08001307 * wpa_bss_get_ie_ext - Fetch a specified extended IE from a BSS entry
1308 * @bss: BSS table entry
1309 * @ext: Information element extension identifier (WLAN_EID_EXT_*)
1310 * Returns: Pointer to the information element (id field) or %NULL if not found
1311 *
1312 * This function returns the first matching information element in the BSS
1313 * entry.
1314 */
1315const u8 * wpa_bss_get_ie_ext(const struct wpa_bss *bss, u8 ext)
1316{
1317 return get_ie_ext(wpa_bss_ie_ptr(bss), bss->ie_len, ext);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001318}
1319
1320
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001321/**
1322 * wpa_bss_get_vendor_ie - Fetch a vendor information element from a BSS entry
1323 * @bss: BSS table entry
1324 * @vendor_type: Vendor type (four octets starting the IE payload)
1325 * Returns: Pointer to the information element (id field) or %NULL if not found
1326 *
1327 * This function returns the first matching information element in the BSS
1328 * entry.
1329 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001330const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type)
1331{
Hai Shalom60840252021-02-19 19:02:11 -08001332 const u8 *ies;
1333 const struct element *elem;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001334
Hai Shalom60840252021-02-19 19:02:11 -08001335 ies = wpa_bss_ie_ptr(bss);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001336
Hai Shalom60840252021-02-19 19:02:11 -08001337 for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, bss->ie_len) {
1338 if (elem->datalen >= 4 &&
1339 vendor_type == WPA_GET_BE32(elem->data))
1340 return &elem->id;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001341 }
1342
1343 return NULL;
1344}
1345
1346
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001347/**
Dmitry Shmidt96571392013-10-14 12:54:46 -07001348 * wpa_bss_get_vendor_ie_beacon - Fetch a vendor information from a BSS entry
1349 * @bss: BSS table entry
1350 * @vendor_type: Vendor type (four octets starting the IE payload)
1351 * Returns: Pointer to the information element (id field) or %NULL if not found
1352 *
1353 * This function returns the first matching information element in the BSS
1354 * entry.
1355 *
1356 * This function is like wpa_bss_get_vendor_ie(), but uses IE buffer only
1357 * from Beacon frames instead of either Beacon or Probe Response frames.
1358 */
1359const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss,
1360 u32 vendor_type)
1361{
Hai Shalom60840252021-02-19 19:02:11 -08001362 const u8 *ies;
1363 const struct element *elem;
Dmitry Shmidt96571392013-10-14 12:54:46 -07001364
1365 if (bss->beacon_ie_len == 0)
1366 return NULL;
1367
Hai Shalom60840252021-02-19 19:02:11 -08001368 ies = wpa_bss_ie_ptr(bss);
1369 ies += bss->ie_len;
Dmitry Shmidt96571392013-10-14 12:54:46 -07001370
Hai Shalom60840252021-02-19 19:02:11 -08001371 for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies,
1372 bss->beacon_ie_len) {
1373 if (elem->datalen >= 4 &&
1374 vendor_type == WPA_GET_BE32(elem->data))
1375 return &elem->id;
Dmitry Shmidt96571392013-10-14 12:54:46 -07001376 }
1377
1378 return NULL;
1379}
1380
1381
1382/**
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001383 * wpa_bss_get_vendor_ie_multi - Fetch vendor IE data from a BSS entry
1384 * @bss: BSS table entry
1385 * @vendor_type: Vendor type (four octets starting the IE payload)
1386 * Returns: Pointer to the information element payload or %NULL if not found
1387 *
1388 * This function returns concatenated payload of possibly fragmented vendor
1389 * specific information elements in the BSS entry. The caller is responsible for
1390 * freeing the returned buffer.
1391 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001392struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
1393 u32 vendor_type)
1394{
1395 struct wpabuf *buf;
1396 const u8 *end, *pos;
1397
1398 buf = wpabuf_alloc(bss->ie_len);
1399 if (buf == NULL)
1400 return NULL;
1401
Hai Shalom60840252021-02-19 19:02:11 -08001402 pos = wpa_bss_ie_ptr(bss);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001403 end = pos + bss->ie_len;
1404
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001405 while (end - pos > 1) {
Hai Shalom60840252021-02-19 19:02:11 -08001406 u8 ie, len;
1407
1408 ie = pos[0];
1409 len = pos[1];
1410 if (len > end - pos - 2)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001411 break;
Hai Shalom60840252021-02-19 19:02:11 -08001412 pos += 2;
1413 if (ie == WLAN_EID_VENDOR_SPECIFIC && len >= 4 &&
1414 vendor_type == WPA_GET_BE32(pos))
1415 wpabuf_put_data(buf, pos + 4, len - 4);
1416 pos += len;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001417 }
1418
1419 if (wpabuf_len(buf) == 0) {
1420 wpabuf_free(buf);
1421 buf = NULL;
1422 }
1423
1424 return buf;
1425}
1426
1427
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001428/**
1429 * wpa_bss_get_vendor_ie_multi_beacon - Fetch vendor IE data from a BSS entry
1430 * @bss: BSS table entry
1431 * @vendor_type: Vendor type (four octets starting the IE payload)
1432 * Returns: Pointer to the information element payload or %NULL if not found
1433 *
1434 * This function returns concatenated payload of possibly fragmented vendor
1435 * specific information elements in the BSS entry. The caller is responsible for
1436 * freeing the returned buffer.
1437 *
1438 * This function is like wpa_bss_get_vendor_ie_multi(), but uses IE buffer only
1439 * from Beacon frames instead of either Beacon or Probe Response frames.
1440 */
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -07001441struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss,
1442 u32 vendor_type)
1443{
1444 struct wpabuf *buf;
1445 const u8 *end, *pos;
1446
1447 buf = wpabuf_alloc(bss->beacon_ie_len);
1448 if (buf == NULL)
1449 return NULL;
1450
Hai Shalom60840252021-02-19 19:02:11 -08001451 pos = wpa_bss_ie_ptr(bss);
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -07001452 pos += bss->ie_len;
1453 end = pos + bss->beacon_ie_len;
1454
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001455 while (end - pos > 1) {
Sunil8cd6f4d2022-06-28 18:40:46 +00001456 u8 id, len;
1457
1458 id = *pos++;
1459 len = *pos++;
1460 if (len > end - pos)
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -07001461 break;
Sunil8cd6f4d2022-06-28 18:40:46 +00001462 if (id == WLAN_EID_VENDOR_SPECIFIC && len >= 4 &&
1463 vendor_type == WPA_GET_BE32(pos))
1464 wpabuf_put_data(buf, pos + 4, len - 4);
1465 pos += len;
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -07001466 }
1467
1468 if (wpabuf_len(buf) == 0) {
1469 wpabuf_free(buf);
1470 buf = NULL;
1471 }
1472
1473 return buf;
1474}
1475
1476
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001477/**
1478 * wpa_bss_get_max_rate - Get maximum legacy TX rate supported in a BSS
1479 * @bss: BSS table entry
1480 * Returns: Maximum legacy rate in units of 500 kbps
1481 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001482int wpa_bss_get_max_rate(const struct wpa_bss *bss)
1483{
1484 int rate = 0;
1485 const u8 *ie;
1486 int i;
1487
1488 ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
1489 for (i = 0; ie && i < ie[1]; i++) {
1490 if ((ie[i + 2] & 0x7f) > rate)
1491 rate = ie[i + 2] & 0x7f;
1492 }
1493
1494 ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
1495 for (i = 0; ie && i < ie[1]; i++) {
1496 if ((ie[i + 2] & 0x7f) > rate)
1497 rate = ie[i + 2] & 0x7f;
1498 }
1499
1500 return rate;
1501}
1502
1503
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001504/**
1505 * wpa_bss_get_bit_rates - Get legacy TX rates supported in a BSS
1506 * @bss: BSS table entry
1507 * @rates: Buffer for returning a pointer to the rates list (units of 500 kbps)
1508 * Returns: number of legacy TX rates or -1 on failure
1509 *
1510 * The caller is responsible for freeing the returned buffer with os_free() in
1511 * case of success.
1512 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001513int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates)
1514{
1515 const u8 *ie, *ie2;
1516 int i, j;
1517 unsigned int len;
1518 u8 *r;
1519
1520 ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
1521 ie2 = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
1522
1523 len = (ie ? ie[1] : 0) + (ie2 ? ie2[1] : 0);
1524
1525 r = os_malloc(len);
1526 if (!r)
1527 return -1;
1528
1529 for (i = 0; ie && i < ie[1]; i++)
1530 r[i] = ie[i + 2] & 0x7f;
1531
1532 for (j = 0; ie2 && j < ie2[1]; j++)
1533 r[i + j] = ie2[j + 2] & 0x7f;
1534
1535 *rates = r;
1536 return len;
1537}
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001538
1539
1540#ifdef CONFIG_FILS
Hai Shalom60840252021-02-19 19:02:11 -08001541const u8 * wpa_bss_get_fils_cache_id(const struct wpa_bss *bss)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001542{
1543 const u8 *ie;
1544
1545 if (bss) {
1546 ie = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
1547 if (ie && ie[1] >= 4 && WPA_GET_LE16(ie + 2) & BIT(7))
1548 return ie + 4;
1549 }
1550
1551 return NULL;
1552}
1553#endif /* CONFIG_FILS */
Hai Shalom74f70d42019-02-11 14:42:39 -08001554
1555
1556int wpa_bss_ext_capab(const struct wpa_bss *bss, unsigned int capab)
1557{
Hai Shalom60840252021-02-19 19:02:11 -08001558 if (!bss)
1559 return 0;
Hai Shalom74f70d42019-02-11 14:42:39 -08001560 return ieee802_11_ext_capab(wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB),
1561 capab);
1562}
Sunil Ravi38ad1ed2023-01-17 23:58:31 +00001563
1564
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001565static void
1566wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s,
1567 struct wpa_bss *bss, u8 mbssid_idx,
1568 const struct ieee80211_neighbor_ap_info *ap_info,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001569 size_t len, u16 *seen, u16 *missing,
1570 struct wpa_ssid *ssid)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001571{
1572 const u8 *pos, *end;
1573 const u8 *mld_params;
1574 u8 count, mld_params_offset;
1575 u8 i, type, link_id;
1576
1577 count = RNR_TBTT_INFO_COUNT_VAL(ap_info->tbtt_info_hdr) + 1;
1578 type = ap_info->tbtt_info_hdr & RNR_TBTT_INFO_HDR_TYPE_MSK;
1579
1580 /* MLD information is at offset 13 or at start */
1581 if (type == 0 && ap_info->tbtt_info_len >= RNR_TBTT_INFO_MLD_LEN) {
1582 /* MLD info is appended */
1583 mld_params_offset = RNR_TBTT_INFO_LEN;
1584 } else {
1585 /* TODO: Support NSTR AP */
1586 return;
1587 }
1588
1589 pos = (const u8 *) ap_info;
1590 end = pos + len;
1591 pos += sizeof(*ap_info);
1592
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00001593 for (i = 0; i < count; i++, pos += ap_info->tbtt_info_len) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001594 u8 bss_params;
1595
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001596 if (end - pos < ap_info->tbtt_info_len)
1597 break;
1598
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001599 bss_params = pos[1 + ETH_ALEN + 4];
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001600 mld_params = pos + mld_params_offset;
1601
1602 link_id = *(mld_params + 1) & EHT_ML_LINK_ID_MSK;
Sunil Ravi99c035e2024-07-12 01:42:03 +00001603 if (link_id >= MAX_NUM_MLD_LINKS)
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00001604 continue;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001605
1606 if (*mld_params != mbssid_idx) {
1607 wpa_printf(MSG_DEBUG,
1608 "MLD: Reported link not part of MLD");
1609 } else if (!(BIT(link_id) & *seen)) {
Sunil Ravi7f769292024-07-23 22:21:32 +00001610 struct wpa_bss *neigh_bss;
1611
1612 if (ssid && ssid->ssid_len)
1613 neigh_bss = wpa_bss_get(wpa_s, pos + 1,
1614 ssid->ssid,
1615 ssid->ssid_len);
1616 else
1617 neigh_bss = wpa_bss_get_bssid(wpa_s, pos + 1);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001618
1619 *seen |= BIT(link_id);
1620 wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link ID=%u",
1621 *mld_params, link_id);
1622
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001623 if (!neigh_bss) {
1624 *missing |= BIT(link_id);
1625 } else if ((!ssid ||
1626 (bss_params & (RNR_BSS_PARAM_SAME_SSID |
1627 RNR_BSS_PARAM_CO_LOCATED)) ||
1628 wpa_scan_res_match(wpa_s, 0, neigh_bss,
1629 ssid, 1, 0)) &&
1630 !wpa_bssid_ignore_is_listed(
1631 wpa_s, neigh_bss->bssid)) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001632 struct mld_link *l;
1633
Sunil Ravi99c035e2024-07-12 01:42:03 +00001634 bss->valid_links |= BIT(link_id);
1635 l = &bss->mld_links[link_id];
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001636 os_memcpy(l->bssid, pos + 1, ETH_ALEN);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001637 l->freq = neigh_bss->freq;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001638 l->disabled = mld_params[2] &
1639 RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001640 }
1641 }
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001642 }
1643}
1644
1645
1646/**
1647 * wpa_bss_parse_basic_ml_element - Parse the Basic Multi-Link element
1648 * @wpa_s: Pointer to wpa_supplicant data
1649 * @bss: BSS table entry
1650 * @mld_addr: AP MLD address (or %NULL)
1651 * @link_info: Array to store link information (or %NULL),
1652 * should be initialized and #MAX_NUM_MLD_LINKS elements long
1653 * @missing_links: Result bitmask of links that were not discovered (or %NULL)
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001654 * @ssid: Target SSID (or %NULL)
1655 * @ap_mld_id: On return would hold the corresponding AP MLD ID (or %NULL)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001656 * Returns: 0 on success or -1 for non-MLD or parsing failures
1657 *
1658 * Parses the Basic Multi-Link element of the BSS into @link_info using the scan
1659 * information stored in the wpa_supplicant data to fill in information for
1660 * links where possible. The @missing_links out parameter will contain any links
1661 * for which no corresponding BSS was found.
1662 */
1663int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
1664 struct wpa_bss *bss,
1665 u8 *ap_mld_addr,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001666 u16 *missing_links,
1667 struct wpa_ssid *ssid,
1668 u8 *ap_mld_id)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001669{
1670 struct ieee802_11_elems elems;
1671 struct wpabuf *mlbuf;
1672 const struct element *elem;
1673 u8 mbssid_idx = 0;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001674 size_t ml_ie_len;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001675 const struct ieee80211_eht_ml *eht_ml;
1676 const struct eht_ml_basic_common_info *ml_basic_common_info;
1677 u8 i, link_id;
1678 const u16 control_mask =
1679 MULTI_LINK_CONTROL_TYPE_MASK |
1680 BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
1681 BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
1682 BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA;
1683 const u16 control =
1684 MULTI_LINK_CONTROL_TYPE_BASIC |
1685 BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
1686 BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
1687 BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA;
1688 u16 missing = 0;
1689 u16 seen;
1690 const u8 *ies_pos = wpa_bss_ie_ptr(bss);
1691 size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len;
1692 int ret = -1;
1693 struct mld_link *l;
1694
1695 if (ieee802_11_parse_elems(ies_pos, ies_len, &elems, 1) ==
1696 ParseFailed) {
1697 wpa_dbg(wpa_s, MSG_DEBUG, "MLD: Failed to parse elements");
1698 return ret;
1699 }
1700
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001701 mlbuf = ieee802_11_defrag(elems.basic_mle, elems.basic_mle_len, true);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001702 if (!mlbuf) {
1703 wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No Multi-Link element");
1704 return ret;
1705 }
1706
1707 ml_ie_len = wpabuf_len(mlbuf);
1708
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001709 if (ssid) {
1710 struct wpa_ie_data ie;
Sunil Ravi7f769292024-07-23 22:21:32 +00001711 const u8 *rsne;
1712 size_t rsne_len;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001713
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00001714 if (elems.rsne_override_2 && wpas_rsn_overriding(wpa_s, ssid)) {
Sunil Ravi7f769292024-07-23 22:21:32 +00001715 rsne = elems.rsne_override_2;
1716 rsne_len = elems.rsne_override_2_len;
1717 } else if (elems.rsne_override &&
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00001718 wpas_rsn_overriding(wpa_s, ssid)) {
Sunil Ravi7f769292024-07-23 22:21:32 +00001719 rsne = elems.rsne_override;
1720 rsne_len = elems.rsne_override_len;
1721 } else {
1722 rsne = elems.rsn_ie;
1723 rsne_len = elems.rsn_ie_len;
1724 }
1725 if (!rsne ||
1726 wpa_parse_wpa_ie(rsne - 2, 2 + rsne_len, &ie)) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001727 wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No RSN element");
1728 goto out;
1729 }
1730
1731 if (!(ie.capabilities & WPA_CAPABILITY_MFPC) ||
1732 wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION) {
1733 wpa_dbg(wpa_s, MSG_DEBUG,
1734 "MLD: No management frame protection");
1735 goto out;
1736 }
1737
1738 ie.key_mgmt &= ~(WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK |
1739 WPA_KEY_MGMT_PSK_SHA256);
1740 if (!(ie.key_mgmt & ssid->key_mgmt)) {
1741 wpa_dbg(wpa_s, MSG_DEBUG,
1742 "MLD: No valid key management");
1743 goto out;
1744 }
1745 }
1746
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001747 /*
1748 * for ext ID + 2 control + common info len + MLD address +
1749 * link info
1750 */
1751 if (ml_ie_len < 2UL + 1UL + ETH_ALEN + 1UL)
1752 goto out;
1753
1754 eht_ml = (const struct ieee80211_eht_ml *) wpabuf_head(mlbuf);
1755 if ((le_to_host16(eht_ml->ml_control) & control_mask) != control) {
1756 wpa_printf(MSG_DEBUG,
1757 "MLD: Unexpected Multi-Link element control=0x%x (mask 0x%x expected 0x%x)",
1758 le_to_host16(eht_ml->ml_control), control_mask,
1759 control);
1760 goto out;
1761 }
1762
1763 ml_basic_common_info =
1764 (const struct eht_ml_basic_common_info *) eht_ml->variable;
1765
1766 /* Common info length should be valid */
1767 if (ml_basic_common_info->len < ETH_ALEN + 1UL)
1768 goto out;
1769
1770 /* Get the MLD address and MLD link ID */
1771 if (ap_mld_addr)
1772 os_memcpy(ap_mld_addr, ml_basic_common_info->mld_addr,
1773 ETH_ALEN);
1774
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001775 link_id = ml_basic_common_info->variable[0] & EHT_ML_LINK_ID_MSK;
Sunil Ravi99c035e2024-07-12 01:42:03 +00001776
1777 bss->mld_link_id = link_id;
1778 seen = bss->valid_links = BIT(link_id);
1779
1780 l = &bss->mld_links[link_id];
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001781 os_memcpy(l->bssid, bss->bssid, ETH_ALEN);
1782 l->freq = bss->freq;
1783
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001784
1785 /*
1786 * The AP MLD ID in the RNR corresponds to the MBSSID index, see
1787 * IEEE P802.11be/D4.0, 9.4.2.169.2 (Neighbor AP Information field).
1788 *
1789 * For the transmitting BSSID it is clear that both the MBSSID index
1790 * and the AP MLD ID in the RNR are zero.
1791 *
1792 * For nontransmitted BSSIDs we will have a BSS generated from the
1793 * MBSSID element(s) using inheritance rules. Included in the elements
1794 * is the MBSSID Index Element. The RNR is copied from the Beacon/Probe
1795 * Response frame that was send by the transmitting BSSID. As such, the
1796 * reported AP MLD ID in the RNR will match the value in the MBSSID
1797 * Index Element.
1798 */
1799 elem = (const struct element *)
1800 wpa_bss_get_ie(bss, WLAN_EID_MULTIPLE_BSSID_INDEX);
1801 if (elem && elem->datalen >= 1)
1802 mbssid_idx = elem->data[0];
1803
1804 for_each_element_id(elem, WLAN_EID_REDUCED_NEIGHBOR_REPORT,
1805 wpa_bss_ie_ptr(bss),
1806 bss->ie_len ? bss->ie_len : bss->beacon_ie_len) {
1807 const struct ieee80211_neighbor_ap_info *ap_info;
1808 const u8 *pos = elem->data;
1809 size_t len = elem->datalen;
1810
1811 /* RNR IE may contain more than one Neighbor AP Info */
1812 while (sizeof(*ap_info) <= len) {
1813 size_t ap_info_len = sizeof(*ap_info);
1814 u8 count;
1815
1816 ap_info = (const struct ieee80211_neighbor_ap_info *)
1817 pos;
1818 count = RNR_TBTT_INFO_COUNT_VAL(ap_info->tbtt_info_hdr) + 1;
1819 ap_info_len += count * ap_info->tbtt_info_len;
1820
1821 if (ap_info_len > len)
1822 goto out;
1823
1824 wpa_bss_parse_ml_rnr_ap_info(wpa_s, bss, mbssid_idx,
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00001825 ap_info, ap_info_len,
1826 &seen, &missing, ssid);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001827
1828 pos += ap_info_len;
1829 len -= ap_info_len;
1830 }
1831 }
1832
Sunil Ravi99c035e2024-07-12 01:42:03 +00001833 wpa_printf(MSG_DEBUG, "MLD: valid_links=%04hx (unresolved: 0x%04hx)",
1834 bss->valid_links, missing);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001835
Sunil Ravi99c035e2024-07-12 01:42:03 +00001836 for_each_link(bss->valid_links, i) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001837 wpa_printf(MSG_DEBUG, "MLD: link=%u, bssid=" MACSTR,
Sunil Ravi99c035e2024-07-12 01:42:03 +00001838 i, MAC2STR(bss->mld_links[i].bssid));
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001839 }
1840
1841 if (missing_links)
1842 *missing_links = missing;
1843
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001844 if (ap_mld_id)
1845 *ap_mld_id = mbssid_idx;
1846
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001847 ret = 0;
1848out:
1849 wpabuf_free(mlbuf);
1850 return ret;
1851}
1852
1853
1854/*
1855 * wpa_bss_parse_reconf_ml_element - Parse the Reconfiguration ML element
1856 * @wpa_s: Pointer to wpa_supplicant data
1857 * @bss: BSS table entry
1858 * Returns: The bitmap of links that are going to be removed
1859 */
1860u16 wpa_bss_parse_reconf_ml_element(struct wpa_supplicant *wpa_s,
1861 struct wpa_bss *bss)
1862{
1863 struct ieee802_11_elems elems;
1864 struct wpabuf *mlbuf;
1865 const u8 *pos = wpa_bss_ie_ptr(bss);
1866 size_t len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len;
1867 const struct ieee80211_eht_ml *ml;
1868 u16 removed_links = 0;
1869 u8 ml_common_len;
1870
1871 if (ieee802_11_parse_elems(pos, len, &elems, 1) == ParseFailed)
1872 return 0;
1873
1874 if (!elems.reconf_mle || !elems.reconf_mle_len)
1875 return 0;
1876
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001877 mlbuf = ieee802_11_defrag(elems.reconf_mle, elems.reconf_mle_len, true);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001878 if (!mlbuf)
1879 return 0;
1880
1881 ml = (const struct ieee80211_eht_ml *) wpabuf_head(mlbuf);
1882 len = wpabuf_len(mlbuf);
1883
1884 if (len < sizeof(*ml))
1885 goto out;
1886
1887 ml_common_len = 1;
1888 if (ml->ml_control & RECONF_MULTI_LINK_CTRL_PRES_MLD_MAC_ADDR)
1889 ml_common_len += ETH_ALEN;
1890
1891 if (len < sizeof(*ml) + ml_common_len) {
1892 wpa_printf(MSG_DEBUG,
1893 "MLD: Unexpected Reconfiguration ML element length: (%zu < %zu)",
1894 len, sizeof(*ml) + ml_common_len);
1895 goto out;
1896 }
1897
1898 pos = ml->variable + ml_common_len;
1899 len -= sizeof(*ml) + ml_common_len;
1900
1901 while (len >= 2 + sizeof(struct ieee80211_eht_per_sta_profile)) {
1902 size_t sub_elem_len = *(pos + 1);
1903
1904 if (2 + sub_elem_len > len) {
1905 wpa_printf(MSG_DEBUG,
1906 "MLD: Invalid link info len: %zu %zu",
1907 2 + sub_elem_len, len);
1908 goto out;
1909 }
1910
1911 if (*pos == EHT_ML_SUB_ELEM_PER_STA_PROFILE) {
1912 const struct ieee80211_eht_per_sta_profile *sta_prof =
1913 (const struct ieee80211_eht_per_sta_profile *)
1914 (pos + 2);
1915 u16 control = le_to_host16(sta_prof->sta_control);
1916 u8 link_id;
1917
1918 link_id = control & EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK;
1919 removed_links |= BIT(link_id);
1920 }
1921
1922 pos += 2 + sub_elem_len;
1923 len -= 2 + sub_elem_len;
1924 }
1925
1926 wpa_printf(MSG_DEBUG, "MLD: Reconfiguration: removed_links=0x%x",
1927 removed_links);
1928out:
1929 wpabuf_free(mlbuf);
1930 return removed_links;
1931}
Sunil Ravi7f769292024-07-23 22:21:32 +00001932
1933
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00001934#ifndef CONFIG_NO_WPA
1935
Sunil Ravi7f769292024-07-23 22:21:32 +00001936static bool wpa_bss_supported_cipher(struct wpa_supplicant *wpa_s,
1937 int pairwise_cipher)
1938{
1939 if (!wpa_s->drv_enc)
1940 return true;
1941
1942 if ((pairwise_cipher & WPA_CIPHER_CCMP) &&
1943 (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_CCMP))
1944 return true;
1945
1946 if ((pairwise_cipher & WPA_CIPHER_GCMP) &&
1947 (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP))
1948 return true;
1949
1950 if ((pairwise_cipher & WPA_CIPHER_CCMP_256) &&
1951 (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_CCMP_256))
1952 return true;
1953
1954 if ((pairwise_cipher & WPA_CIPHER_GCMP_256) &&
1955 (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP_256))
1956 return true;
1957
1958 return false;
1959}
1960
1961
1962static bool wpa_bss_supported_key_mgmt(struct wpa_supplicant *wpa_s,
1963 int key_mgmt)
1964{
1965 if (!wpa_s->drv_key_mgmt)
1966 return true;
1967
1968 if ((key_mgmt & WPA_KEY_MGMT_IEEE8021X) &&
1969 (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA2))
1970 return true;
1971 if ((key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) &&
1972 (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_802_1X_SHA256))
1973 return true;
1974 if ((key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) &&
1975 (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT))
1976 return true;
1977 if ((key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) &&
1978 (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384))
1979 return true;
1980 if ((key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) &&
1981 (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B))
1982 return true;
1983 if ((key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) &&
1984 (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192))
1985 return true;
1986 if ((key_mgmt & WPA_KEY_MGMT_PSK) &&
1987 (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK))
1988 return true;
1989 if ((key_mgmt & WPA_KEY_MGMT_FT_PSK) &&
1990 (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK))
1991 return true;
1992 if ((key_mgmt & WPA_KEY_MGMT_PSK_SHA256) &&
1993 (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_PSK_SHA256))
1994 return true;
1995 if ((key_mgmt & WPA_KEY_MGMT_SAE) &&
1996 (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE))
1997 return true;
1998 if ((key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) &&
1999 (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY))
2000 return true;
2001 if ((key_mgmt & WPA_KEY_MGMT_FT_SAE) &&
2002 (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE))
2003 return true;
2004 if ((key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) &&
2005 (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE_EXT_KEY))
2006 return true;
2007 if ((key_mgmt & WPA_KEY_MGMT_OWE) &&
2008 (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_OWE))
2009 return true;
2010 if ((key_mgmt & WPA_KEY_MGMT_DPP) &&
2011 (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_DPP))
2012 return true;
2013 if ((key_mgmt & WPA_KEY_MGMT_FILS_SHA256) &&
2014 (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256))
2015 return true;
2016 if ((key_mgmt & WPA_KEY_MGMT_FILS_SHA384) &&
2017 (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384))
2018 return true;
2019 if ((key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) &&
2020 (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256))
2021 return true;
2022 if ((key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) &&
2023 (wpa_s->drv_key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384))
2024 return true;
2025
2026 return false;
2027}
2028
2029
2030static bool wpa_bss_supported_rsne(struct wpa_supplicant *wpa_s,
2031 struct wpa_ssid *ssid, const u8 *ie)
2032{
2033 struct wpa_ie_data data;
2034
2035 if (wpa_parse_wpa_ie_rsn(ie, 2 + ie[1], &data) < 0)
2036 return false;
2037
2038 /* Check that there is a supported AKM and pairwise cipher based on
2039 * overall capabilities */
2040 if (!data.pairwise_cipher || !data.key_mgmt)
2041 return false;
2042
2043 if (wpa_s->drv_capa_known) {
2044 if (!wpa_bss_supported_cipher(wpa_s, data.pairwise_cipher) ||
2045 !wpa_bss_supported_key_mgmt(wpa_s, data.key_mgmt))
2046 return false;
2047 }
2048
2049 if (ssid) {
2050 /* Check that there is a supported AKM and pairwise cipher
2051 * based on the specific network profile. */
2052 if ((ssid->pairwise_cipher & data.pairwise_cipher) == 0)
2053 return false;
2054 if ((ssid->key_mgmt & data.key_mgmt) == 0)
2055 return false;
2056 }
2057
2058 return true;
2059}
2060
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00002061#endif /* CONFIG_NO_WPA */
2062
Sunil Ravi7f769292024-07-23 22:21:32 +00002063
2064const u8 * wpa_bss_get_rsne(struct wpa_supplicant *wpa_s,
2065 const struct wpa_bss *bss, struct wpa_ssid *ssid,
2066 bool mlo)
2067{
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00002068#ifndef CONFIG_NO_WPA
Sunil Ravi7f769292024-07-23 22:21:32 +00002069 const u8 *ie;
2070
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00002071 if (wpas_rsn_overriding(wpa_s, ssid)) {
Sunil Ravi7f769292024-07-23 22:21:32 +00002072 if (!ssid)
2073 ssid = wpa_s->current_ssid;
2074
2075 /* MLO cases for RSN overriding are required to use RSNE
2076 * Override 2 element and RSNXE Override element together. */
2077 ie = wpa_bss_get_vendor_ie(bss, RSNE_OVERRIDE_2_IE_VENDOR_TYPE);
2078 if (mlo && ie &&
2079 !wpa_bss_get_vendor_ie(bss,
2080 RSNXE_OVERRIDE_IE_VENDOR_TYPE)) {
2081 wpa_printf(MSG_DEBUG, "BSS " MACSTR
2082 " advertises RSNE Override 2 element without RSNXE Override element - ignore RSNE Override 2 element for MLO",
2083 MAC2STR(bss->bssid));
2084 } else if (ie && wpa_bss_supported_rsne(wpa_s, ssid, ie)) {
2085 return ie;
2086 }
2087
2088 if (!mlo) {
2089 ie = wpa_bss_get_vendor_ie(
2090 bss, RSNE_OVERRIDE_IE_VENDOR_TYPE);
2091 if (ie && wpa_bss_supported_rsne(wpa_s, ssid, ie))
2092 return ie;
2093 }
2094 }
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00002095#endif /* CONFIG_NO_WPA */
Sunil Ravi7f769292024-07-23 22:21:32 +00002096
2097 return wpa_bss_get_ie(bss, WLAN_EID_RSN);
2098}
2099
2100
2101const u8 * wpa_bss_get_rsnxe(struct wpa_supplicant *wpa_s,
2102 const struct wpa_bss *bss, struct wpa_ssid *ssid,
2103 bool mlo)
2104{
2105 const u8 *ie;
2106
Sunil Ravi79e6c4f2025-01-04 00:47:06 +00002107 if (wpas_rsn_overriding(wpa_s, ssid)) {
Sunil Ravi7f769292024-07-23 22:21:32 +00002108 ie = wpa_bss_get_vendor_ie(bss, RSNXE_OVERRIDE_IE_VENDOR_TYPE);
2109 if (ie) {
2110 const u8 *tmp;
2111
2112 tmp = wpa_bss_get_rsne(wpa_s, bss, ssid, mlo);
2113 if (!tmp || tmp[0] == WLAN_EID_RSN) {
2114 /* An acceptable RSNE override element was not
2115 * found, so need to ignore RSNXE overriding. */
2116 goto out;
2117 }
2118
2119 return ie;
2120 }
2121
2122 /* MLO cases for RSN overriding are required to use RSNE
2123 * Override 2 element and RSNXE Override element together. */
2124 if (mlo && wpa_bss_get_vendor_ie(
2125 bss, RSNE_OVERRIDE_2_IE_VENDOR_TYPE)) {
2126 wpa_printf(MSG_DEBUG, "BSS " MACSTR
2127 " advertises RSNXE Override element without RSNE Override 2 element - ignore RSNXE Override element for MLO",
2128 MAC2STR(bss->bssid));
2129 goto out;
2130 }
2131 }
2132
2133out:
2134 return wpa_bss_get_ie(bss, WLAN_EID_RSNX);
2135}