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_supplicant/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 if (wpa_s->current_bss &&
127 (bss->ssid_len != wpa_s->current_bss->ssid_len ||
128 os_memcmp(bss->ssid, wpa_s->current_bss->ssid,
129 bss->ssid_len) != 0))
130 return 0; /* SSID has changed */
131
132 return !is_zero_ether_addr(bss->bssid) && wpa_s->current_bss->bssid &&
133 (os_memcmp(bss->bssid, wpa_s->current_bss->bssid, ETH_ALEN) == 0);
134 }
135
wpa_bss_remove_oldest_unknown(struct wpa_supplicant * wpa_s)136 static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s)
137 {
138 struct wpa_bss *bss;
139
140 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
141 if (!wpa_bss_known(wpa_s, bss)) {
142 wpa_bss_remove(wpa_s, bss, __func__);
143 return 0;
144 }
145 }
146
147 return -1;
148 }
149
wpa_bss_remove_oldest(struct wpa_supplicant * wpa_s)150 static int wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s)
151 {
152 struct wpa_bss *bss;
153
154 /*
155 * Remove the oldest entry that does not match with any configured
156 * network.
157 */
158 if (wpa_bss_remove_oldest_unknown(wpa_s) == 0)
159 return 0;
160
161 /*
162 * Remove the oldest entry that isn't currently in use.
163 */
164 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
165 if (!wpa_bss_in_use(wpa_s, bss)) {
166 wpa_bss_remove(wpa_s, bss, __func__);
167 return 0;
168 }
169 }
170
171 return -1;
172 }
173
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)174 static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
175 const u8 *ssid, size_t ssid_len,
176 struct wpa_scan_res *res,
177 struct os_reltime *fetch_time)
178 {
179 struct wpa_bss *bss;
180
181 if ((wpa_s->num_bss + 1 > MAX_BSS_COUNT) &&
182 (wpa_bss_remove_oldest(wpa_s) < 0)) {
183 wpa_printf(MSG_ERROR,
184 "Failed to clean older entries, rejecting scan result");
185 return NULL;
186 }
187
188 bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len);
189 if (bss == NULL)
190 return NULL;
191 bss->id = wpa_s->bss_next_id++;
192 bss->last_update_idx = wpa_s->bss_update_idx;
193 wpa_bss_copy_res(bss, res, fetch_time);
194 os_memcpy(bss->ssid, ssid, ssid_len);
195 bss->ssid_len = ssid_len;
196 bss->ie_len = res->ie_len;
197 bss->beacon_ie_len = res->beacon_ie_len;
198 os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
199
200 dl_list_add_tail(&wpa_s->bss, &bss->list);
201 dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
202 wpa_s->num_bss++;
203 wpa_dbg(wpa_s, MSG_INFO, "BSS: Add new id %u BSSID " MACSTR
204 " SSID '%s' chan %d",
205 bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len),
206 bss->channel);
207 return bss;
208 }
209
210 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)211 wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
212 struct wpa_scan_res *res, struct os_reltime *fetch_time)
213 {
214 if (bss->last_update_idx == wpa_s->bss_update_idx) {
215 struct os_reltime update_time;
216
217 /*
218 * Some drivers (e.g., cfg80211) include multiple BSS entries
219 * for the same BSS if that BSS's channel changes. The BSS list
220 * implementation in wpa_supplicant does not do that and we need
221 * to filter out the obsolete results here to make sure only the
222 * most current BSS information remains in the table.
223 */
224 wpa_printf(MSG_DEBUG, "BSS: " MACSTR
225 " has multiple entries in the scan results - select the most current one",
226 MAC2STR(bss->bssid));
227 calculate_update_time(fetch_time, res->age, &update_time);
228 wpa_printf(MSG_DEBUG,
229 "Accept this BSS entry since it looks more current than the previous update");
230 }
231
232 bss->last_update_idx = wpa_s->bss_update_idx;
233 wpa_bss_copy_res(bss, res, fetch_time);
234 /* Move the entry to the end of the list */
235 dl_list_del(&bss->list);
236 if (bss->ie_len + bss->beacon_ie_len >=
237 res->ie_len + res->beacon_ie_len) {
238 os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
239 bss->ie_len = res->ie_len;
240 bss->beacon_ie_len = res->beacon_ie_len;
241 } else {
242 struct wpa_bss *nbss;
243 struct dl_list *prev = bss->list_id.prev;
244 dl_list_del(&bss->list_id);
245 nbss = os_realloc(bss, sizeof(*bss) + res->ie_len +
246 res->beacon_ie_len);
247 if (nbss) {
248 unsigned int i;
249 for (i = 0; i < wpa_s->last_scan_res_used; i++) {
250 if (wpa_s->last_scan_res[i] == bss) {
251 wpa_s->last_scan_res[i] = nbss;
252 break;
253 }
254 }
255 if (wpa_s->current_bss == bss)
256 wpa_s->current_bss = nbss;
257 bss = nbss;
258 os_memcpy(bss + 1, res + 1,
259 res->ie_len + res->beacon_ie_len);
260 bss->ie_len = res->ie_len;
261 bss->beacon_ie_len = res->beacon_ie_len;
262 }
263 dl_list_add(prev, &bss->list_id);
264 }
265 dl_list_add_tail(&wpa_s->bss, &bss->list);
266
267 return bss;
268 }
269
270
271 /**
272 * wpa_bss_update_start - Start a BSS table update from scan results
273 * @wpa_s: Pointer to wpa_supplicant data
274 *
275 * This function is called at the start of each BSS table update round for new
276 * scan results. The actual scan result entries are indicated with calls to
277 * wpa_bss_update_scan_res() and the update round is finished with a call to
278 * wpa_bss_update_end().
279 */
wpa_bss_update_start(struct wpa_supplicant * wpa_s)280 void wpa_bss_update_start(struct wpa_supplicant *wpa_s)
281 {
282 wpa_s->bss_update_idx++;
283 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u",
284 wpa_s->bss_update_idx);
285 wpa_s->last_scan_res_used = 0;
286 }
287
288
289 /**
290 * wpa_bss_update_scan_res - Update a BSS table entry based on a scan result
291 * @wpa_s: Pointer to wpa_supplicant data
292 * @res: Scan result
293 * @fetch_time: Time when the result was fetched from the driver
294 *
295 * This function updates a BSS table entry (or adds one) based on a scan result.
296 * This is called separately for each scan result between the calls to
297 * wpa_bss_update_start() and wpa_bss_update_end().
298 */
wpa_bss_update_scan_res(struct wpa_supplicant * wpa_s,struct wpa_scan_res * res,struct os_reltime * fetch_time)299 void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
300 struct wpa_scan_res *res,
301 struct os_reltime *fetch_time)
302 {
303 const u8 *ssid;
304 struct wpa_bss *bss;
305
306 ssid = wpa_scan_get_ie(res, WLAN_EID_SSID);
307 if (ssid == NULL) {
308 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for "
309 MACSTR, MAC2STR(res->bssid));
310 return;
311 }
312 if (ssid[1] > SSID_MAX_LEN) {
313 wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for "
314 MACSTR, MAC2STR(res->bssid));
315 return;
316 }
317
318 /* TODO: add option for ignoring BSSes we are not interested in
319 * (to save memory) */
320
321 bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
322 if (bss == NULL)
323 bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res, fetch_time);
324 else {
325 bss = wpa_bss_update(wpa_s, bss, res, fetch_time);
326 if (wpa_s->last_scan_res) {
327 unsigned int i;
328 for (i = 0; i < wpa_s->last_scan_res_used; i++) {
329 if (bss == wpa_s->last_scan_res[i]) {
330 /* Already in the list */
331 return;
332 }
333 }
334 }
335 }
336
337 if (bss == NULL)
338 return;
339 if (wpa_s->last_scan_res_used >= wpa_s->last_scan_res_size) {
340 struct wpa_bss **n;
341 unsigned int siz;
342 if (wpa_s->last_scan_res_size == 0)
343 siz = 32;
344 else
345 siz = wpa_s->last_scan_res_size * 2;
346 n = os_realloc_array(wpa_s->last_scan_res, siz,
347 sizeof(struct wpa_bss *));
348 if (n == NULL)
349 return;
350 wpa_s->last_scan_res = n;
351 wpa_s->last_scan_res_size = siz;
352 }
353
354 if (wpa_s->last_scan_res)
355 wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss;
356 }
357
358
359 /**
360 * wpa_bss_update_end - End a BSS table update from scan results
361 * @wpa_s: Pointer to wpa_supplicant data
362 * @info: Information about scan parameters
363 * @new_scan: Whether this update round was based on a new scan
364 *
365 * This function is called at the end of each BSS table update round for new
366 * scan results. The start of the update was indicated with a call to
367 * wpa_bss_update_start().
368 */
wpa_bss_update_end(struct wpa_supplicant * wpa_s)369 void wpa_bss_update_end(struct wpa_supplicant *wpa_s)
370 {
371 os_get_reltime(&wpa_s->last_scan);
372 }
373
374 /**
375 * wpa_bss_init - Initialize BSS table
376 * @wpa_s: Pointer to wpa_supplicant data
377 * Returns: 0 on success, -1 on failure
378 *
379 * This prepares BSS table lists and timer for periodic updates. The BSS table
380 * is deinitialized with wpa_bss_deinit() once not needed anymore.
381 */
wpa_bss_init(struct wpa_supplicant * wpa_s)382 int wpa_bss_init(struct wpa_supplicant *wpa_s)
383 {
384 dl_list_init(&wpa_s->bss);
385 dl_list_init(&wpa_s->bss_id);
386 return 0;
387 }
388
389
390 /**
391 * wpa_bss_flush - Flush all unused BSS entries
392 * @wpa_s: Pointer to wpa_supplicant data
393 */
wpa_bss_flush(struct wpa_supplicant * wpa_s)394 void wpa_bss_flush(struct wpa_supplicant *wpa_s)
395 {
396 struct wpa_bss *bss, *n;
397
398 if (wpa_s->bss.next == NULL)
399 return; /* BSS table not yet initialized */
400
401 dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
402 if (wpa_bss_in_use(wpa_s, bss))
403 continue;
404 wpa_bss_remove(wpa_s, bss, __func__);
405 }
406 }
407
408
409 /**
410 * wpa_bss_deinit - Deinitialize BSS table
411 * @wpa_s: Pointer to wpa_supplicant data
412 */
wpa_bss_deinit(struct wpa_supplicant * wpa_s)413 void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
414 {
415 wpa_bss_flush(wpa_s);
416 }
417
418
419 /**
420 * wpa_bss_get_bssid - Fetch a BSS table entry based on BSSID
421 * @wpa_s: Pointer to wpa_supplicant data
422 * @bssid: BSSID
423 * Returns: Pointer to the BSS entry or %NULL if not found
424 */
wpa_bss_get_bssid(struct wpa_supplicant * wpa_s,const u8 * bssid)425 struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
426 const u8 *bssid)
427 {
428 struct wpa_bss *bss;
429 dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
430 if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
431 return bss;
432 }
433 return NULL;
434 }
435
436
437 /**
438 * wpa_bss_get_next_bss - Fetch a next BSS table entry from the list
439 * @wpa_s: Pointer to wpa_supplicant data
440 * @bss: BSS
441 * Returns: Pointer to the BSS entry or %NULL if not found
442 */
wpa_bss_get_next_bss(struct wpa_supplicant * wpa_s,struct wpa_bss * prev_bss)443 struct wpa_bss * wpa_bss_get_next_bss(struct wpa_supplicant *wpa_s,
444 struct wpa_bss *prev_bss)
445 {
446 struct wpa_bss *bss;
447
448 if (!prev_bss)
449 return dl_list_first(&wpa_s->bss, struct wpa_bss, list);
450 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
451 if (os_memcmp(bss->bssid, prev_bss->bssid, ETH_ALEN) == 0)
452 return dl_list_entry(bss->list.next, struct wpa_bss, list);
453 }
454 return NULL;
455 }
456
457 /**
458 * wpa_bss_get_ie - Fetch a specified information element from a BSS entry
459 * @bss: BSS table entry
460 * @ie: Information element identitifier (WLAN_EID_*)
461 * Returns: Pointer to the information element (id field) or %NULL if not found
462 *
463 * This function returns the first matching information element in the BSS
464 * entry.
465 */
wpa_bss_get_ie(const struct wpa_bss * bss,u8 ie)466 const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie)
467 {
468 return get_ie((const u8 *) (bss + 1), bss->ie_len, ie);
469 }
470
wpa_bss_ext_capab(const struct wpa_bss * bss,unsigned int capab)471 int wpa_bss_ext_capab(const struct wpa_bss *bss, unsigned int capab)
472 {
473 return ieee802_11_ext_capab(wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB),
474 capab);
475 }
476