blob: 22b694cf377b327ae9150b4610040619fdccc7b2 [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
276
Dmitry Shmidt29333592017-01-09 12:27:11 -0800277void calculate_update_time(const struct os_reltime *fetch_time,
278 unsigned int age_ms,
279 struct os_reltime *update_time)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700280{
281 os_time_t usec;
282
Dmitry Shmidt444d5672013-04-01 13:08:44 -0700283 update_time->sec = fetch_time->sec;
284 update_time->usec = fetch_time->usec;
285 update_time->sec -= age_ms / 1000;
286 usec = (age_ms % 1000) * 1000;
287 if (update_time->usec < usec) {
288 update_time->sec--;
289 update_time->usec += 1000000;
290 }
291 update_time->usec -= usec;
292}
293
294
295static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src,
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800296 struct os_reltime *fetch_time)
Dmitry Shmidt444d5672013-04-01 13:08:44 -0700297{
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700298 dst->flags = src->flags;
299 os_memcpy(dst->bssid, src->bssid, ETH_ALEN);
300 dst->freq = src->freq;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000301 dst->max_cw = src->max_cw;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700302 dst->beacon_int = src->beacon_int;
303 dst->caps = src->caps;
304 dst->qual = src->qual;
305 dst->noise = src->noise;
306 dst->level = src->level;
307 dst->tsf = src->tsf;
Sunil Ravia04bd252022-05-02 22:54:18 -0700308 dst->beacon_newer = src->beacon_newer;
Dmitry Shmidt7f656022015-02-25 14:36:37 -0800309 dst->est_throughput = src->est_throughput;
310 dst->snr = src->snr;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700311
Dmitry Shmidt444d5672013-04-01 13:08:44 -0700312 calculate_update_time(fetch_time, src->age, &dst->last_update);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700313}
314
315
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -0700316static int wpa_bss_is_wps_candidate(struct wpa_supplicant *wpa_s,
317 struct wpa_bss *bss)
318{
319#ifdef CONFIG_WPS
320 struct wpa_ssid *ssid;
321 struct wpabuf *wps_ie;
322 int pbc = 0, ret;
323
324 wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
325 if (!wps_ie)
326 return 0;
327
328 if (wps_is_selected_pbc_registrar(wps_ie)) {
329 pbc = 1;
330 } else if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) {
331 wpabuf_free(wps_ie);
332 return 0;
333 }
334
335 for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
336 if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
337 continue;
338 if (ssid->ssid_len &&
339 (ssid->ssid_len != bss->ssid_len ||
340 os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) != 0))
341 continue;
342
343 if (pbc)
344 ret = eap_is_wps_pbc_enrollee(&ssid->eap);
345 else
346 ret = eap_is_wps_pin_enrollee(&ssid->eap);
347 wpabuf_free(wps_ie);
348 return ret;
349 }
350 wpabuf_free(wps_ie);
351#endif /* CONFIG_WPS */
352
353 return 0;
354}
355
356
Hai Shalom60840252021-02-19 19:02:11 -0800357static bool is_p2p_pending_bss(struct wpa_supplicant *wpa_s,
358 struct wpa_bss *bss)
359{
360#ifdef CONFIG_P2P
361 u8 addr[ETH_ALEN];
362
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000363 if (ether_addr_equal(bss->bssid, wpa_s->pending_join_iface_addr))
Hai Shalom60840252021-02-19 19:02:11 -0800364 return true;
365 if (!is_zero_ether_addr(wpa_s->pending_join_dev_addr) &&
366 p2p_parse_dev_addr(wpa_bss_ie_ptr(bss), bss->ie_len, addr) == 0 &&
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000367 ether_addr_equal(addr, wpa_s->pending_join_dev_addr))
Hai Shalom60840252021-02-19 19:02:11 -0800368 return true;
369#endif /* CONFIG_P2P */
370 return false;
371}
372
373
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800374static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
375{
376 struct wpa_ssid *ssid;
377
Hai Shalom60840252021-02-19 19:02:11 -0800378 if (is_p2p_pending_bss(wpa_s, bss))
379 return 1;
380
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800381 for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
382 if (ssid->ssid == NULL || ssid->ssid_len == 0)
383 continue;
384 if (ssid->ssid_len == bss->ssid_len &&
385 os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) == 0)
386 return 1;
387 }
388
389 return 0;
390}
391
392
Dmitry Shmidt04949592012-07-19 12:16:46 -0700393static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
394{
Sunil Ravi89eba102022-09-13 21:04:37 -0700395 int i;
396
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800397 if (bss == wpa_s->current_bss)
398 return 1;
399
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000400 if (bss == wpa_s->ml_connect_probe_bss)
401 return 1;
402
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800403 if (wpa_s->current_bss &&
404 (bss->ssid_len != wpa_s->current_bss->ssid_len ||
405 os_memcmp(bss->ssid, wpa_s->current_bss->ssid,
406 bss->ssid_len) != 0))
407 return 0; /* SSID has changed */
408
Sunil Ravi89eba102022-09-13 21:04:37 -0700409 if (!is_zero_ether_addr(bss->bssid) &&
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000410 (ether_addr_equal(bss->bssid, wpa_s->bssid) ||
411 ether_addr_equal(bss->bssid, wpa_s->pending_bssid)))
Sunil Ravi89eba102022-09-13 21:04:37 -0700412 return 1;
413
414 if (!wpa_s->valid_links)
415 return 0;
416
Sunil Ravi88611412024-06-28 17:34:56 +0000417 for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
418 if (!(wpa_s->valid_links & BIT(i)))
419 continue;
420
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000421 if (ether_addr_equal(bss->bssid, wpa_s->links[i].bssid))
Sunil Ravi89eba102022-09-13 21:04:37 -0700422 return 1;
423 }
424
425 return 0;
Dmitry Shmidt04949592012-07-19 12:16:46 -0700426}
427
428
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800429static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s)
430{
431 struct wpa_bss *bss;
432
433 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
Dmitry Shmidtd5ab1b52016-06-21 12:38:41 -0700434 if (!wpa_bss_known(wpa_s, bss) &&
435 !wpa_bss_is_wps_candidate(wpa_s, bss)) {
Dmitry Shmidt04949592012-07-19 12:16:46 -0700436 wpa_bss_remove(wpa_s, bss, __func__);
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800437 return 0;
438 }
439 }
440
441 return -1;
442}
443
444
Dmitry Shmidt04949592012-07-19 12:16:46 -0700445static int wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s)
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800446{
Dmitry Shmidt04949592012-07-19 12:16:46 -0700447 struct wpa_bss *bss;
448
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800449 /*
450 * Remove the oldest entry that does not match with any configured
451 * network.
452 */
453 if (wpa_bss_remove_oldest_unknown(wpa_s) == 0)
Dmitry Shmidt04949592012-07-19 12:16:46 -0700454 return 0;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800455
456 /*
Dmitry Shmidt04949592012-07-19 12:16:46 -0700457 * Remove the oldest entry that isn't currently in use.
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800458 */
Dmitry Shmidt04949592012-07-19 12:16:46 -0700459 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
460 if (!wpa_bss_in_use(wpa_s, bss)) {
461 wpa_bss_remove(wpa_s, bss, __func__);
462 return 0;
463 }
464 }
465
466 return -1;
Dmitry Shmidt1f69aa52012-01-24 16:10:04 -0800467}
468
469
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700470static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
471 const u8 *ssid, size_t ssid_len,
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800472 struct wpa_scan_res *res,
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800473 struct os_reltime *fetch_time)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700474{
475 struct wpa_bss *bss;
Sunil Ravi89eba102022-09-13 21:04:37 -0700476 char extra[100];
477 const u8 *ml_ie;
478 char *pos, *end;
479 int ret = 0;
480 const u8 *mld_addr;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700481
482 bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len);
483 if (bss == NULL)
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700484 return NULL;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700485 bss->id = wpa_s->bss_next_id++;
486 bss->last_update_idx = wpa_s->bss_update_idx;
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800487 wpa_bss_copy_res(bss, res, fetch_time);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700488 os_memcpy(bss->ssid, ssid, ssid_len);
489 bss->ssid_len = ssid_len;
490 bss->ie_len = res->ie_len;
491 bss->beacon_ie_len = res->beacon_ie_len;
Hai Shalom60840252021-02-19 19:02:11 -0800492 os_memcpy(bss->ies, res + 1, res->ie_len + res->beacon_ie_len);
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700493 wpa_bss_set_hessid(bss);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700494
Sunil Ravi89eba102022-09-13 21:04:37 -0700495 os_memset(bss->mld_addr, 0, ETH_ALEN);
496 ml_ie = wpa_scan_get_ml_ie(res, MULTI_LINK_CONTROL_TYPE_BASIC);
497 if (ml_ie) {
498 mld_addr = get_basic_mle_mld_addr(&ml_ie[3], ml_ie[1] - 1);
499 if (mld_addr)
500 os_memcpy(bss->mld_addr, mld_addr, ETH_ALEN);
501 }
502
Jouni Malinen7a6c8302013-09-27 15:47:09 +0300503 if (wpa_s->num_bss + 1 > wpa_s->conf->bss_max_count &&
504 wpa_bss_remove_oldest(wpa_s) != 0) {
505 wpa_printf(MSG_ERROR, "Increasing the MAX BSS count to %d "
506 "because all BSSes are in use. We should normally "
507 "not get here!", (int) wpa_s->num_bss + 1);
508 wpa_s->conf->bss_max_count = wpa_s->num_bss + 1;
509 }
510
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700511 dl_list_add_tail(&wpa_s->bss, &bss->list);
512 dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
513 wpa_s->num_bss++;
Sunil Ravi89eba102022-09-13 21:04:37 -0700514
515 extra[0] = '\0';
516 pos = extra;
517 end = pos + sizeof(extra);
Hai Shalom81f62d82019-07-22 12:10:00 -0700518 if (!is_zero_ether_addr(bss->hessid))
Sunil Ravi89eba102022-09-13 21:04:37 -0700519 ret = os_snprintf(pos, end - pos, " HESSID " MACSTR,
520 MAC2STR(bss->hessid));
521
522 if (!is_zero_ether_addr(bss->mld_addr) &&
523 !os_snprintf_error(end - pos, ret)) {
524 pos += ret;
525 ret = os_snprintf(pos, end - pos, " MLD ADDR " MACSTR,
526 MAC2STR(bss->mld_addr));
527 }
528
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700529 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR
Hai Shalom81f62d82019-07-22 12:10:00 -0700530 " SSID '%s' freq %d%s",
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800531 bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len),
Hai Shalom81f62d82019-07-22 12:10:00 -0700532 bss->freq, extra);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700533 wpas_notify_bss_added(wpa_s, bss->bssid, bss->id);
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700534 return bss;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700535}
536
537
538static int are_ies_equal(const struct wpa_bss *old,
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700539 const struct wpa_scan_res *new_res, u32 ie)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700540{
541 const u8 *old_ie, *new_ie;
542 struct wpabuf *old_ie_buff = NULL;
543 struct wpabuf *new_ie_buff = NULL;
544 int new_ie_len, old_ie_len, ret, is_multi;
545
546 switch (ie) {
547 case WPA_IE_VENDOR_TYPE:
548 old_ie = wpa_bss_get_vendor_ie(old, ie);
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700549 new_ie = wpa_scan_get_vendor_ie(new_res, ie);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700550 is_multi = 0;
551 break;
552 case WPS_IE_VENDOR_TYPE:
553 old_ie_buff = wpa_bss_get_vendor_ie_multi(old, ie);
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700554 new_ie_buff = wpa_scan_get_vendor_ie_multi(new_res, ie);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700555 is_multi = 1;
556 break;
557 case WLAN_EID_RSN:
558 case WLAN_EID_SUPP_RATES:
559 case WLAN_EID_EXT_SUPP_RATES:
560 old_ie = wpa_bss_get_ie(old, ie);
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700561 new_ie = wpa_scan_get_ie(new_res, ie);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700562 is_multi = 0;
563 break;
564 default:
565 wpa_printf(MSG_DEBUG, "bss: %s: cannot compare IEs", __func__);
566 return 0;
567 }
568
569 if (is_multi) {
570 /* in case of multiple IEs stored in buffer */
571 old_ie = old_ie_buff ? wpabuf_head_u8(old_ie_buff) : NULL;
572 new_ie = new_ie_buff ? wpabuf_head_u8(new_ie_buff) : NULL;
573 old_ie_len = old_ie_buff ? wpabuf_len(old_ie_buff) : 0;
574 new_ie_len = new_ie_buff ? wpabuf_len(new_ie_buff) : 0;
575 } else {
576 /* in case of single IE */
577 old_ie_len = old_ie ? old_ie[1] + 2 : 0;
578 new_ie_len = new_ie ? new_ie[1] + 2 : 0;
579 }
580
581 if (!old_ie || !new_ie)
582 ret = !old_ie && !new_ie;
583 else
584 ret = (old_ie_len == new_ie_len &&
585 os_memcmp(old_ie, new_ie, old_ie_len) == 0);
586
587 wpabuf_free(old_ie_buff);
588 wpabuf_free(new_ie_buff);
589
590 return ret;
591}
592
593
594static u32 wpa_bss_compare_res(const struct wpa_bss *old,
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700595 const struct wpa_scan_res *new_res)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700596{
597 u32 changes = 0;
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700598 int caps_diff = old->caps ^ new_res->caps;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700599
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700600 if (old->freq != new_res->freq)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700601 changes |= WPA_BSS_FREQ_CHANGED_FLAG;
602
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700603 if (old->level != new_res->level)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700604 changes |= WPA_BSS_SIGNAL_CHANGED_FLAG;
605
606 if (caps_diff & IEEE80211_CAP_PRIVACY)
607 changes |= WPA_BSS_PRIVACY_CHANGED_FLAG;
608
609 if (caps_diff & IEEE80211_CAP_IBSS)
610 changes |= WPA_BSS_MODE_CHANGED_FLAG;
611
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700612 if (old->ie_len == new_res->ie_len &&
Hai Shalom60840252021-02-19 19:02:11 -0800613 os_memcmp(wpa_bss_ie_ptr(old), new_res + 1, old->ie_len) == 0)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700614 return changes;
615 changes |= WPA_BSS_IES_CHANGED_FLAG;
616
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700617 if (!are_ies_equal(old, new_res, WPA_IE_VENDOR_TYPE))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700618 changes |= WPA_BSS_WPAIE_CHANGED_FLAG;
619
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700620 if (!are_ies_equal(old, new_res, WLAN_EID_RSN))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700621 changes |= WPA_BSS_RSNIE_CHANGED_FLAG;
622
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700623 if (!are_ies_equal(old, new_res, WPS_IE_VENDOR_TYPE))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700624 changes |= WPA_BSS_WPS_CHANGED_FLAG;
625
Dmitry Shmidt1d755d02015-04-28 10:34:29 -0700626 if (!are_ies_equal(old, new_res, WLAN_EID_SUPP_RATES) ||
627 !are_ies_equal(old, new_res, WLAN_EID_EXT_SUPP_RATES))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700628 changes |= WPA_BSS_RATES_CHANGED_FLAG;
629
630 return changes;
631}
632
633
Hai Shalom60840252021-02-19 19:02:11 -0800634void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes,
635 const struct wpa_bss *bss)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700636{
637 if (changes & WPA_BSS_FREQ_CHANGED_FLAG)
638 wpas_notify_bss_freq_changed(wpa_s, bss->id);
639
640 if (changes & WPA_BSS_SIGNAL_CHANGED_FLAG)
641 wpas_notify_bss_signal_changed(wpa_s, bss->id);
642
643 if (changes & WPA_BSS_PRIVACY_CHANGED_FLAG)
644 wpas_notify_bss_privacy_changed(wpa_s, bss->id);
645
646 if (changes & WPA_BSS_MODE_CHANGED_FLAG)
647 wpas_notify_bss_mode_changed(wpa_s, bss->id);
648
649 if (changes & WPA_BSS_WPAIE_CHANGED_FLAG)
650 wpas_notify_bss_wpaie_changed(wpa_s, bss->id);
651
652 if (changes & WPA_BSS_RSNIE_CHANGED_FLAG)
653 wpas_notify_bss_rsnie_changed(wpa_s, bss->id);
654
655 if (changes & WPA_BSS_WPS_CHANGED_FLAG)
656 wpas_notify_bss_wps_changed(wpa_s, bss->id);
657
658 if (changes & WPA_BSS_IES_CHANGED_FLAG)
659 wpas_notify_bss_ies_changed(wpa_s, bss->id);
660
661 if (changes & WPA_BSS_RATES_CHANGED_FLAG)
662 wpas_notify_bss_rates_changed(wpa_s, bss->id);
Dmitry Shmidt661b4f72014-09-29 14:58:27 -0700663
664 wpas_notify_bss_seen(wpa_s, bss->id);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700665}
666
667
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700668static struct wpa_bss *
669wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800670 struct wpa_scan_res *res, struct os_reltime *fetch_time)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700671{
672 u32 changes;
673
Dmitry Shmidtabb90a32016-12-05 15:34:39 -0800674 if (bss->last_update_idx == wpa_s->bss_update_idx) {
675 struct os_reltime update_time;
676
677 /*
678 * Some drivers (e.g., cfg80211) include multiple BSS entries
679 * for the same BSS if that BSS's channel changes. The BSS list
680 * implementation in wpa_supplicant does not do that and we need
681 * to filter out the obsolete results here to make sure only the
682 * most current BSS information remains in the table.
683 */
684 wpa_printf(MSG_DEBUG, "BSS: " MACSTR
685 " has multiple entries in the scan results - select the most current one",
686 MAC2STR(bss->bssid));
687 calculate_update_time(fetch_time, res->age, &update_time);
688 wpa_printf(MSG_DEBUG,
689 "Previous last_update: %u.%06u (freq %d%s)",
690 (unsigned int) bss->last_update.sec,
691 (unsigned int) bss->last_update.usec,
692 bss->freq,
693 (bss->flags & WPA_BSS_ASSOCIATED) ? " assoc" : "");
694 wpa_printf(MSG_DEBUG, "New last_update: %u.%06u (freq %d%s)",
695 (unsigned int) update_time.sec,
696 (unsigned int) update_time.usec,
697 res->freq,
698 (res->flags & WPA_SCAN_ASSOCIATED) ? " assoc" : "");
699 if ((bss->flags & WPA_BSS_ASSOCIATED) ||
700 (!(res->flags & WPA_SCAN_ASSOCIATED) &&
701 !os_reltime_before(&bss->last_update, &update_time))) {
702 wpa_printf(MSG_DEBUG,
703 "Ignore this BSS entry since the previous update looks more current");
704 return bss;
705 }
706 wpa_printf(MSG_DEBUG,
707 "Accept this BSS entry since it looks more current than the previous update");
708 }
709
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700710 changes = wpa_bss_compare_res(bss, res);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800711 if (changes & WPA_BSS_FREQ_CHANGED_FLAG)
712 wpa_printf(MSG_DEBUG, "BSS: " MACSTR " changed freq %d --> %d",
713 MAC2STR(bss->bssid), bss->freq, res->freq);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700714 bss->scan_miss_count = 0;
715 bss->last_update_idx = wpa_s->bss_update_idx;
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800716 wpa_bss_copy_res(bss, res, fetch_time);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700717 /* Move the entry to the end of the list */
718 dl_list_del(&bss->list);
Dmitry Shmidt96571392013-10-14 12:54:46 -0700719#ifdef CONFIG_P2P
720 if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
Sunil Ravib0ac25f2024-07-12 01:42:03 +0000721 !wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE) &&
722 !(changes & WPA_BSS_FREQ_CHANGED_FLAG)) {
Dmitry Shmidt96571392013-10-14 12:54:46 -0700723 /*
724 * This can happen when non-P2P station interface runs a scan
725 * without P2P IE in the Probe Request frame. P2P GO would reply
726 * to that with a Probe Response that does not include P2P IE.
727 * Do not update the IEs in this BSS entry to avoid such loss of
728 * information that may be needed for P2P operations to
729 * determine group information.
730 */
731 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Do not update scan IEs for "
732 MACSTR " since that would remove P2P IE information",
733 MAC2STR(bss->bssid));
734 } else
735#endif /* CONFIG_P2P */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700736 if (bss->ie_len + bss->beacon_ie_len >=
737 res->ie_len + res->beacon_ie_len) {
Hai Shalom60840252021-02-19 19:02:11 -0800738 os_memcpy(bss->ies, res + 1, res->ie_len + res->beacon_ie_len);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700739 bss->ie_len = res->ie_len;
740 bss->beacon_ie_len = res->beacon_ie_len;
741 } else {
742 struct wpa_bss *nbss;
743 struct dl_list *prev = bss->list_id.prev;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000744 struct wpa_connect_work *cwork;
745 unsigned int i;
746 bool update_current_bss = wpa_s->current_bss == bss;
747 bool update_ml_probe_bss = wpa_s->ml_connect_probe_bss == bss;
748
749 cwork = wpa_bss_check_pending_connect(wpa_s, bss);
750
751 for (i = 0; i < wpa_s->last_scan_res_used; i++) {
752 if (wpa_s->last_scan_res[i] == bss)
753 break;
754 }
755
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700756 dl_list_del(&bss->list_id);
757 nbss = os_realloc(bss, sizeof(*bss) + res->ie_len +
758 res->beacon_ie_len);
759 if (nbss) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000760 if (i != wpa_s->last_scan_res_used)
761 wpa_s->last_scan_res[i] = nbss;
762
763 if (update_current_bss)
Dmitry Shmidt04949592012-07-19 12:16:46 -0700764 wpa_s->current_bss = nbss;
Sunil Ravi2a14cf12023-11-21 00:54:38 +0000765
766 if (update_ml_probe_bss)
767 wpa_s->ml_connect_probe_bss = nbss;
768
769 if (cwork)
770 wpa_bss_update_pending_connect(cwork, nbss);
771
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700772 bss = nbss;
Hai Shalom60840252021-02-19 19:02:11 -0800773 os_memcpy(bss->ies, res + 1,
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700774 res->ie_len + res->beacon_ie_len);
775 bss->ie_len = res->ie_len;
776 bss->beacon_ie_len = res->beacon_ie_len;
777 }
778 dl_list_add(prev, &bss->list_id);
779 }
Sunil Ravi89eba102022-09-13 21:04:37 -0700780 if (changes & WPA_BSS_IES_CHANGED_FLAG) {
781 const u8 *ml_ie, *mld_addr;
782
Dmitry Shmidt4530cfd2012-09-09 15:20:40 -0700783 wpa_bss_set_hessid(bss);
Sunil Ravi89eba102022-09-13 21:04:37 -0700784 os_memset(bss->mld_addr, 0, ETH_ALEN);
785 ml_ie = wpa_scan_get_ml_ie(res, MULTI_LINK_CONTROL_TYPE_BASIC);
786 if (ml_ie) {
787 mld_addr = get_basic_mle_mld_addr(&ml_ie[3],
788 ml_ie[1] - 1);
789 if (mld_addr)
790 os_memcpy(bss->mld_addr, mld_addr, ETH_ALEN);
791 }
792 }
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700793 dl_list_add_tail(&wpa_s->bss, &bss->list);
794
795 notify_bss_changes(wpa_s, changes, bss);
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700796
797 return bss;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700798}
799
800
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800801/**
802 * wpa_bss_update_start - Start a BSS table update from scan results
803 * @wpa_s: Pointer to wpa_supplicant data
804 *
805 * This function is called at the start of each BSS table update round for new
806 * scan results. The actual scan result entries are indicated with calls to
807 * wpa_bss_update_scan_res() and the update round is finished with a call to
808 * wpa_bss_update_end().
809 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700810void wpa_bss_update_start(struct wpa_supplicant *wpa_s)
811{
812 wpa_s->bss_update_idx++;
813 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u",
814 wpa_s->bss_update_idx);
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700815 wpa_s->last_scan_res_used = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700816}
817
818
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800819/**
820 * wpa_bss_update_scan_res - Update a BSS table entry based on a scan result
821 * @wpa_s: Pointer to wpa_supplicant data
822 * @res: Scan result
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800823 * @fetch_time: Time when the result was fetched from the driver
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800824 *
825 * This function updates a BSS table entry (or adds one) based on a scan result.
826 * This is called separately for each scan result between the calls to
827 * wpa_bss_update_start() and wpa_bss_update_end().
828 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700829void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800830 struct wpa_scan_res *res,
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800831 struct os_reltime *fetch_time)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700832{
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800833 const u8 *ssid, *p2p, *mesh;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700834 struct wpa_bss *bss;
835
Dmitry Shmidt444d5672013-04-01 13:08:44 -0700836 if (wpa_s->conf->ignore_old_scan_res) {
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800837 struct os_reltime update;
Dmitry Shmidt444d5672013-04-01 13:08:44 -0700838 calculate_update_time(fetch_time, res->age, &update);
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800839 if (os_reltime_before(&update, &wpa_s->scan_trigger_time)) {
840 struct os_reltime age;
841 os_reltime_sub(&wpa_s->scan_trigger_time, &update,
842 &age);
Dmitry Shmidt444d5672013-04-01 13:08:44 -0700843 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Ignore driver BSS "
844 "table entry that is %u.%06u seconds older "
845 "than our scan trigger",
846 (unsigned int) age.sec,
847 (unsigned int) age.usec);
848 return;
849 }
850 }
851
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700852 ssid = wpa_scan_get_ie(res, WLAN_EID_SSID);
853 if (ssid == NULL) {
854 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for "
855 MACSTR, MAC2STR(res->bssid));
856 return;
857 }
Dmitry Shmidt9d9e6022015-04-23 10:34:55 -0700858 if (ssid[1] > SSID_MAX_LEN) {
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700859 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for "
860 MACSTR, MAC2STR(res->bssid));
861 return;
862 }
863
864 p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE);
Dmitry Shmidt04949592012-07-19 12:16:46 -0700865#ifdef CONFIG_P2P
866 if (p2p == NULL &&
867 wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
868 /*
869 * If it's a P2P specific interface, then don't update
870 * the scan result without a P2P IE.
871 */
872 wpa_printf(MSG_DEBUG, "BSS: No P2P IE - skipping BSS " MACSTR
873 " update for P2P interface", MAC2STR(res->bssid));
874 return;
875 }
876#endif /* CONFIG_P2P */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700877 if (p2p && ssid[1] == P2P_WILDCARD_SSID_LEN &&
878 os_memcmp(ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0)
879 return; /* Skip P2P listen discovery results here */
880
881 /* TODO: add option for ignoring BSSes we are not interested in
882 * (to save memory) */
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800883
884 mesh = wpa_scan_get_ie(res, WLAN_EID_MESH_ID);
Dmitry Shmidt9d9e6022015-04-23 10:34:55 -0700885 if (mesh && mesh[1] <= SSID_MAX_LEN)
Dmitry Shmidt6c0da2b2015-01-05 13:08:17 -0800886 ssid = mesh;
887
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700888 bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
889 if (bss == NULL)
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800890 bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res, fetch_time);
Dmitry Shmidt56052862013-10-04 10:23:25 -0700891 else {
Dmitry Shmidtf8623282013-02-20 14:34:59 -0800892 bss = wpa_bss_update(wpa_s, bss, res, fetch_time);
Dmitry Shmidt56052862013-10-04 10:23:25 -0700893 if (wpa_s->last_scan_res) {
894 unsigned int i;
895 for (i = 0; i < wpa_s->last_scan_res_used; i++) {
896 if (bss == wpa_s->last_scan_res[i]) {
897 /* Already in the list */
898 return;
899 }
900 }
901 }
902 }
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700903
904 if (bss == NULL)
905 return;
906 if (wpa_s->last_scan_res_used >= wpa_s->last_scan_res_size) {
907 struct wpa_bss **n;
908 unsigned int siz;
909 if (wpa_s->last_scan_res_size == 0)
910 siz = 32;
911 else
912 siz = wpa_s->last_scan_res_size * 2;
913 n = os_realloc_array(wpa_s->last_scan_res, siz,
914 sizeof(struct wpa_bss *));
915 if (n == NULL)
916 return;
917 wpa_s->last_scan_res = n;
918 wpa_s->last_scan_res_size = siz;
919 }
920
Dmitry Shmidt7832adb2014-04-29 10:53:02 -0700921 if (wpa_s->last_scan_res)
922 wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700923}
924
925
926static int wpa_bss_included_in_scan(const struct wpa_bss *bss,
927 const struct scan_info *info)
928{
929 int found;
930 size_t i;
931
932 if (info == NULL)
933 return 1;
934
935 if (info->num_freqs) {
936 found = 0;
937 for (i = 0; i < info->num_freqs; i++) {
938 if (bss->freq == info->freqs[i]) {
939 found = 1;
940 break;
941 }
942 }
943 if (!found)
944 return 0;
945 }
946
947 if (info->num_ssids) {
948 found = 0;
949 for (i = 0; i < info->num_ssids; i++) {
950 const struct wpa_driver_scan_ssid *s = &info->ssids[i];
951 if ((s->ssid == NULL || s->ssid_len == 0) ||
952 (s->ssid_len == bss->ssid_len &&
953 os_memcmp(s->ssid, bss->ssid, bss->ssid_len) ==
954 0)) {
955 found = 1;
956 break;
957 }
958 }
959 if (!found)
960 return 0;
961 }
962
963 return 1;
964}
965
966
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -0800967/**
968 * wpa_bss_update_end - End a BSS table update from scan results
969 * @wpa_s: Pointer to wpa_supplicant data
970 * @info: Information about scan parameters
971 * @new_scan: Whether this update round was based on a new scan
972 *
973 * This function is called at the end of each BSS table update round for new
974 * scan results. The start of the update was indicated with a call to
975 * wpa_bss_update_start().
976 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700977void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
978 int new_scan)
979{
980 struct wpa_bss *bss, *n;
981
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -0800982 os_get_reltime(&wpa_s->last_scan);
Dmitry Shmidtd80a4012015-11-05 16:35:40 -0800983 if ((info && info->aborted) || !new_scan)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700984 return; /* do not expire entries without new scan */
985
986 dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
987 if (wpa_bss_in_use(wpa_s, bss))
988 continue;
989 if (!wpa_bss_included_in_scan(bss, info))
990 continue; /* expire only BSSes that were scanned */
991 if (bss->last_update_idx < wpa_s->bss_update_idx)
992 bss->scan_miss_count++;
993 if (bss->scan_miss_count >=
994 wpa_s->conf->bss_expiration_scan_count) {
Dmitry Shmidt04949592012-07-19 12:16:46 -0700995 wpa_bss_remove(wpa_s, bss, "no match in scan");
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700996 }
997 }
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -0700998
Hai Shalomfdcde762020-04-02 11:19:20 -0700999 wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%zu/%zu",
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001000 wpa_s->last_scan_res_used, wpa_s->last_scan_res_size);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001001}
1002
1003
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001004/**
1005 * wpa_bss_flush_by_age - Flush old BSS entries
1006 * @wpa_s: Pointer to wpa_supplicant data
1007 * @age: Maximum entry age in seconds
1008 *
1009 * Remove BSS entries that have not been updated during the last @age seconds.
1010 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001011void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age)
1012{
1013 struct wpa_bss *bss, *n;
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001014 struct os_reltime t;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001015
1016 if (dl_list_empty(&wpa_s->bss))
1017 return;
1018
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001019 os_get_reltime(&t);
Sunil Raviaf8751c2023-03-29 11:35:17 -07001020
1021 if (t.sec < age)
1022 return; /* avoid underflow; there can be no older entries */
1023
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001024 t.sec -= age;
1025
1026 dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
1027 if (wpa_bss_in_use(wpa_s, bss))
1028 continue;
1029
Sunil Ravi38ad1ed2023-01-17 23:58:31 +00001030 if (wpa_s->reassoc_same_ess &&
1031 wpa_s->wpa_state != WPA_COMPLETED &&
1032 wpa_s->last_ssid &&
1033 bss->ssid_len == wpa_s->last_ssid->ssid_len &&
1034 os_memcmp(bss->ssid, wpa_s->last_ssid->ssid,
1035 bss->ssid_len) == 0)
1036 continue;
1037
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001038 if (os_reltime_before(&bss->last_update, &t)) {
Dmitry Shmidt04949592012-07-19 12:16:46 -07001039 wpa_bss_remove(wpa_s, bss, __func__);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001040 } else
1041 break;
1042 }
1043}
1044
1045
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001046/**
1047 * wpa_bss_init - Initialize BSS table
1048 * @wpa_s: Pointer to wpa_supplicant data
1049 * Returns: 0 on success, -1 on failure
1050 *
1051 * This prepares BSS table lists and timer for periodic updates. The BSS table
1052 * is deinitialized with wpa_bss_deinit() once not needed anymore.
1053 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001054int wpa_bss_init(struct wpa_supplicant *wpa_s)
1055{
1056 dl_list_init(&wpa_s->bss);
1057 dl_list_init(&wpa_s->bss_id);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001058 return 0;
1059}
1060
1061
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001062/**
1063 * wpa_bss_flush - Flush all unused BSS entries
1064 * @wpa_s: Pointer to wpa_supplicant data
1065 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001066void wpa_bss_flush(struct wpa_supplicant *wpa_s)
1067{
1068 struct wpa_bss *bss, *n;
1069
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001070 wpa_s->clear_driver_scan_cache = 1;
1071
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001072 if (wpa_s->bss.next == NULL)
1073 return; /* BSS table not yet initialized */
1074
1075 dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
1076 if (wpa_bss_in_use(wpa_s, bss))
1077 continue;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001078 wpa_bss_remove(wpa_s, bss, __func__);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001079 }
1080}
1081
1082
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001083/**
1084 * wpa_bss_deinit - Deinitialize BSS table
1085 * @wpa_s: Pointer to wpa_supplicant data
1086 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001087void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
1088{
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001089 wpa_bss_flush(wpa_s);
1090}
1091
1092
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001093/**
1094 * wpa_bss_get_bssid - Fetch a BSS table entry based on BSSID
1095 * @wpa_s: Pointer to wpa_supplicant data
1096 * @bssid: BSSID
1097 * Returns: Pointer to the BSS entry or %NULL if not found
1098 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001099struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
1100 const u8 *bssid)
1101{
1102 struct wpa_bss *bss;
Dmitry Shmidt04949592012-07-19 12:16:46 -07001103 if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
1104 return NULL;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001105 dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001106 if (ether_addr_equal(bss->bssid, bssid))
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001107 return bss;
1108 }
1109 return NULL;
1110}
1111
1112
Dmitry Shmidt444d5672013-04-01 13:08:44 -07001113/**
1114 * wpa_bss_get_bssid_latest - Fetch the latest BSS table entry based on BSSID
1115 * @wpa_s: Pointer to wpa_supplicant data
1116 * @bssid: BSSID
1117 * Returns: Pointer to the BSS entry or %NULL if not found
1118 *
1119 * This function is like wpa_bss_get_bssid(), but full BSS table is iterated to
1120 * find the entry that has the most recent update. This can help in finding the
1121 * correct entry in cases where the SSID of the AP may have changed recently
1122 * (e.g., in WPS reconfiguration cases).
1123 */
1124struct wpa_bss * wpa_bss_get_bssid_latest(struct wpa_supplicant *wpa_s,
1125 const u8 *bssid)
1126{
1127 struct wpa_bss *bss, *found = NULL;
1128 if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
1129 return NULL;
1130 dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001131 if (!ether_addr_equal(bss->bssid, bssid))
Dmitry Shmidt444d5672013-04-01 13:08:44 -07001132 continue;
1133 if (found == NULL ||
Dmitry Shmidtfb79edc2014-01-10 10:45:54 -08001134 os_reltime_before(&found->last_update, &bss->last_update))
Dmitry Shmidt444d5672013-04-01 13:08:44 -07001135 found = bss;
1136 }
1137 return found;
1138}
1139
1140
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08001141#ifdef CONFIG_P2P
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001142/**
Hai Shalomc3565922019-10-28 11:58:20 -07001143 * wpa_bss_get_p2p_dev_addr - Fetch the latest BSS table entry based on P2P Device Addr
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001144 * @wpa_s: Pointer to wpa_supplicant data
1145 * @dev_addr: P2P Device Address of the GO
1146 * Returns: Pointer to the BSS entry or %NULL if not found
Hai Shalomc3565922019-10-28 11:58:20 -07001147 *
1148 * This function tries to find the entry that has the most recent update. This
1149 * can help in finding the correct entry in cases where the SSID of the P2P
1150 * Device may have changed recently.
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001151 */
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08001152struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s,
1153 const u8 *dev_addr)
1154{
Hai Shalomc3565922019-10-28 11:58:20 -07001155 struct wpa_bss *bss, *found = NULL;
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08001156 dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
1157 u8 addr[ETH_ALEN];
Hai Shalom60840252021-02-19 19:02:11 -08001158 if (p2p_parse_dev_addr(wpa_bss_ie_ptr(bss), bss->ie_len,
Hai Shalomc3565922019-10-28 11:58:20 -07001159 addr) != 0 ||
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001160 !ether_addr_equal(addr, dev_addr))
Hai Shalomc3565922019-10-28 11:58:20 -07001161 continue;
1162 if (!found ||
1163 os_reltime_before(&found->last_update, &bss->last_update))
1164 found = bss;
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08001165 }
Hai Shalomc3565922019-10-28 11:58:20 -07001166 return found;
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08001167}
1168#endif /* CONFIG_P2P */
1169
1170
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001171/**
1172 * wpa_bss_get_id - Fetch a BSS table entry based on identifier
1173 * @wpa_s: Pointer to wpa_supplicant data
1174 * @id: Unique identifier (struct wpa_bss::id) assigned for the entry
1175 * Returns: Pointer to the BSS entry or %NULL if not found
1176 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001177struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
1178{
1179 struct wpa_bss *bss;
1180 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
1181 if (bss->id == id)
1182 return bss;
1183 }
1184 return NULL;
1185}
1186
1187
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001188/**
Dmitry Shmidtf8623282013-02-20 14:34:59 -08001189 * wpa_bss_get_id_range - Fetch a BSS table entry based on identifier range
1190 * @wpa_s: Pointer to wpa_supplicant data
1191 * @idf: Smallest allowed identifier assigned for the entry
1192 * @idf: Largest allowed identifier assigned for the entry
1193 * Returns: Pointer to the BSS entry or %NULL if not found
1194 *
1195 * This function is similar to wpa_bss_get_id() but allows a BSS entry with the
1196 * smallest id value to be fetched within the specified range without the
1197 * caller having to know the exact id.
1198 */
1199struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s,
1200 unsigned int idf, unsigned int idl)
1201{
1202 struct wpa_bss *bss;
1203 dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
1204 if (bss->id >= idf && bss->id <= idl)
1205 return bss;
1206 }
1207 return NULL;
1208}
1209
1210
1211/**
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001212 * wpa_bss_get_ie - Fetch a specified information element from a BSS entry
1213 * @bss: BSS table entry
1214 * @ie: Information element identitifier (WLAN_EID_*)
1215 * Returns: Pointer to the information element (id field) or %NULL if not found
1216 *
1217 * This function returns the first matching information element in the BSS
1218 * entry.
1219 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001220const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie)
1221{
Hai Shalom60840252021-02-19 19:02:11 -08001222 return get_ie(wpa_bss_ie_ptr(bss), bss->ie_len, ie);
1223}
1224
1225
1226/**
1227 * wpa_bss_get_ie_ext - Fetch a specified extended IE from a BSS entry
1228 * @bss: BSS table entry
1229 * @ext: Information element extension identifier (WLAN_EID_EXT_*)
1230 * Returns: Pointer to the information element (id field) or %NULL if not found
1231 *
1232 * This function returns the first matching information element in the BSS
1233 * entry.
1234 */
1235const u8 * wpa_bss_get_ie_ext(const struct wpa_bss *bss, u8 ext)
1236{
1237 return get_ie_ext(wpa_bss_ie_ptr(bss), bss->ie_len, ext);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001238}
1239
1240
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001241/**
1242 * wpa_bss_get_vendor_ie - Fetch a vendor information element from a BSS entry
1243 * @bss: BSS table entry
1244 * @vendor_type: Vendor type (four octets starting the IE payload)
1245 * Returns: Pointer to the information element (id field) or %NULL if not found
1246 *
1247 * This function returns the first matching information element in the BSS
1248 * entry.
1249 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001250const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type)
1251{
Hai Shalom60840252021-02-19 19:02:11 -08001252 const u8 *ies;
1253 const struct element *elem;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001254
Hai Shalom60840252021-02-19 19:02:11 -08001255 ies = wpa_bss_ie_ptr(bss);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001256
Hai Shalom60840252021-02-19 19:02:11 -08001257 for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, bss->ie_len) {
1258 if (elem->datalen >= 4 &&
1259 vendor_type == WPA_GET_BE32(elem->data))
1260 return &elem->id;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001261 }
1262
1263 return NULL;
1264}
1265
1266
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001267/**
Dmitry Shmidt96571392013-10-14 12:54:46 -07001268 * wpa_bss_get_vendor_ie_beacon - Fetch a vendor information from a BSS entry
1269 * @bss: BSS table entry
1270 * @vendor_type: Vendor type (four octets starting the IE payload)
1271 * Returns: Pointer to the information element (id field) or %NULL if not found
1272 *
1273 * This function returns the first matching information element in the BSS
1274 * entry.
1275 *
1276 * This function is like wpa_bss_get_vendor_ie(), but uses IE buffer only
1277 * from Beacon frames instead of either Beacon or Probe Response frames.
1278 */
1279const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss,
1280 u32 vendor_type)
1281{
Hai Shalom60840252021-02-19 19:02:11 -08001282 const u8 *ies;
1283 const struct element *elem;
Dmitry Shmidt96571392013-10-14 12:54:46 -07001284
1285 if (bss->beacon_ie_len == 0)
1286 return NULL;
1287
Hai Shalom60840252021-02-19 19:02:11 -08001288 ies = wpa_bss_ie_ptr(bss);
1289 ies += bss->ie_len;
Dmitry Shmidt96571392013-10-14 12:54:46 -07001290
Hai Shalom60840252021-02-19 19:02:11 -08001291 for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies,
1292 bss->beacon_ie_len) {
1293 if (elem->datalen >= 4 &&
1294 vendor_type == WPA_GET_BE32(elem->data))
1295 return &elem->id;
Dmitry Shmidt96571392013-10-14 12:54:46 -07001296 }
1297
1298 return NULL;
1299}
1300
1301
1302/**
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001303 * wpa_bss_get_vendor_ie_multi - Fetch vendor IE data from a BSS entry
1304 * @bss: BSS table entry
1305 * @vendor_type: Vendor type (four octets starting the IE payload)
1306 * Returns: Pointer to the information element payload or %NULL if not found
1307 *
1308 * This function returns concatenated payload of possibly fragmented vendor
1309 * specific information elements in the BSS entry. The caller is responsible for
1310 * freeing the returned buffer.
1311 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001312struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
1313 u32 vendor_type)
1314{
1315 struct wpabuf *buf;
1316 const u8 *end, *pos;
1317
1318 buf = wpabuf_alloc(bss->ie_len);
1319 if (buf == NULL)
1320 return NULL;
1321
Hai Shalom60840252021-02-19 19:02:11 -08001322 pos = wpa_bss_ie_ptr(bss);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001323 end = pos + bss->ie_len;
1324
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001325 while (end - pos > 1) {
Hai Shalom60840252021-02-19 19:02:11 -08001326 u8 ie, len;
1327
1328 ie = pos[0];
1329 len = pos[1];
1330 if (len > end - pos - 2)
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001331 break;
Hai Shalom60840252021-02-19 19:02:11 -08001332 pos += 2;
1333 if (ie == WLAN_EID_VENDOR_SPECIFIC && len >= 4 &&
1334 vendor_type == WPA_GET_BE32(pos))
1335 wpabuf_put_data(buf, pos + 4, len - 4);
1336 pos += len;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001337 }
1338
1339 if (wpabuf_len(buf) == 0) {
1340 wpabuf_free(buf);
1341 buf = NULL;
1342 }
1343
1344 return buf;
1345}
1346
1347
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001348/**
1349 * wpa_bss_get_vendor_ie_multi_beacon - Fetch vendor IE data from a BSS entry
1350 * @bss: BSS table entry
1351 * @vendor_type: Vendor type (four octets starting the IE payload)
1352 * Returns: Pointer to the information element payload or %NULL if not found
1353 *
1354 * This function returns concatenated payload of possibly fragmented vendor
1355 * specific information elements in the BSS entry. The caller is responsible for
1356 * freeing the returned buffer.
1357 *
1358 * This function is like wpa_bss_get_vendor_ie_multi(), but uses IE buffer only
1359 * from Beacon frames instead of either Beacon or Probe Response frames.
1360 */
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -07001361struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss,
1362 u32 vendor_type)
1363{
1364 struct wpabuf *buf;
1365 const u8 *end, *pos;
1366
1367 buf = wpabuf_alloc(bss->beacon_ie_len);
1368 if (buf == NULL)
1369 return NULL;
1370
Hai Shalom60840252021-02-19 19:02:11 -08001371 pos = wpa_bss_ie_ptr(bss);
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -07001372 pos += bss->ie_len;
1373 end = pos + bss->beacon_ie_len;
1374
Dmitry Shmidtd80a4012015-11-05 16:35:40 -08001375 while (end - pos > 1) {
Sunil8cd6f4d2022-06-28 18:40:46 +00001376 u8 id, len;
1377
1378 id = *pos++;
1379 len = *pos++;
1380 if (len > end - pos)
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -07001381 break;
Sunil8cd6f4d2022-06-28 18:40:46 +00001382 if (id == WLAN_EID_VENDOR_SPECIFIC && len >= 4 &&
1383 vendor_type == WPA_GET_BE32(pos))
1384 wpabuf_put_data(buf, pos + 4, len - 4);
1385 pos += len;
Dmitry Shmidt9bce59c2012-09-11 15:06:38 -07001386 }
1387
1388 if (wpabuf_len(buf) == 0) {
1389 wpabuf_free(buf);
1390 buf = NULL;
1391 }
1392
1393 return buf;
1394}
1395
1396
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001397/**
1398 * wpa_bss_get_max_rate - Get maximum legacy TX rate supported in a BSS
1399 * @bss: BSS table entry
1400 * Returns: Maximum legacy rate in units of 500 kbps
1401 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001402int wpa_bss_get_max_rate(const struct wpa_bss *bss)
1403{
1404 int rate = 0;
1405 const u8 *ie;
1406 int i;
1407
1408 ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
1409 for (i = 0; ie && i < ie[1]; i++) {
1410 if ((ie[i + 2] & 0x7f) > rate)
1411 rate = ie[i + 2] & 0x7f;
1412 }
1413
1414 ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
1415 for (i = 0; ie && i < ie[1]; i++) {
1416 if ((ie[i + 2] & 0x7f) > rate)
1417 rate = ie[i + 2] & 0x7f;
1418 }
1419
1420 return rate;
1421}
1422
1423
Dmitry Shmidta54fa5f2013-01-15 13:53:35 -08001424/**
1425 * wpa_bss_get_bit_rates - Get legacy TX rates supported in a BSS
1426 * @bss: BSS table entry
1427 * @rates: Buffer for returning a pointer to the rates list (units of 500 kbps)
1428 * Returns: number of legacy TX rates or -1 on failure
1429 *
1430 * The caller is responsible for freeing the returned buffer with os_free() in
1431 * case of success.
1432 */
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001433int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates)
1434{
1435 const u8 *ie, *ie2;
1436 int i, j;
1437 unsigned int len;
1438 u8 *r;
1439
1440 ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
1441 ie2 = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
1442
1443 len = (ie ? ie[1] : 0) + (ie2 ? ie2[1] : 0);
1444
1445 r = os_malloc(len);
1446 if (!r)
1447 return -1;
1448
1449 for (i = 0; ie && i < ie[1]; i++)
1450 r[i] = ie[i + 2] & 0x7f;
1451
1452 for (j = 0; ie2 && j < ie2[1]; j++)
1453 r[i + j] = ie2[j + 2] & 0x7f;
1454
1455 *rates = r;
1456 return len;
1457}
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001458
1459
1460#ifdef CONFIG_FILS
Hai Shalom60840252021-02-19 19:02:11 -08001461const u8 * wpa_bss_get_fils_cache_id(const struct wpa_bss *bss)
Dmitry Shmidtd2986c22017-10-23 14:22:09 -07001462{
1463 const u8 *ie;
1464
1465 if (bss) {
1466 ie = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
1467 if (ie && ie[1] >= 4 && WPA_GET_LE16(ie + 2) & BIT(7))
1468 return ie + 4;
1469 }
1470
1471 return NULL;
1472}
1473#endif /* CONFIG_FILS */
Hai Shalom74f70d42019-02-11 14:42:39 -08001474
1475
1476int wpa_bss_ext_capab(const struct wpa_bss *bss, unsigned int capab)
1477{
Hai Shalom60840252021-02-19 19:02:11 -08001478 if (!bss)
1479 return 0;
Hai Shalom74f70d42019-02-11 14:42:39 -08001480 return ieee802_11_ext_capab(wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB),
1481 capab);
1482}
Sunil Ravi38ad1ed2023-01-17 23:58:31 +00001483
1484
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001485static void
1486wpa_bss_parse_ml_rnr_ap_info(struct wpa_supplicant *wpa_s,
1487 struct wpa_bss *bss, u8 mbssid_idx,
1488 const struct ieee80211_neighbor_ap_info *ap_info,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001489 size_t len, u16 *seen, u16 *missing,
1490 struct wpa_ssid *ssid)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001491{
1492 const u8 *pos, *end;
1493 const u8 *mld_params;
1494 u8 count, mld_params_offset;
1495 u8 i, type, link_id;
1496
1497 count = RNR_TBTT_INFO_COUNT_VAL(ap_info->tbtt_info_hdr) + 1;
1498 type = ap_info->tbtt_info_hdr & RNR_TBTT_INFO_HDR_TYPE_MSK;
1499
1500 /* MLD information is at offset 13 or at start */
1501 if (type == 0 && ap_info->tbtt_info_len >= RNR_TBTT_INFO_MLD_LEN) {
1502 /* MLD info is appended */
1503 mld_params_offset = RNR_TBTT_INFO_LEN;
1504 } else {
1505 /* TODO: Support NSTR AP */
1506 return;
1507 }
1508
1509 pos = (const u8 *) ap_info;
1510 end = pos + len;
1511 pos += sizeof(*ap_info);
1512
1513 for (i = 0; i < count; i++) {
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001514 u8 bss_params;
1515
Sunil Ravi88611412024-06-28 17:34:56 +00001516 if (bss->n_mld_links >= MAX_NUM_MLD_LINKS)
1517 return;
1518
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001519 if (end - pos < ap_info->tbtt_info_len)
1520 break;
1521
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001522 bss_params = pos[1 + ETH_ALEN + 4];
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001523 mld_params = pos + mld_params_offset;
1524
1525 link_id = *(mld_params + 1) & EHT_ML_LINK_ID_MSK;
1526
1527 if (*mld_params != mbssid_idx) {
1528 wpa_printf(MSG_DEBUG,
1529 "MLD: Reported link not part of MLD");
1530 } else if (!(BIT(link_id) & *seen)) {
1531 struct wpa_bss *neigh_bss =
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001532 wpa_bss_get_bssid(wpa_s, pos + 1);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001533
1534 *seen |= BIT(link_id);
1535 wpa_printf(MSG_DEBUG, "MLD: mld ID=%u, link ID=%u",
1536 *mld_params, link_id);
1537
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001538 if (!neigh_bss) {
1539 *missing |= BIT(link_id);
1540 } else if ((!ssid ||
1541 (bss_params & (RNR_BSS_PARAM_SAME_SSID |
1542 RNR_BSS_PARAM_CO_LOCATED)) ||
1543 wpa_scan_res_match(wpa_s, 0, neigh_bss,
1544 ssid, 1, 0)) &&
1545 !wpa_bssid_ignore_is_listed(
1546 wpa_s, neigh_bss->bssid)) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001547 struct mld_link *l;
1548
Sunil Ravi88611412024-06-28 17:34:56 +00001549 l = &bss->mld_links[bss->n_mld_links];
1550 l->link_id = link_id;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001551 os_memcpy(l->bssid, pos + 1, ETH_ALEN);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001552 l->freq = neigh_bss->freq;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001553 l->disabled = mld_params[2] &
1554 RNR_TBTT_INFO_MLD_PARAM2_LINK_DISABLED;
Sunil Ravi88611412024-06-28 17:34:56 +00001555 bss->n_mld_links++;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001556 }
1557 }
1558
1559 pos += ap_info->tbtt_info_len;
1560 }
1561}
1562
1563
1564/**
1565 * wpa_bss_parse_basic_ml_element - Parse the Basic Multi-Link element
1566 * @wpa_s: Pointer to wpa_supplicant data
1567 * @bss: BSS table entry
1568 * @mld_addr: AP MLD address (or %NULL)
1569 * @link_info: Array to store link information (or %NULL),
1570 * should be initialized and #MAX_NUM_MLD_LINKS elements long
1571 * @missing_links: Result bitmask of links that were not discovered (or %NULL)
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001572 * @ssid: Target SSID (or %NULL)
1573 * @ap_mld_id: On return would hold the corresponding AP MLD ID (or %NULL)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001574 * Returns: 0 on success or -1 for non-MLD or parsing failures
1575 *
1576 * Parses the Basic Multi-Link element of the BSS into @link_info using the scan
1577 * information stored in the wpa_supplicant data to fill in information for
1578 * links where possible. The @missing_links out parameter will contain any links
1579 * for which no corresponding BSS was found.
1580 */
1581int wpa_bss_parse_basic_ml_element(struct wpa_supplicant *wpa_s,
1582 struct wpa_bss *bss,
1583 u8 *ap_mld_addr,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001584 u16 *missing_links,
1585 struct wpa_ssid *ssid,
1586 u8 *ap_mld_id)
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001587{
1588 struct ieee802_11_elems elems;
1589 struct wpabuf *mlbuf;
1590 const struct element *elem;
1591 u8 mbssid_idx = 0;
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001592 size_t ml_ie_len;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001593 const struct ieee80211_eht_ml *eht_ml;
1594 const struct eht_ml_basic_common_info *ml_basic_common_info;
1595 u8 i, link_id;
1596 const u16 control_mask =
1597 MULTI_LINK_CONTROL_TYPE_MASK |
1598 BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
1599 BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
1600 BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA;
1601 const u16 control =
1602 MULTI_LINK_CONTROL_TYPE_BASIC |
1603 BASIC_MULTI_LINK_CTRL_PRES_LINK_ID |
1604 BASIC_MULTI_LINK_CTRL_PRES_BSS_PARAM_CH_COUNT |
1605 BASIC_MULTI_LINK_CTRL_PRES_MLD_CAPA;
1606 u16 missing = 0;
1607 u16 seen;
1608 const u8 *ies_pos = wpa_bss_ie_ptr(bss);
1609 size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len;
1610 int ret = -1;
1611 struct mld_link *l;
1612
1613 if (ieee802_11_parse_elems(ies_pos, ies_len, &elems, 1) ==
1614 ParseFailed) {
1615 wpa_dbg(wpa_s, MSG_DEBUG, "MLD: Failed to parse elements");
1616 return ret;
1617 }
1618
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001619 mlbuf = ieee802_11_defrag(elems.basic_mle, elems.basic_mle_len, true);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001620 if (!mlbuf) {
1621 wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No Multi-Link element");
1622 return ret;
1623 }
1624
1625 ml_ie_len = wpabuf_len(mlbuf);
1626
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001627 if (ssid) {
1628 struct wpa_ie_data ie;
1629
1630 if (!elems.rsn_ie ||
1631 wpa_parse_wpa_ie(elems.rsn_ie - 2, 2 + elems.rsn_ie_len,
1632 &ie)) {
1633 wpa_dbg(wpa_s, MSG_DEBUG, "MLD: No RSN element");
1634 goto out;
1635 }
1636
1637 if (!(ie.capabilities & WPA_CAPABILITY_MFPC) ||
1638 wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION) {
1639 wpa_dbg(wpa_s, MSG_DEBUG,
1640 "MLD: No management frame protection");
1641 goto out;
1642 }
1643
1644 ie.key_mgmt &= ~(WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK |
1645 WPA_KEY_MGMT_PSK_SHA256);
1646 if (!(ie.key_mgmt & ssid->key_mgmt)) {
1647 wpa_dbg(wpa_s, MSG_DEBUG,
1648 "MLD: No valid key management");
1649 goto out;
1650 }
1651 }
1652
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001653 /*
1654 * for ext ID + 2 control + common info len + MLD address +
1655 * link info
1656 */
1657 if (ml_ie_len < 2UL + 1UL + ETH_ALEN + 1UL)
1658 goto out;
1659
1660 eht_ml = (const struct ieee80211_eht_ml *) wpabuf_head(mlbuf);
1661 if ((le_to_host16(eht_ml->ml_control) & control_mask) != control) {
1662 wpa_printf(MSG_DEBUG,
1663 "MLD: Unexpected Multi-Link element control=0x%x (mask 0x%x expected 0x%x)",
1664 le_to_host16(eht_ml->ml_control), control_mask,
1665 control);
1666 goto out;
1667 }
1668
1669 ml_basic_common_info =
1670 (const struct eht_ml_basic_common_info *) eht_ml->variable;
1671
1672 /* Common info length should be valid */
1673 if (ml_basic_common_info->len < ETH_ALEN + 1UL)
1674 goto out;
1675
1676 /* Get the MLD address and MLD link ID */
1677 if (ap_mld_addr)
1678 os_memcpy(ap_mld_addr, ml_basic_common_info->mld_addr,
1679 ETH_ALEN);
1680
Sunil Ravi88611412024-06-28 17:34:56 +00001681 bss->n_mld_links = 0;
1682 l = &bss->mld_links[bss->n_mld_links];
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001683 link_id = ml_basic_common_info->variable[0] & EHT_ML_LINK_ID_MSK;
Sunil Ravi88611412024-06-28 17:34:56 +00001684 l->link_id = link_id;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001685 os_memcpy(l->bssid, bss->bssid, ETH_ALEN);
1686 l->freq = bss->freq;
1687
Sunil Ravi88611412024-06-28 17:34:56 +00001688 seen = BIT(link_id);
1689 bss->n_mld_links++;
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001690
1691 /*
1692 * The AP MLD ID in the RNR corresponds to the MBSSID index, see
1693 * IEEE P802.11be/D4.0, 9.4.2.169.2 (Neighbor AP Information field).
1694 *
1695 * For the transmitting BSSID it is clear that both the MBSSID index
1696 * and the AP MLD ID in the RNR are zero.
1697 *
1698 * For nontransmitted BSSIDs we will have a BSS generated from the
1699 * MBSSID element(s) using inheritance rules. Included in the elements
1700 * is the MBSSID Index Element. The RNR is copied from the Beacon/Probe
1701 * Response frame that was send by the transmitting BSSID. As such, the
1702 * reported AP MLD ID in the RNR will match the value in the MBSSID
1703 * Index Element.
1704 */
1705 elem = (const struct element *)
1706 wpa_bss_get_ie(bss, WLAN_EID_MULTIPLE_BSSID_INDEX);
1707 if (elem && elem->datalen >= 1)
1708 mbssid_idx = elem->data[0];
1709
1710 for_each_element_id(elem, WLAN_EID_REDUCED_NEIGHBOR_REPORT,
1711 wpa_bss_ie_ptr(bss),
1712 bss->ie_len ? bss->ie_len : bss->beacon_ie_len) {
1713 const struct ieee80211_neighbor_ap_info *ap_info;
1714 const u8 *pos = elem->data;
1715 size_t len = elem->datalen;
1716
1717 /* RNR IE may contain more than one Neighbor AP Info */
1718 while (sizeof(*ap_info) <= len) {
1719 size_t ap_info_len = sizeof(*ap_info);
1720 u8 count;
1721
1722 ap_info = (const struct ieee80211_neighbor_ap_info *)
1723 pos;
1724 count = RNR_TBTT_INFO_COUNT_VAL(ap_info->tbtt_info_hdr) + 1;
1725 ap_info_len += count * ap_info->tbtt_info_len;
1726
1727 if (ap_info_len > len)
1728 goto out;
1729
1730 wpa_bss_parse_ml_rnr_ap_info(wpa_s, bss, mbssid_idx,
1731 ap_info, len, &seen,
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001732 &missing, ssid);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001733
1734 pos += ap_info_len;
1735 len -= ap_info_len;
1736 }
1737 }
1738
Sunil Ravi88611412024-06-28 17:34:56 +00001739 wpa_printf(MSG_DEBUG, "MLD: n_mld_links=%u (unresolved: 0x%04hx)",
1740 bss->n_mld_links, missing);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001741
Sunil Ravi88611412024-06-28 17:34:56 +00001742 for (i = 0; i < bss->n_mld_links; i++) {
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001743 wpa_printf(MSG_DEBUG, "MLD: link=%u, bssid=" MACSTR,
Sunil Ravi88611412024-06-28 17:34:56 +00001744 bss->mld_links[i].link_id,
1745 MAC2STR(bss->mld_links[i].bssid));
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001746 }
1747
1748 if (missing_links)
1749 *missing_links = missing;
1750
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001751 if (ap_mld_id)
1752 *ap_mld_id = mbssid_idx;
1753
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001754 ret = 0;
1755out:
1756 wpabuf_free(mlbuf);
1757 return ret;
1758}
1759
1760
1761/*
1762 * wpa_bss_parse_reconf_ml_element - Parse the Reconfiguration ML element
1763 * @wpa_s: Pointer to wpa_supplicant data
1764 * @bss: BSS table entry
1765 * Returns: The bitmap of links that are going to be removed
1766 */
1767u16 wpa_bss_parse_reconf_ml_element(struct wpa_supplicant *wpa_s,
1768 struct wpa_bss *bss)
1769{
1770 struct ieee802_11_elems elems;
1771 struct wpabuf *mlbuf;
1772 const u8 *pos = wpa_bss_ie_ptr(bss);
1773 size_t len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len;
1774 const struct ieee80211_eht_ml *ml;
1775 u16 removed_links = 0;
1776 u8 ml_common_len;
1777
1778 if (ieee802_11_parse_elems(pos, len, &elems, 1) == ParseFailed)
1779 return 0;
1780
1781 if (!elems.reconf_mle || !elems.reconf_mle_len)
1782 return 0;
1783
Sunil Ravib0ac25f2024-07-12 01:42:03 +00001784 mlbuf = ieee802_11_defrag(elems.reconf_mle, elems.reconf_mle_len, true);
Sunil Ravi2a14cf12023-11-21 00:54:38 +00001785 if (!mlbuf)
1786 return 0;
1787
1788 ml = (const struct ieee80211_eht_ml *) wpabuf_head(mlbuf);
1789 len = wpabuf_len(mlbuf);
1790
1791 if (len < sizeof(*ml))
1792 goto out;
1793
1794 ml_common_len = 1;
1795 if (ml->ml_control & RECONF_MULTI_LINK_CTRL_PRES_MLD_MAC_ADDR)
1796 ml_common_len += ETH_ALEN;
1797
1798 if (len < sizeof(*ml) + ml_common_len) {
1799 wpa_printf(MSG_DEBUG,
1800 "MLD: Unexpected Reconfiguration ML element length: (%zu < %zu)",
1801 len, sizeof(*ml) + ml_common_len);
1802 goto out;
1803 }
1804
1805 pos = ml->variable + ml_common_len;
1806 len -= sizeof(*ml) + ml_common_len;
1807
1808 while (len >= 2 + sizeof(struct ieee80211_eht_per_sta_profile)) {
1809 size_t sub_elem_len = *(pos + 1);
1810
1811 if (2 + sub_elem_len > len) {
1812 wpa_printf(MSG_DEBUG,
1813 "MLD: Invalid link info len: %zu %zu",
1814 2 + sub_elem_len, len);
1815 goto out;
1816 }
1817
1818 if (*pos == EHT_ML_SUB_ELEM_PER_STA_PROFILE) {
1819 const struct ieee80211_eht_per_sta_profile *sta_prof =
1820 (const struct ieee80211_eht_per_sta_profile *)
1821 (pos + 2);
1822 u16 control = le_to_host16(sta_prof->sta_control);
1823 u8 link_id;
1824
1825 link_id = control & EHT_PER_STA_RECONF_CTRL_LINK_ID_MSK;
1826 removed_links |= BIT(link_id);
1827 }
1828
1829 pos += 2 + sub_elem_len;
1830 len -= 2 + sub_elem_len;
1831 }
1832
1833 wpa_printf(MSG_DEBUG, "MLD: Reconfiguration: removed_links=0x%x",
1834 removed_links);
1835out:
1836 wpabuf_free(mlbuf);
1837 return removed_links;
1838}