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