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->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