1 /*
2 * BSS table
3 * Copyright (c) 2009-2019, Jouni Malinen <j@w1.fi>
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9 #include "utils/includes.h"
10
11 #include "utils/common.h"
12 #include "common/ieee802_11_defs.h"
13 #include "common/ieee802_11_common.h"
14 #include "drivers/driver.h"
15 #include "eap_peer/eap.h"
16 #include "wpa_supplicant_i.h"
17 #include "scan.h"
18 #include "bss.h"
19 #ifdef ESP_SUPPLICANT
20 #include "esp_wifi_driver.h"
21 #endif
22
23 #define MAX_BSS_COUNT 20
24
wpa_bss_remove(struct wpa_supplicant * wpa_s,struct wpa_bss * bss,const char * reason)25 void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
26 const char *reason)
27 {
28 if (wpa_s->last_scan_res) {
29 unsigned int i;
30 for (i = 0; i < wpa_s->last_scan_res_used; i++) {
31 if (wpa_s->last_scan_res[i] == bss) {
32 os_memmove(&wpa_s->last_scan_res[i],
33 &wpa_s->last_scan_res[i + 1],
34 (wpa_s->last_scan_res_used - i - 1)
35 * sizeof(struct wpa_bss *));
36 wpa_s->last_scan_res_used--;
37 break;
38 }
39 }
40 }
41 dl_list_del(&bss->list);
42 dl_list_del(&bss->list_id);
43 wpa_s->num_bss--;
44 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR
45 " SSID '%s' due to %s", bss->id, MAC2STR(bss->bssid),
46 wpa_ssid_txt(bss->ssid, bss->ssid_len), reason);
47 os_free(bss);
48 }
49
50
51 /**
52 * wpa_bss_get - Fetch a BSS table entry based on BSSID and SSID
53 * @wpa_s: Pointer to wpa_supplicant data
54 * @bssid: BSSID
55 * @ssid: SSID
56 * @ssid_len: Length of @ssid
57 * Returns: Pointer to the BSS entry or %NULL if not found
58 */
wpa_bss_get(struct wpa_supplicant * wpa_s,const u8 * bssid,const u8 * ssid,size_t ssid_len)59 struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
60 const u8 *ssid, size_t ssid_len)
61 {
62 struct wpa_bss *bss;
63 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
64 if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
65 bss->ssid_len == ssid_len &&
66 os_memcmp(bss->ssid, ssid, ssid_len) == 0)
67 return bss;
68 }
69 return NULL;
70 }
71
72
calculate_update_time(const struct os_reltime * fetch_time,unsigned int age_ms,struct os_reltime * update_time)73 void calculate_update_time(const struct os_reltime *fetch_time,
74 unsigned int age_ms,
75 struct os_reltime *update_time)
76 {
77 os_time_t usec;
78
79 update_time->sec = fetch_time->sec;
80 update_time->usec = fetch_time->usec;
81 update_time->sec -= age_ms / 1000;
82 usec = (age_ms % 1000) * 1000;
83 if (update_time->usec < usec) {
84 update_time->sec--;
85 update_time->usec += 1000000;
86 }
87 update_time->usec -= usec;
88 }
89
90
wpa_bss_copy_res(struct wpa_bss * dst,struct wpa_scan_res * src,struct os_reltime * fetch_time)91 static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src,
92 struct os_reltime *fetch_time)
93 {
94 dst->flags = src->flags;
95 os_memcpy(dst->bssid, src->bssid, ETH_ALEN);
96 dst->channel = src->chan;
97 dst->beacon_int = src->beacon_int;
98 dst->caps = src->caps;
99 dst->noise = src->noise;
100 dst->level = src->level;
101 dst->tsf = src->tsf;
102 dst->parent_tsf = src->parent_tsf;
103
104 calculate_update_time(fetch_time, src->age, &dst->last_update);
105 }
106
107 #ifdef ESP_SUPPLICANT
wpa_bss_known(struct wpa_supplicant * wpa_s,struct wpa_bss * bss)108 static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
109 {
110 struct wifi_ssid *ssid = esp_wifi_sta_get_prof_ssid_internal();
111
112 if (ssid->ssid == NULL || ssid->len == 0)
113 return 0;
114 if (ssid->len == bss->ssid_len &&
115 os_memcmp(ssid->ssid, bss->ssid, ssid->len) == 0)
116 return 1;
117 return 0;
118 }
119 #endif
120
wpa_bss_in_use(struct wpa_supplicant * wpa_s,struct wpa_bss * bss)121 static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
122 {
123 if (bss == wpa_s->current_bss)
124 return 1;
125
126 #ifndef ESP_SUPPLICANT
127 if (wpa_s->current_bss &&
128 (bss->ssid_len != wpa_s->current_bss->ssid_len ||
129 os_memcmp(bss->ssid, wpa_s->current_bss->ssid,
130 bss->ssid_len) != 0))
131 return 0; /* SSID has changed */
132
133 return !is_zero_ether_addr(bss->bssid) && wpa_s->current_bss->bssid &&
134 (os_memcmp(bss->bssid, wpa_s->current_bss->bssid, ETH_ALEN) == 0);
135 #else
136 return 0;
137 #endif
138 }
139
wpa_bss_remove_oldest_unknown(struct wpa_supplicant * wpa_s)140 static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s)
141 {
142 struct wpa_bss *bss;
143
144 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
145 if (!wpa_bss_known(wpa_s, bss)) {
146 wpa_bss_remove(wpa_s, bss, __func__);
147 return 0;
148 }
149 }
150
151 return -1;
152 }
153
wpa_bss_remove_oldest(struct wpa_supplicant * wpa_s)154 static int wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s)
155 {
156 struct wpa_bss *bss;
157
158 /*
159 * Remove the oldest entry that does not match with any configured
160 * network.
161 */
162 if (wpa_bss_remove_oldest_unknown(wpa_s) == 0)
163 return 0;
164
165 /*
166 * Remove the oldest entry that isn't currently in use.
167 */
168 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
169 if (!wpa_bss_in_use(wpa_s, bss)) {
170 wpa_bss_remove(wpa_s, bss, __func__);
171 return 0;
172 }
173 }
174
175 return -1;
176 }
177
wpa_bss_add(struct wpa_supplicant * wpa_s,const u8 * ssid,size_t ssid_len,struct wpa_scan_res * res,struct os_reltime * fetch_time)178 static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
179 const u8 *ssid, size_t ssid_len,
180 struct wpa_scan_res *res,
181 struct os_reltime *fetch_time)
182 {
183 struct wpa_bss *bss;
184
185 if ((wpa_s->num_bss + 1 > MAX_BSS_COUNT) &&
186 (wpa_bss_remove_oldest(wpa_s) < 0)) {
187 wpa_printf(MSG_ERROR,
188 "Failed to clean older entries, rejecting scan result");
189 return NULL;
190 }
191
192 bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len);
193 if (bss == NULL)
194 return NULL;
195 bss->id = wpa_s->bss_next_id++;
196 bss->last_update_idx = wpa_s->bss_update_idx;
197 wpa_bss_copy_res(bss, res, fetch_time);
198 os_memcpy(bss->ssid, ssid, ssid_len);
199 bss->ssid_len = ssid_len;
200 bss->ie_len = res->ie_len;
201 bss->beacon_ie_len = res->beacon_ie_len;
202 os_memcpy(bss->ies, res + 1, res->ie_len + res->beacon_ie_len);
203
204 dl_list_add_tail(&wpa_s->bss, &bss->list);
205 dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
206 wpa_s->num_bss++;
207 wpa_dbg(wpa_s, MSG_INFO, "BSS: Add new id %u BSSID " MACSTR
208 " SSID '%s' chan %d",
209 bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len),
210 bss->channel);
211 return bss;
212 }
213
214 static struct wpa_bss *
wpa_bss_update(struct wpa_supplicant * wpa_s,struct wpa_bss * bss,struct wpa_scan_res * res,struct os_reltime * fetch_time)215 wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
216 struct wpa_scan_res *res, struct os_reltime *fetch_time)
217 {
218 if (bss->last_update_idx == wpa_s->bss_update_idx) {
219 struct os_reltime update_time;
220
221 /*
222 * Some drivers (e.g., cfg80211) include multiple BSS entries
223 * for the same BSS if that BSS's channel changes. The BSS list
224 * implementation in wpa_supplicant does not do that and we need
225 * to filter out the obsolete results here to make sure only the
226 * most current BSS information remains in the table.
227 */
228 wpa_printf(MSG_DEBUG, "BSS: " MACSTR
229 " has multiple entries in the scan results - select the most current one",
230 MAC2STR(bss->bssid));
231 calculate_update_time(fetch_time, res->age, &update_time);
232 wpa_printf(MSG_DEBUG,
233 "Accept this BSS entry since it looks more current than the previous update");
234 }
235
236 bss->last_update_idx = wpa_s->bss_update_idx;
237 wpa_bss_copy_res(bss, res, fetch_time);
238 /* Move the entry to the end of the list */
239 dl_list_del(&bss->list);
240 if (bss->ie_len + bss->beacon_ie_len >=
241 res->ie_len + res->beacon_ie_len) {
242 os_memcpy(bss->ies, res + 1, res->ie_len + res->beacon_ie_len);
243 bss->ie_len = res->ie_len;
244 bss->beacon_ie_len = res->beacon_ie_len;
245 } else {
246 struct wpa_bss *nbss;
247 struct dl_list *prev = bss->list_id.prev;
248 dl_list_del(&bss->list_id);
249 nbss = os_realloc(bss, sizeof(*bss) + res->ie_len +
250 res->beacon_ie_len);
251 if (nbss) {
252 unsigned int i;
253 for (i = 0; i < wpa_s->last_scan_res_used; i++) {
254 if (wpa_s->last_scan_res[i] == bss) {
255 wpa_s->last_scan_res[i] = nbss;
256 break;
257 }
258 }
259 if (wpa_s->current_bss == bss)
260 wpa_s->current_bss = nbss;
261 bss = nbss;
262 os_memcpy(bss->ies, res + 1,
263 res->ie_len + res->beacon_ie_len);
264 bss->ie_len = res->ie_len;
265 bss->beacon_ie_len = res->beacon_ie_len;
266 }
267 dl_list_add(prev, &bss->list_id);
268 }
269 dl_list_add_tail(&wpa_s->bss, &bss->list);
270
271 return bss;
272 }
273
274
275 /**
276 * wpa_bss_update_start - Start a BSS table update from scan results
277 * @wpa_s: Pointer to wpa_supplicant data
278 *
279 * This function is called at the start of each BSS table update round for new
280 * scan results. The actual scan result entries are indicated with calls to
281 * wpa_bss_update_scan_res() and the update round is finished with a call to
282 * wpa_bss_update_end().
283 */
wpa_bss_update_start(struct wpa_supplicant * wpa_s)284 void wpa_bss_update_start(struct wpa_supplicant *wpa_s)
285 {
286 wpa_s->bss_update_idx++;
287 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u",
288 wpa_s->bss_update_idx);
289 wpa_s->last_scan_res_used = 0;
290 }
291
292
293 /**
294 * wpa_bss_update_scan_res - Update a BSS table entry based on a scan result
295 * @wpa_s: Pointer to wpa_supplicant data
296 * @res: Scan result
297 * @fetch_time: Time when the result was fetched from the driver
298 *
299 * This function updates a BSS table entry (or adds one) based on a scan result.
300 * This is called separately for each scan result between the calls to
301 * wpa_bss_update_start() and wpa_bss_update_end().
302 */
wpa_bss_update_scan_res(struct wpa_supplicant * wpa_s,struct wpa_scan_res * res,struct os_reltime * fetch_time)303 void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
304 struct wpa_scan_res *res,
305 struct os_reltime *fetch_time)
306 {
307 const u8 *ssid;
308 struct wpa_bss *bss;
309
310 ssid = wpa_scan_get_ie(res, WLAN_EID_SSID);
311 if (ssid == NULL) {
312 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for "
313 MACSTR, MAC2STR(res->bssid));
314 return;
315 }
316 if (ssid[1] > SSID_MAX_LEN) {
317 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for "
318 MACSTR, MAC2STR(res->bssid));
319 return;
320 }
321
322 /* TODO: add option for ignoring BSSes we are not interested in
323 * (to save memory) */
324
325 bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
326 if (bss == NULL)
327 bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res, fetch_time);
328 else {
329 bss = wpa_bss_update(wpa_s, bss, res, fetch_time);
330 if (wpa_s->last_scan_res) {
331 unsigned int i;
332 for (i = 0; i < wpa_s->last_scan_res_used; i++) {
333 if (bss == wpa_s->last_scan_res[i]) {
334 /* Already in the list */
335 return;
336 }
337 }
338 }
339 }
340
341 if (bss == NULL)
342 return;
343 if (wpa_s->last_scan_res_used >= wpa_s->last_scan_res_size) {
344 struct wpa_bss **n;
345 unsigned int siz;
346 if (wpa_s->last_scan_res_size == 0)
347 siz = 32;
348 else
349 siz = wpa_s->last_scan_res_size * 2;
350 n = os_realloc_array(wpa_s->last_scan_res, siz,
351 sizeof(struct wpa_bss *));
352 if (n == NULL)
353 return;
354 wpa_s->last_scan_res = n;
355 wpa_s->last_scan_res_size = siz;
356 }
357
358 if (wpa_s->last_scan_res)
359 wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss;
360 }
361
362
363 /**
364 * wpa_bss_update_end - End a BSS table update from scan results
365 * @wpa_s: Pointer to wpa_supplicant data
366 * @info: Information about scan parameters
367 * @new_scan: Whether this update round was based on a new scan
368 *
369 * This function is called at the end of each BSS table update round for new
370 * scan results. The start of the update was indicated with a call to
371 * wpa_bss_update_start().
372 */
wpa_bss_update_end(struct wpa_supplicant * wpa_s)373 void wpa_bss_update_end(struct wpa_supplicant *wpa_s)
374 {
375 os_get_reltime(&wpa_s->last_scan);
376 }
377
378 /**
379 * wpa_bss_init - Initialize BSS table
380 * @wpa_s: Pointer to wpa_supplicant data
381 * Returns: 0 on success, -1 on failure
382 *
383 * This prepares BSS table lists and timer for periodic updates. The BSS table
384 * is deinitialized with wpa_bss_deinit() once not needed anymore.
385 */
wpa_bss_init(struct wpa_supplicant * wpa_s)386 int wpa_bss_init(struct wpa_supplicant *wpa_s)
387 {
388 dl_list_init(&wpa_s->bss);
389 dl_list_init(&wpa_s->bss_id);
390 return 0;
391 }
392
393
394 /**
395 * wpa_bss_flush - Flush all unused BSS entries
396 * @wpa_s: Pointer to wpa_supplicant data
397 */
wpa_bss_flush(struct wpa_supplicant * wpa_s)398 void wpa_bss_flush(struct wpa_supplicant *wpa_s)
399 {
400 struct wpa_bss *bss, *n;
401
402 if (wpa_s->bss.next == NULL)
403 return; /* BSS table not yet initialized */
404
405 dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
406 if (wpa_bss_in_use(wpa_s, bss))
407 continue;
408 wpa_bss_remove(wpa_s, bss, __func__);
409 }
410 }
411
412
413 /**
414 * wpa_bss_deinit - Deinitialize BSS table
415 * @wpa_s: Pointer to wpa_supplicant data
416 */
wpa_bss_deinit(struct wpa_supplicant * wpa_s)417 void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
418 {
419 wpa_bss_flush(wpa_s);
420 }
421
422
423 /**
424 * wpa_bss_get_bssid - Fetch a BSS table entry based on BSSID
425 * @wpa_s: Pointer to wpa_supplicant data
426 * @bssid: BSSID
427 * Returns: Pointer to the BSS entry or %NULL if not found
428 */
wpa_bss_get_bssid(struct wpa_supplicant * wpa_s,const u8 * bssid)429 struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
430 const u8 *bssid)
431 {
432 struct wpa_bss *bss;
433 dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
434 if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
435 return bss;
436 }
437 return NULL;
438 }
439
440
441 /**
442 * wpa_bss_get_next_bss - Fetch a next BSS table entry from the list
443 * @wpa_s: Pointer to wpa_supplicant data
444 * @bss: BSS
445 * Returns: Pointer to the BSS entry or %NULL if not found
446 */
wpa_bss_get_next_bss(struct wpa_supplicant * wpa_s,struct wpa_bss * prev_bss)447 struct wpa_bss * wpa_bss_get_next_bss(struct wpa_supplicant *wpa_s,
448 struct wpa_bss *prev_bss)
449 {
450 struct wpa_bss *bss;
451
452 if (!prev_bss)
453 return dl_list_first(&wpa_s->bss, struct wpa_bss, list);
454 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
455 if (os_memcmp(bss->bssid, prev_bss->bssid, ETH_ALEN) == 0)
456 return dl_list_entry(bss->list.next, struct wpa_bss, list);
457 }
458 return NULL;
459 }
460
461 /**
462 * wpa_bss_get_ie - Fetch a specified information element from a BSS entry
463 * @bss: BSS table entry
464 * @ie: Information element identitifier (WLAN_EID_*)
465 * Returns: Pointer to the information element (id field) or %NULL if not found
466 *
467 * This function returns the first matching information element in the BSS
468 * entry.
469 */
wpa_bss_get_ie(const struct wpa_bss * bss,u8 ie)470 const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie)
471 {
472 return get_ie((const u8 *) (bss + 1), bss->ie_len, ie);
473 }
474
475 /**
476 * wpa_bss_get_vendor_ie - Fetch a vendor information element from a BSS entry
477 * @bss: BSS table entry
478 * @vendor_type: Vendor type (four octets starting the IE payload)
479 * Returns: Pointer to the information element (id field) or %NULL if not found
480 *
481 * This function returns the first matching information element in the BSS
482 * entry.
483 */
wpa_bss_get_vendor_ie(const struct wpa_bss * bss,u32 vendor_type)484 const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type)
485 {
486 const u8 *ies;
487 const struct element *elem;
488
489 ies = wpa_bss_ie_ptr(bss);
490
491 for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, bss->ie_len) {
492 if (elem->datalen >= 4 &&
493 vendor_type == WPA_GET_BE32(elem->data))
494 return &elem->id;
495 }
496
497 return NULL;
498 }
499
wpa_bss_ext_capab(const struct wpa_bss * bss,unsigned int capab)500 int wpa_bss_ext_capab(const struct wpa_bss *bss, unsigned int capab)
501 {
502 return ieee802_11_ext_capab(wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB),
503 capab);
504 }
505