Branch data Line data Source code
1 : : /*
2 : : * BSS table
3 : : * Copyright (c) 2009-2012, 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 "utils/eloop.h"
13 : : #include "common/ieee802_11_defs.h"
14 : : #include "drivers/driver.h"
15 : : #include "wpa_supplicant_i.h"
16 : : #include "config.h"
17 : : #include "notify.h"
18 : : #include "scan.h"
19 : : #include "bss.h"
20 : :
21 : :
22 : : /**
23 : : * WPA_BSS_EXPIRATION_PERIOD - Period of expiration run in seconds
24 : : */
25 : : #define WPA_BSS_EXPIRATION_PERIOD 10
26 : :
27 : : #define WPA_BSS_FREQ_CHANGED_FLAG BIT(0)
28 : : #define WPA_BSS_SIGNAL_CHANGED_FLAG BIT(1)
29 : : #define WPA_BSS_PRIVACY_CHANGED_FLAG BIT(2)
30 : : #define WPA_BSS_MODE_CHANGED_FLAG BIT(3)
31 : : #define WPA_BSS_WPAIE_CHANGED_FLAG BIT(4)
32 : : #define WPA_BSS_RSNIE_CHANGED_FLAG BIT(5)
33 : : #define WPA_BSS_WPS_CHANGED_FLAG BIT(6)
34 : : #define WPA_BSS_RATES_CHANGED_FLAG BIT(7)
35 : : #define WPA_BSS_IES_CHANGED_FLAG BIT(8)
36 : :
37 : :
38 : 780 : static void wpa_bss_set_hessid(struct wpa_bss *bss)
39 : : {
40 : : #ifdef CONFIG_INTERWORKING
41 : 780 : const u8 *ie = wpa_bss_get_ie(bss, WLAN_EID_INTERWORKING);
42 [ + + ][ + + ]: 780 : if (ie == NULL || (ie[1] != 7 && ie[1] != 9)) {
[ + + ]
43 : 737 : os_memset(bss->hessid, 0, ETH_ALEN);
44 : 780 : return;
45 : : }
46 [ + + ]: 43 : if (ie[1] == 7)
47 : 1 : os_memcpy(bss->hessid, ie + 3, ETH_ALEN);
48 : : else
49 : 42 : os_memcpy(bss->hessid, ie + 5, ETH_ALEN);
50 : : #endif /* CONFIG_INTERWORKING */
51 : : }
52 : :
53 : :
54 : : /**
55 : : * wpa_bss_anqp_alloc - Allocate ANQP data structure for a BSS entry
56 : : * Returns: Allocated ANQP data structure or %NULL on failure
57 : : *
58 : : * The allocated ANQP data structure has its users count set to 1. It may be
59 : : * shared by multiple BSS entries and each shared entry is freed with
60 : : * wpa_bss_anqp_free().
61 : : */
62 : 87 : struct wpa_bss_anqp * wpa_bss_anqp_alloc(void)
63 : : {
64 : : struct wpa_bss_anqp *anqp;
65 : 87 : anqp = os_zalloc(sizeof(*anqp));
66 [ - + ]: 87 : if (anqp == NULL)
67 : 0 : return NULL;
68 : 87 : anqp->users = 1;
69 : 87 : return anqp;
70 : : }
71 : :
72 : :
73 : : /**
74 : : * wpa_bss_anqp_clone - Clone an ANQP data structure
75 : : * @anqp: ANQP data structure from wpa_bss_anqp_alloc()
76 : : * Returns: Cloned ANQP data structure or %NULL on failure
77 : : */
78 : 1 : static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp)
79 : : {
80 : : struct wpa_bss_anqp *n;
81 : :
82 : 1 : n = os_zalloc(sizeof(*n));
83 [ - + ]: 1 : if (n == NULL)
84 : 0 : return NULL;
85 : :
86 : : #define ANQP_DUP(f) if (anqp->f) n->f = wpabuf_dup(anqp->f)
87 : : #ifdef CONFIG_INTERWORKING
88 [ - + ]: 1 : ANQP_DUP(venue_name);
89 [ - + ]: 1 : ANQP_DUP(network_auth_type);
90 [ - + ]: 1 : ANQP_DUP(roaming_consortium);
91 [ - + ]: 1 : ANQP_DUP(ip_addr_type_availability);
92 [ + - ]: 1 : ANQP_DUP(nai_realm);
93 [ - + ]: 1 : ANQP_DUP(anqp_3gpp);
94 [ + - ]: 1 : ANQP_DUP(domain_name);
95 : : #endif /* CONFIG_INTERWORKING */
96 : : #ifdef CONFIG_HS20
97 [ - + ]: 1 : ANQP_DUP(hs20_operator_friendly_name);
98 [ - + ]: 1 : ANQP_DUP(hs20_wan_metrics);
99 [ - + ]: 1 : ANQP_DUP(hs20_connection_capability);
100 [ - + ]: 1 : ANQP_DUP(hs20_operating_class);
101 [ - + ]: 1 : ANQP_DUP(hs20_osu_providers_list);
102 : : #endif /* CONFIG_HS20 */
103 : : #undef ANQP_DUP
104 : :
105 : 1 : return n;
106 : : }
107 : :
108 : :
109 : : /**
110 : : * wpa_bss_anqp_unshare_alloc - Unshare ANQP data (if shared) in a BSS entry
111 : : * @bss: BSS entry
112 : : * Returns: 0 on success, -1 on failure
113 : : *
114 : : * This function ensures the specific BSS entry has an ANQP data structure that
115 : : * is not shared with any other BSS entry.
116 : : */
117 : 21 : int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss)
118 : : {
119 : : struct wpa_bss_anqp *anqp;
120 : :
121 [ + + ][ + + ]: 21 : if (bss->anqp && bss->anqp->users > 1) {
122 : : /* allocated, but shared - clone an unshared copy */
123 : 1 : anqp = wpa_bss_anqp_clone(bss->anqp);
124 [ - + ]: 1 : if (anqp == NULL)
125 : 0 : return -1;
126 : 1 : anqp->users = 1;
127 : 1 : bss->anqp->users--;
128 : 1 : bss->anqp = anqp;
129 : 1 : return 0;
130 : : }
131 : :
132 [ + + ]: 20 : if (bss->anqp)
133 : 12 : return 0; /* already allocated and not shared */
134 : :
135 : : /* not allocated - allocate a new storage area */
136 : 8 : bss->anqp = wpa_bss_anqp_alloc();
137 [ + - ]: 21 : return bss->anqp ? 0 : -1;
138 : : }
139 : :
140 : :
141 : : /**
142 : : * wpa_bss_anqp_free - Free an ANQP data structure
143 : : * @anqp: ANQP data structure from wpa_bss_anqp_alloc() or wpa_bss_anqp_clone()
144 : : */
145 : 734 : static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp)
146 : : {
147 [ + + ]: 734 : if (anqp == NULL)
148 : 646 : return;
149 : :
150 : 88 : anqp->users--;
151 [ - + ]: 88 : if (anqp->users > 0) {
152 : : /* Another BSS entry holds a pointer to this ANQP info */
153 : 0 : return;
154 : : }
155 : :
156 : : #ifdef CONFIG_INTERWORKING
157 : 88 : wpabuf_free(anqp->venue_name);
158 : 88 : wpabuf_free(anqp->network_auth_type);
159 : 88 : wpabuf_free(anqp->roaming_consortium);
160 : 88 : wpabuf_free(anqp->ip_addr_type_availability);
161 : 88 : wpabuf_free(anqp->nai_realm);
162 : 88 : wpabuf_free(anqp->anqp_3gpp);
163 : 88 : wpabuf_free(anqp->domain_name);
164 : : #endif /* CONFIG_INTERWORKING */
165 : : #ifdef CONFIG_HS20
166 : 88 : wpabuf_free(anqp->hs20_operator_friendly_name);
167 : 88 : wpabuf_free(anqp->hs20_wan_metrics);
168 : 88 : wpabuf_free(anqp->hs20_connection_capability);
169 : 88 : wpabuf_free(anqp->hs20_operating_class);
170 : 88 : wpabuf_free(anqp->hs20_osu_providers_list);
171 : : #endif /* CONFIG_HS20 */
172 : :
173 : 734 : os_free(anqp);
174 : : }
175 : :
176 : :
177 : 734 : static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
178 : : const char *reason)
179 : : {
180 [ + - ]: 734 : if (wpa_s->last_scan_res) {
181 : : unsigned int i;
182 [ + + ]: 779 : for (i = 0; i < wpa_s->last_scan_res_used; i++) {
183 [ + + ]: 737 : if (wpa_s->last_scan_res[i] == bss) {
184 : 692 : os_memmove(&wpa_s->last_scan_res[i],
185 : : &wpa_s->last_scan_res[i + 1],
186 : : (wpa_s->last_scan_res_used - i - 1)
187 : : * sizeof(struct wpa_bss *));
188 : 692 : wpa_s->last_scan_res_used--;
189 : 692 : break;
190 : : }
191 : : }
192 : : }
193 : 734 : dl_list_del(&bss->list);
194 : 734 : dl_list_del(&bss->list_id);
195 : 734 : wpa_s->num_bss--;
196 : 734 : wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR
197 : : " SSID '%s' due to %s", bss->id, MAC2STR(bss->bssid),
198 : : wpa_ssid_txt(bss->ssid, bss->ssid_len), reason);
199 : 734 : wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id);
200 : 734 : wpa_bss_anqp_free(bss->anqp);
201 : 734 : os_free(bss);
202 : 734 : }
203 : :
204 : :
205 : : /**
206 : : * wpa_bss_get - Fetch a BSS table entry based on BSSID and SSID
207 : : * @wpa_s: Pointer to wpa_supplicant data
208 : : * @bssid: BSSID
209 : : * @ssid: SSID
210 : : * @ssid_len: Length of @ssid
211 : : * Returns: Pointer to the BSS entry or %NULL if not found
212 : : */
213 : 1855 : struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
214 : : const u8 *ssid, size_t ssid_len)
215 : : {
216 : : struct wpa_bss *bss;
217 [ - + ]: 1855 : if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
218 : 0 : return NULL;
219 [ + + ]: 2453 : dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
220 [ + + ][ + + ]: 1594 : if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
221 [ + + ]: 1085 : bss->ssid_len == ssid_len &&
222 : 1085 : os_memcmp(bss->ssid, ssid, ssid_len) == 0)
223 : 996 : return bss;
224 : : }
225 : 1855 : return NULL;
226 : : }
227 : :
228 : :
229 : 2385 : static void calculate_update_time(const struct os_reltime *fetch_time,
230 : : unsigned int age_ms,
231 : : struct os_reltime *update_time)
232 : : {
233 : : os_time_t usec;
234 : :
235 : 2385 : update_time->sec = fetch_time->sec;
236 : 2385 : update_time->usec = fetch_time->usec;
237 : 2385 : update_time->sec -= age_ms / 1000;
238 : 2385 : usec = (age_ms % 1000) * 1000;
239 [ + + ]: 2385 : if (update_time->usec < usec) {
240 : 471 : update_time->sec--;
241 : 471 : update_time->usec += 1000000;
242 : : }
243 : 2385 : update_time->usec -= usec;
244 : 2385 : }
245 : :
246 : :
247 : 1457 : static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src,
248 : : struct os_reltime *fetch_time)
249 : : {
250 : 1457 : dst->flags = src->flags;
251 : 1457 : os_memcpy(dst->bssid, src->bssid, ETH_ALEN);
252 : 1457 : dst->freq = src->freq;
253 : 1457 : dst->beacon_int = src->beacon_int;
254 : 1457 : dst->caps = src->caps;
255 : 1457 : dst->qual = src->qual;
256 : 1457 : dst->noise = src->noise;
257 : 1457 : dst->level = src->level;
258 : 1457 : dst->tsf = src->tsf;
259 : :
260 : 1457 : calculate_update_time(fetch_time, src->age, &dst->last_update);
261 : 1457 : }
262 : :
263 : :
264 : 0 : static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
265 : : {
266 : : struct wpa_ssid *ssid;
267 : :
268 [ # # ]: 0 : for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
269 [ # # ][ # # ]: 0 : if (ssid->ssid == NULL || ssid->ssid_len == 0)
270 : 0 : continue;
271 [ # # ][ # # ]: 0 : if (ssid->ssid_len == bss->ssid_len &&
272 : 0 : os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) == 0)
273 : 0 : return 1;
274 : : }
275 : :
276 : 0 : return 0;
277 : : }
278 : :
279 : :
280 : 2398 : static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
281 : : {
282 [ + + ][ + + ]: 4687 : return bss == wpa_s->current_bss ||
283 [ + + ]: 2289 : os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
284 : 2289 : os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0;
285 : : }
286 : :
287 : :
288 : 0 : static int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s)
289 : : {
290 : : struct wpa_bss *bss;
291 : :
292 [ # # ]: 0 : dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
293 [ # # ]: 0 : if (!wpa_bss_known(wpa_s, bss)) {
294 : 0 : wpa_bss_remove(wpa_s, bss, __func__);
295 : 0 : return 0;
296 : : }
297 : : }
298 : :
299 : 0 : return -1;
300 : : }
301 : :
302 : :
303 : 0 : static int wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s)
304 : : {
305 : : struct wpa_bss *bss;
306 : :
307 : : /*
308 : : * Remove the oldest entry that does not match with any configured
309 : : * network.
310 : : */
311 [ # # ]: 0 : if (wpa_bss_remove_oldest_unknown(wpa_s) == 0)
312 : 0 : return 0;
313 : :
314 : : /*
315 : : * Remove the oldest entry that isn't currently in use.
316 : : */
317 [ # # ]: 0 : dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
318 [ # # ]: 0 : if (!wpa_bss_in_use(wpa_s, bss)) {
319 : 0 : wpa_bss_remove(wpa_s, bss, __func__);
320 : 0 : return 0;
321 : : }
322 : : }
323 : :
324 : 0 : return -1;
325 : : }
326 : :
327 : :
328 : 734 : static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
329 : : const u8 *ssid, size_t ssid_len,
330 : : struct wpa_scan_res *res,
331 : : struct os_reltime *fetch_time)
332 : : {
333 : : struct wpa_bss *bss;
334 : :
335 : 734 : bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len);
336 [ - + ]: 734 : if (bss == NULL)
337 : 0 : return NULL;
338 : 734 : bss->id = wpa_s->bss_next_id++;
339 : 734 : bss->last_update_idx = wpa_s->bss_update_idx;
340 : 734 : wpa_bss_copy_res(bss, res, fetch_time);
341 : 734 : os_memcpy(bss->ssid, ssid, ssid_len);
342 : 734 : bss->ssid_len = ssid_len;
343 : 734 : bss->ie_len = res->ie_len;
344 : 734 : bss->beacon_ie_len = res->beacon_ie_len;
345 : 734 : os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
346 : 734 : wpa_bss_set_hessid(bss);
347 : :
348 [ - + # # ]: 734 : if (wpa_s->num_bss + 1 > wpa_s->conf->bss_max_count &&
349 : 0 : wpa_bss_remove_oldest(wpa_s) != 0) {
350 : 0 : wpa_printf(MSG_ERROR, "Increasing the MAX BSS count to %d "
351 : : "because all BSSes are in use. We should normally "
352 : 0 : "not get here!", (int) wpa_s->num_bss + 1);
353 : 0 : wpa_s->conf->bss_max_count = wpa_s->num_bss + 1;
354 : : }
355 : :
356 : 734 : dl_list_add_tail(&wpa_s->bss, &bss->list);
357 : 734 : dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
358 : 734 : wpa_s->num_bss++;
359 : 734 : wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR
360 : : " SSID '%s'",
361 : : bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len));
362 : 734 : wpas_notify_bss_added(wpa_s, bss->bssid, bss->id);
363 : 734 : return bss;
364 : : }
365 : :
366 : :
367 : 230 : static int are_ies_equal(const struct wpa_bss *old,
368 : : const struct wpa_scan_res *new, u32 ie)
369 : : {
370 : : const u8 *old_ie, *new_ie;
371 : 230 : struct wpabuf *old_ie_buff = NULL;
372 : 230 : struct wpabuf *new_ie_buff = NULL;
373 : : int new_ie_len, old_ie_len, ret, is_multi;
374 : :
375 [ + + + - ]: 230 : switch (ie) {
376 : : case WPA_IE_VENDOR_TYPE:
377 : 46 : old_ie = wpa_bss_get_vendor_ie(old, ie);
378 : 46 : new_ie = wpa_scan_get_vendor_ie(new, ie);
379 : 46 : is_multi = 0;
380 : 46 : break;
381 : : case WPS_IE_VENDOR_TYPE:
382 : 46 : old_ie_buff = wpa_bss_get_vendor_ie_multi(old, ie);
383 : 46 : new_ie_buff = wpa_scan_get_vendor_ie_multi(new, ie);
384 : 46 : is_multi = 1;
385 : 46 : break;
386 : : case WLAN_EID_RSN:
387 : : case WLAN_EID_SUPP_RATES:
388 : : case WLAN_EID_EXT_SUPP_RATES:
389 : 138 : old_ie = wpa_bss_get_ie(old, ie);
390 : 138 : new_ie = wpa_scan_get_ie(new, ie);
391 : 138 : is_multi = 0;
392 : 138 : break;
393 : : default:
394 : 0 : wpa_printf(MSG_DEBUG, "bss: %s: cannot compare IEs", __func__);
395 : 0 : return 0;
396 : : }
397 : :
398 [ + + ]: 230 : if (is_multi) {
399 : : /* in case of multiple IEs stored in buffer */
400 [ + + ]: 46 : old_ie = old_ie_buff ? wpabuf_head_u8(old_ie_buff) : NULL;
401 [ + + ]: 46 : new_ie = new_ie_buff ? wpabuf_head_u8(new_ie_buff) : NULL;
402 [ + + ]: 46 : old_ie_len = old_ie_buff ? wpabuf_len(old_ie_buff) : 0;
403 [ + + ]: 46 : new_ie_len = new_ie_buff ? wpabuf_len(new_ie_buff) : 0;
404 : : } else {
405 : : /* in case of single IE */
406 [ + + ]: 184 : old_ie_len = old_ie ? old_ie[1] + 2 : 0;
407 [ + + ]: 184 : new_ie_len = new_ie ? new_ie[1] + 2 : 0;
408 : : }
409 : :
410 [ + + ][ - + ]: 230 : if (!old_ie || !new_ie)
411 [ + - ][ + + ]: 93 : ret = !old_ie && !new_ie;
412 : : else
413 [ + + ][ + + ]: 137 : ret = (old_ie_len == new_ie_len &&
414 : 112 : os_memcmp(old_ie, new_ie, old_ie_len) == 0);
415 : :
416 : 230 : wpabuf_free(old_ie_buff);
417 : 230 : wpabuf_free(new_ie_buff);
418 : :
419 : 230 : return ret;
420 : : }
421 : :
422 : :
423 : 723 : static u32 wpa_bss_compare_res(const struct wpa_bss *old,
424 : : const struct wpa_scan_res *new)
425 : : {
426 : 723 : u32 changes = 0;
427 : 723 : int caps_diff = old->caps ^ new->caps;
428 : :
429 [ + + ]: 723 : if (old->freq != new->freq)
430 : 3 : changes |= WPA_BSS_FREQ_CHANGED_FLAG;
431 : :
432 [ - + ]: 723 : if (old->level != new->level)
433 : 0 : changes |= WPA_BSS_SIGNAL_CHANGED_FLAG;
434 : :
435 [ + + ]: 723 : if (caps_diff & IEEE80211_CAP_PRIVACY)
436 : 8 : changes |= WPA_BSS_PRIVACY_CHANGED_FLAG;
437 : :
438 [ - + ]: 723 : if (caps_diff & IEEE80211_CAP_IBSS)
439 : 0 : changes |= WPA_BSS_MODE_CHANGED_FLAG;
440 : :
441 [ + + ][ + + ]: 723 : if (old->ie_len == new->ie_len &&
442 : 696 : os_memcmp(old + 1, new + 1, old->ie_len) == 0)
443 : 677 : return changes;
444 : 46 : changes |= WPA_BSS_IES_CHANGED_FLAG;
445 : :
446 [ + + ]: 46 : if (!are_ies_equal(old, new, WPA_IE_VENDOR_TYPE))
447 : 8 : changes |= WPA_BSS_WPAIE_CHANGED_FLAG;
448 : :
449 [ + + ]: 46 : if (!are_ies_equal(old, new, WLAN_EID_RSN))
450 : 9 : changes |= WPA_BSS_RSNIE_CHANGED_FLAG;
451 : :
452 [ + + ]: 46 : if (!are_ies_equal(old, new, WPS_IE_VENDOR_TYPE))
453 : 28 : changes |= WPA_BSS_WPS_CHANGED_FLAG;
454 : :
455 [ + - - + ]: 92 : if (!are_ies_equal(old, new, WLAN_EID_SUPP_RATES) ||
456 : 46 : !are_ies_equal(old, new, WLAN_EID_EXT_SUPP_RATES))
457 : 0 : changes |= WPA_BSS_RATES_CHANGED_FLAG;
458 : :
459 : 723 : return changes;
460 : : }
461 : :
462 : :
463 : 723 : static void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes,
464 : : const struct wpa_bss *bss)
465 : : {
466 [ + + ]: 723 : if (changes & WPA_BSS_FREQ_CHANGED_FLAG)
467 : 3 : wpas_notify_bss_freq_changed(wpa_s, bss->id);
468 : :
469 [ - + ]: 723 : if (changes & WPA_BSS_SIGNAL_CHANGED_FLAG)
470 : 0 : wpas_notify_bss_signal_changed(wpa_s, bss->id);
471 : :
472 [ + + ]: 723 : if (changes & WPA_BSS_PRIVACY_CHANGED_FLAG)
473 : 8 : wpas_notify_bss_privacy_changed(wpa_s, bss->id);
474 : :
475 [ - + ]: 723 : if (changes & WPA_BSS_MODE_CHANGED_FLAG)
476 : 0 : wpas_notify_bss_mode_changed(wpa_s, bss->id);
477 : :
478 [ + + ]: 723 : if (changes & WPA_BSS_WPAIE_CHANGED_FLAG)
479 : 8 : wpas_notify_bss_wpaie_changed(wpa_s, bss->id);
480 : :
481 [ + + ]: 723 : if (changes & WPA_BSS_RSNIE_CHANGED_FLAG)
482 : 9 : wpas_notify_bss_rsnie_changed(wpa_s, bss->id);
483 : :
484 [ + + ]: 723 : if (changes & WPA_BSS_WPS_CHANGED_FLAG)
485 : 28 : wpas_notify_bss_wps_changed(wpa_s, bss->id);
486 : :
487 [ + + ]: 723 : if (changes & WPA_BSS_IES_CHANGED_FLAG)
488 : 46 : wpas_notify_bss_ies_changed(wpa_s, bss->id);
489 : :
490 [ - + ]: 723 : if (changes & WPA_BSS_RATES_CHANGED_FLAG)
491 : 0 : wpas_notify_bss_rates_changed(wpa_s, bss->id);
492 : 723 : }
493 : :
494 : :
495 : : static struct wpa_bss *
496 : 723 : wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
497 : : struct wpa_scan_res *res, struct os_reltime *fetch_time)
498 : : {
499 : : u32 changes;
500 : :
501 : 723 : changes = wpa_bss_compare_res(bss, res);
502 : 723 : bss->scan_miss_count = 0;
503 : 723 : bss->last_update_idx = wpa_s->bss_update_idx;
504 : 723 : wpa_bss_copy_res(bss, res, fetch_time);
505 : : /* Move the entry to the end of the list */
506 : 723 : dl_list_del(&bss->list);
507 : : #ifdef CONFIG_P2P
508 [ + + - + ]: 1140 : if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
509 : 417 : !wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE)) {
510 : : /*
511 : : * This can happen when non-P2P station interface runs a scan
512 : : * without P2P IE in the Probe Request frame. P2P GO would reply
513 : : * to that with a Probe Response that does not include P2P IE.
514 : : * Do not update the IEs in this BSS entry to avoid such loss of
515 : : * information that may be needed for P2P operations to
516 : : * determine group information.
517 : : */
518 : 0 : wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Do not update scan IEs for "
519 : : MACSTR " since that would remove P2P IE information",
520 : : MAC2STR(bss->bssid));
521 : : } else
522 : : #endif /* CONFIG_P2P */
523 [ + + ]: 723 : if (bss->ie_len + bss->beacon_ie_len >=
524 : 723 : res->ie_len + res->beacon_ie_len) {
525 : 679 : os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
526 : 679 : bss->ie_len = res->ie_len;
527 : 679 : bss->beacon_ie_len = res->beacon_ie_len;
528 : : } else {
529 : : struct wpa_bss *nbss;
530 : 44 : struct dl_list *prev = bss->list_id.prev;
531 : 44 : dl_list_del(&bss->list_id);
532 : 44 : nbss = os_realloc(bss, sizeof(*bss) + res->ie_len +
533 : 44 : res->beacon_ie_len);
534 [ + - ]: 44 : if (nbss) {
535 : : unsigned int i;
536 [ + + ]: 53 : for (i = 0; i < wpa_s->last_scan_res_used; i++) {
537 [ - + ]: 9 : if (wpa_s->last_scan_res[i] == bss) {
538 : 0 : wpa_s->last_scan_res[i] = nbss;
539 : 0 : break;
540 : : }
541 : : }
542 [ + + ]: 44 : if (wpa_s->current_bss == bss)
543 : 4 : wpa_s->current_bss = nbss;
544 : 44 : bss = nbss;
545 : 44 : os_memcpy(bss + 1, res + 1,
546 : : res->ie_len + res->beacon_ie_len);
547 : 44 : bss->ie_len = res->ie_len;
548 : 44 : bss->beacon_ie_len = res->beacon_ie_len;
549 : : }
550 : 44 : dl_list_add(prev, &bss->list_id);
551 : : }
552 [ + + ]: 723 : if (changes & WPA_BSS_IES_CHANGED_FLAG)
553 : 46 : wpa_bss_set_hessid(bss);
554 : 723 : dl_list_add_tail(&wpa_s->bss, &bss->list);
555 : :
556 : 723 : notify_bss_changes(wpa_s, changes, bss);
557 : :
558 : 723 : return bss;
559 : : }
560 : :
561 : :
562 : : /**
563 : : * wpa_bss_update_start - Start a BSS table update from scan results
564 : : * @wpa_s: Pointer to wpa_supplicant data
565 : : *
566 : : * This function is called at the start of each BSS table update round for new
567 : : * scan results. The actual scan result entries are indicated with calls to
568 : : * wpa_bss_update_scan_res() and the update round is finished with a call to
569 : : * wpa_bss_update_end().
570 : : */
571 : 1357 : void wpa_bss_update_start(struct wpa_supplicant *wpa_s)
572 : : {
573 : 1357 : wpa_s->bss_update_idx++;
574 : 1357 : wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u",
575 : : wpa_s->bss_update_idx);
576 : 1357 : wpa_s->last_scan_res_used = 0;
577 : 1357 : }
578 : :
579 : :
580 : : /**
581 : : * wpa_bss_update_scan_res - Update a BSS table entry based on a scan result
582 : : * @wpa_s: Pointer to wpa_supplicant data
583 : : * @res: Scan result
584 : : * @fetch_time: Time when the result was fetched from the driver
585 : : *
586 : : * This function updates a BSS table entry (or adds one) based on a scan result.
587 : : * This is called separately for each scan result between the calls to
588 : : * wpa_bss_update_start() and wpa_bss_update_end().
589 : : */
590 : 2293 : void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
591 : : struct wpa_scan_res *res,
592 : : struct os_reltime *fetch_time)
593 : : {
594 : : const u8 *ssid, *p2p;
595 : : struct wpa_bss *bss;
596 : :
597 [ + + ]: 2293 : if (wpa_s->conf->ignore_old_scan_res) {
598 : : struct os_reltime update;
599 : 928 : calculate_update_time(fetch_time, res->age, &update);
600 [ + + ]: 928 : if (os_reltime_before(&update, &wpa_s->scan_trigger_time)) {
601 : : struct os_reltime age;
602 : 314 : os_reltime_sub(&wpa_s->scan_trigger_time, &update,
603 : : &age);
604 : 314 : wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Ignore driver BSS "
605 : : "table entry that is %u.%06u seconds older "
606 : : "than our scan trigger",
607 : : (unsigned int) age.sec,
608 : : (unsigned int) age.usec);
609 : 314 : return;
610 : : }
611 : : }
612 : :
613 : 1979 : ssid = wpa_scan_get_ie(res, WLAN_EID_SSID);
614 [ - + ]: 1979 : if (ssid == NULL) {
615 : 0 : wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for "
616 : : MACSTR, MAC2STR(res->bssid));
617 : 0 : return;
618 : : }
619 [ - + ]: 1979 : if (ssid[1] > 32) {
620 : 0 : wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for "
621 : : MACSTR, MAC2STR(res->bssid));
622 : 0 : return;
623 : : }
624 : :
625 : 1979 : p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE);
626 : : #ifdef CONFIG_P2P
627 [ + + ][ + + ]: 1979 : if (p2p == NULL &&
628 : 881 : wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
629 : : /*
630 : : * If it's a P2P specific interface, then don't update
631 : : * the scan result without a P2P IE.
632 : : */
633 : 5 : wpa_printf(MSG_DEBUG, "BSS: No P2P IE - skipping BSS " MACSTR
634 : 30 : " update for P2P interface", MAC2STR(res->bssid));
635 : 5 : return;
636 : : }
637 : : #endif /* CONFIG_P2P */
638 [ + + ][ + + ]: 1974 : if (p2p && ssid[1] == P2P_WILDCARD_SSID_LEN &&
[ + - ]
639 : 517 : os_memcmp(ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0)
640 : 517 : return; /* Skip P2P listen discovery results here */
641 : :
642 : : /* TODO: add option for ignoring BSSes we are not interested in
643 : : * (to save memory) */
644 : 1457 : bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
645 [ + + ]: 1457 : if (bss == NULL)
646 : 734 : bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res, fetch_time);
647 : : else {
648 : 723 : bss = wpa_bss_update(wpa_s, bss, res, fetch_time);
649 [ + - ]: 723 : if (wpa_s->last_scan_res) {
650 : : unsigned int i;
651 [ + + ]: 970 : for (i = 0; i < wpa_s->last_scan_res_used; i++) {
652 [ - + ]: 247 : if (bss == wpa_s->last_scan_res[i]) {
653 : : /* Already in the list */
654 : 0 : return;
655 : : }
656 : : }
657 : : }
658 : : }
659 : :
660 [ - + ]: 1457 : if (bss == NULL)
661 : 0 : return;
662 [ + + ]: 1457 : if (wpa_s->last_scan_res_used >= wpa_s->last_scan_res_size) {
663 : : struct wpa_bss **n;
664 : : unsigned int siz;
665 [ + - ]: 23 : if (wpa_s->last_scan_res_size == 0)
666 : 23 : siz = 32;
667 : : else
668 : 0 : siz = wpa_s->last_scan_res_size * 2;
669 : 23 : n = os_realloc_array(wpa_s->last_scan_res, siz,
670 : : sizeof(struct wpa_bss *));
671 [ - + ]: 23 : if (n == NULL)
672 : 0 : return;
673 : 23 : wpa_s->last_scan_res = n;
674 : 23 : wpa_s->last_scan_res_size = siz;
675 : : }
676 : :
677 : 2293 : wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss;
678 : : }
679 : :
680 : :
681 : 1500 : static int wpa_bss_included_in_scan(const struct wpa_bss *bss,
682 : : const struct scan_info *info)
683 : : {
684 : : int found;
685 : : size_t i;
686 : :
687 [ - + ]: 1500 : if (info == NULL)
688 : 0 : return 1;
689 : :
690 [ + - ]: 1500 : if (info->num_freqs) {
691 : 1500 : found = 0;
692 [ + + ]: 2199 : for (i = 0; i < info->num_freqs; i++) {
693 [ + + ]: 2139 : if (bss->freq == info->freqs[i]) {
694 : 1440 : found = 1;
695 : 1440 : break;
696 : : }
697 : : }
698 [ + + ]: 1500 : if (!found)
699 : 60 : return 0;
700 : : }
701 : :
702 [ + - ]: 1440 : if (info->num_ssids) {
703 : 1440 : found = 0;
704 [ + + ]: 2089 : for (i = 0; i < info->num_ssids; i++) {
705 : 1444 : const struct wpa_driver_scan_ssid *s = &info->ssids[i];
706 [ + - ][ + + ]: 1444 : if ((s->ssid == NULL || s->ssid_len == 0) ||
[ + + ]
707 [ + + ]: 140 : (s->ssid_len == bss->ssid_len &&
708 : 140 : os_memcmp(s->ssid, bss->ssid, bss->ssid_len) ==
709 : : 0)) {
710 : 795 : found = 1;
711 : 795 : break;
712 : : }
713 : : }
714 [ + + ]: 1440 : if (!found)
715 : 645 : return 0;
716 : : }
717 : :
718 : 1500 : return 1;
719 : : }
720 : :
721 : :
722 : : /**
723 : : * wpa_bss_update_end - End a BSS table update from scan results
724 : : * @wpa_s: Pointer to wpa_supplicant data
725 : : * @info: Information about scan parameters
726 : : * @new_scan: Whether this update round was based on a new scan
727 : : *
728 : : * This function is called at the end of each BSS table update round for new
729 : : * scan results. The start of the update was indicated with a call to
730 : : * wpa_bss_update_start().
731 : : */
732 : 1357 : void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
733 : : int new_scan)
734 : : {
735 : : struct wpa_bss *bss, *n;
736 : :
737 : 1357 : os_get_reltime(&wpa_s->last_scan);
738 [ - + ]: 1357 : if (!new_scan)
739 : 1357 : return; /* do not expire entries without new scan */
740 : :
741 [ + + ]: 2903 : dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
742 [ + + ]: 1546 : if (wpa_bss_in_use(wpa_s, bss))
743 : 46 : continue;
744 [ + + ]: 1500 : if (!wpa_bss_included_in_scan(bss, info))
745 : 705 : continue; /* expire only BSSes that were scanned */
746 [ + + ]: 795 : if (bss->last_update_idx < wpa_s->bss_update_idx)
747 : 22 : bss->scan_miss_count++;
748 [ + + ]: 795 : if (bss->scan_miss_count >=
749 : 795 : wpa_s->conf->bss_expiration_scan_count) {
750 : 2 : wpa_bss_remove(wpa_s, bss, "no match in scan");
751 : : }
752 : : }
753 : :
754 : 1357 : wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%u/%u",
755 : : wpa_s->last_scan_res_used, wpa_s->last_scan_res_size);
756 : : }
757 : :
758 : :
759 : : /**
760 : : * wpa_bss_flush_by_age - Flush old BSS entries
761 : : * @wpa_s: Pointer to wpa_supplicant data
762 : : * @age: Maximum entry age in seconds
763 : : *
764 : : * Remove BSS entries that have not been updated during the last @age seconds.
765 : : */
766 : 439 : void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age)
767 : : {
768 : : struct wpa_bss *bss, *n;
769 : : struct os_reltime t;
770 : :
771 [ + + ]: 439 : if (dl_list_empty(&wpa_s->bss))
772 : 439 : return;
773 : :
774 : 110 : os_get_reltime(&t);
775 : 110 : t.sec -= age;
776 : :
777 [ + + ]: 176 : dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
778 [ + + ]: 118 : if (wpa_bss_in_use(wpa_s, bss))
779 : 66 : continue;
780 : :
781 [ - + ]: 52 : if (os_reltime_before(&bss->last_update, &t)) {
782 : 0 : wpa_bss_remove(wpa_s, bss, __func__);
783 : : } else
784 : 52 : break;
785 : : }
786 : : }
787 : :
788 : :
789 : 439 : static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx)
790 : : {
791 : 439 : struct wpa_supplicant *wpa_s = eloop_ctx;
792 : :
793 : 439 : wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age);
794 : 439 : eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0,
795 : : wpa_bss_timeout, wpa_s, NULL);
796 : 439 : }
797 : :
798 : :
799 : : /**
800 : : * wpa_bss_init - Initialize BSS table
801 : : * @wpa_s: Pointer to wpa_supplicant data
802 : : * Returns: 0 on success, -1 on failure
803 : : *
804 : : * This prepares BSS table lists and timer for periodic updates. The BSS table
805 : : * is deinitialized with wpa_bss_deinit() once not needed anymore.
806 : : */
807 : 39 : int wpa_bss_init(struct wpa_supplicant *wpa_s)
808 : : {
809 : 39 : dl_list_init(&wpa_s->bss);
810 : 39 : dl_list_init(&wpa_s->bss_id);
811 : 39 : eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0,
812 : : wpa_bss_timeout, wpa_s, NULL);
813 : 39 : return 0;
814 : : }
815 : :
816 : :
817 : : /**
818 : : * wpa_bss_flush - Flush all unused BSS entries
819 : : * @wpa_s: Pointer to wpa_supplicant data
820 : : */
821 : 1304 : void wpa_bss_flush(struct wpa_supplicant *wpa_s)
822 : : {
823 : : struct wpa_bss *bss, *n;
824 : :
825 : 1304 : wpa_s->clear_driver_scan_cache = 1;
826 : :
827 [ + + ]: 1304 : if (wpa_s->bss.next == NULL)
828 : 1304 : return; /* BSS table not yet initialized */
829 : :
830 [ + + ]: 2037 : dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
831 [ + + ]: 734 : if (wpa_bss_in_use(wpa_s, bss))
832 : 2 : continue;
833 : 732 : wpa_bss_remove(wpa_s, bss, __func__);
834 : : }
835 : : }
836 : :
837 : :
838 : : /**
839 : : * wpa_bss_deinit - Deinitialize BSS table
840 : : * @wpa_s: Pointer to wpa_supplicant data
841 : : */
842 : 40 : void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
843 : : {
844 : 40 : eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL);
845 : 40 : wpa_bss_flush(wpa_s);
846 : 40 : }
847 : :
848 : :
849 : : /**
850 : : * wpa_bss_get_bssid - Fetch a BSS table entry based on BSSID
851 : : * @wpa_s: Pointer to wpa_supplicant data
852 : : * @bssid: BSSID
853 : : * Returns: Pointer to the BSS entry or %NULL if not found
854 : : */
855 : 330 : struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
856 : : const u8 *bssid)
857 : : {
858 : : struct wpa_bss *bss;
859 [ - + ]: 330 : if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
860 : 0 : return NULL;
861 [ + - ]: 406 : dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
862 [ + + ]: 406 : if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
863 : 330 : return bss;
864 : : }
865 : 330 : return NULL;
866 : : }
867 : :
868 : :
869 : : /**
870 : : * wpa_bss_get_bssid_latest - Fetch the latest BSS table entry based on BSSID
871 : : * @wpa_s: Pointer to wpa_supplicant data
872 : : * @bssid: BSSID
873 : : * Returns: Pointer to the BSS entry or %NULL if not found
874 : : *
875 : : * This function is like wpa_bss_get_bssid(), but full BSS table is iterated to
876 : : * find the entry that has the most recent update. This can help in finding the
877 : : * correct entry in cases where the SSID of the AP may have changed recently
878 : : * (e.g., in WPS reconfiguration cases).
879 : : */
880 : 157 : struct wpa_bss * wpa_bss_get_bssid_latest(struct wpa_supplicant *wpa_s,
881 : : const u8 *bssid)
882 : : {
883 : 157 : struct wpa_bss *bss, *found = NULL;
884 [ - + ]: 157 : if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid))
885 : 0 : return NULL;
886 [ + + ]: 303 : dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
887 [ + + ]: 146 : if (os_memcmp(bss->bssid, bssid, ETH_ALEN) != 0)
888 : 32 : continue;
889 [ + + - + ]: 122 : if (found == NULL ||
890 : 8 : os_reltime_before(&found->last_update, &bss->last_update))
891 : 106 : found = bss;
892 : : }
893 : 157 : return found;
894 : : }
895 : :
896 : :
897 : : #ifdef CONFIG_P2P
898 : : /**
899 : : * wpa_bss_get_p2p_dev_addr - Fetch a BSS table entry based on P2P Device Addr
900 : : * @wpa_s: Pointer to wpa_supplicant data
901 : : * @dev_addr: P2P Device Address of the GO
902 : : * Returns: Pointer to the BSS entry or %NULL if not found
903 : : */
904 : 15 : struct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s,
905 : : const u8 *dev_addr)
906 : : {
907 : : struct wpa_bss *bss;
908 [ + - ]: 17 : dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) {
909 : : u8 addr[ETH_ALEN];
910 [ + + ]: 17 : if (p2p_parse_dev_addr((const u8 *) (bss + 1), bss->ie_len,
911 [ + - ]: 15 : addr) == 0 &&
912 : 15 : os_memcmp(addr, dev_addr, ETH_ALEN) == 0)
913 : 15 : return bss;
914 : : }
915 : 15 : return NULL;
916 : : }
917 : : #endif /* CONFIG_P2P */
918 : :
919 : :
920 : : /**
921 : : * wpa_bss_get_id - Fetch a BSS table entry based on identifier
922 : : * @wpa_s: Pointer to wpa_supplicant data
923 : : * @id: Unique identifier (struct wpa_bss::id) assigned for the entry
924 : : * Returns: Pointer to the BSS entry or %NULL if not found
925 : : */
926 : 1 : struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
927 : : {
928 : : struct wpa_bss *bss;
929 [ + - ]: 1 : dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
930 [ + - ]: 1 : if (bss->id == id)
931 : 1 : return bss;
932 : : }
933 : 1 : return NULL;
934 : : }
935 : :
936 : :
937 : : /**
938 : : * wpa_bss_get_id_range - Fetch a BSS table entry based on identifier range
939 : : * @wpa_s: Pointer to wpa_supplicant data
940 : : * @idf: Smallest allowed identifier assigned for the entry
941 : : * @idf: Largest allowed identifier assigned for the entry
942 : : * Returns: Pointer to the BSS entry or %NULL if not found
943 : : *
944 : : * This function is similar to wpa_bss_get_id() but allows a BSS entry with the
945 : : * smallest id value to be fetched within the specified range without the
946 : : * caller having to know the exact id.
947 : : */
948 : 1 : struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s,
949 : : unsigned int idf, unsigned int idl)
950 : : {
951 : : struct wpa_bss *bss;
952 [ + - ]: 1 : dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
953 [ + - ][ + - ]: 1 : if (bss->id >= idf && bss->id <= idl)
954 : 1 : return bss;
955 : : }
956 : 1 : return NULL;
957 : : }
958 : :
959 : :
960 : : /**
961 : : * wpa_bss_get_ie - Fetch a specified information element from a BSS entry
962 : : * @bss: BSS table entry
963 : : * @ie: Information element identitifier (WLAN_EID_*)
964 : : * Returns: Pointer to the information element (id field) or %NULL if not found
965 : : *
966 : : * This function returns the first matching information element in the BSS
967 : : * entry.
968 : : */
969 : 7841 : const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie)
970 : : {
971 : : const u8 *end, *pos;
972 : :
973 : 7841 : pos = (const u8 *) (bss + 1);
974 : 7841 : end = pos + bss->ie_len;
975 : :
976 [ + + ]: 58013 : while (pos + 1 < end) {
977 [ - + ]: 55755 : if (pos + 2 + pos[1] > end)
978 : 0 : break;
979 [ + + ]: 55755 : if (pos[0] == ie)
980 : 5583 : return pos;
981 : 50172 : pos += 2 + pos[1];
982 : : }
983 : :
984 : 7841 : return NULL;
985 : : }
986 : :
987 : :
988 : : /**
989 : : * wpa_bss_get_vendor_ie - Fetch a vendor information element from a BSS entry
990 : : * @bss: BSS table entry
991 : : * @vendor_type: Vendor type (four octets starting the IE payload)
992 : : * Returns: Pointer to the information element (id field) or %NULL if not found
993 : : *
994 : : * This function returns the first matching information element in the BSS
995 : : * entry.
996 : : */
997 : 8617 : const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type)
998 : : {
999 : : const u8 *end, *pos;
1000 : :
1001 : 8617 : pos = (const u8 *) (bss + 1);
1002 : 8617 : end = pos + bss->ie_len;
1003 : :
1004 [ + + ]: 100820 : while (pos + 1 < end) {
1005 [ - + ]: 94177 : if (pos + 2 + pos[1] > end)
1006 : 0 : break;
1007 [ + + ]: 112090 : if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
[ + - + + ]
1008 : 17913 : vendor_type == WPA_GET_BE32(&pos[2]))
1009 : 1974 : return pos;
1010 : 92203 : pos += 2 + pos[1];
1011 : : }
1012 : :
1013 : 8617 : return NULL;
1014 : : }
1015 : :
1016 : :
1017 : : /**
1018 : : * wpa_bss_get_vendor_ie_beacon - Fetch a vendor information from a BSS entry
1019 : : * @bss: BSS table entry
1020 : : * @vendor_type: Vendor type (four octets starting the IE payload)
1021 : : * Returns: Pointer to the information element (id field) or %NULL if not found
1022 : : *
1023 : : * This function returns the first matching information element in the BSS
1024 : : * entry.
1025 : : *
1026 : : * This function is like wpa_bss_get_vendor_ie(), but uses IE buffer only
1027 : : * from Beacon frames instead of either Beacon or Probe Response frames.
1028 : : */
1029 : 942 : const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss,
1030 : : u32 vendor_type)
1031 : : {
1032 : : const u8 *end, *pos;
1033 : :
1034 [ + + ]: 942 : if (bss->beacon_ie_len == 0)
1035 : 268 : return NULL;
1036 : :
1037 : 674 : pos = (const u8 *) (bss + 1);
1038 : 674 : pos += bss->ie_len;
1039 : 674 : end = pos + bss->beacon_ie_len;
1040 : :
1041 [ + + ]: 8620 : while (pos + 1 < end) {
1042 [ - + ]: 7952 : if (pos + 2 + pos[1] > end)
1043 : 0 : break;
1044 [ + + ]: 8969 : if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
[ + - + + ]
1045 : 1017 : vendor_type == WPA_GET_BE32(&pos[2]))
1046 : 6 : return pos;
1047 : 7946 : pos += 2 + pos[1];
1048 : : }
1049 : :
1050 : 942 : return NULL;
1051 : : }
1052 : :
1053 : :
1054 : : /**
1055 : : * wpa_bss_get_vendor_ie_multi - Fetch vendor IE data from a BSS entry
1056 : : * @bss: BSS table entry
1057 : : * @vendor_type: Vendor type (four octets starting the IE payload)
1058 : : * Returns: Pointer to the information element payload or %NULL if not found
1059 : : *
1060 : : * This function returns concatenated payload of possibly fragmented vendor
1061 : : * specific information elements in the BSS entry. The caller is responsible for
1062 : : * freeing the returned buffer.
1063 : : */
1064 : 2251 : struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
1065 : : u32 vendor_type)
1066 : : {
1067 : : struct wpabuf *buf;
1068 : : const u8 *end, *pos;
1069 : :
1070 : 2251 : buf = wpabuf_alloc(bss->ie_len);
1071 [ - + ]: 2251 : if (buf == NULL)
1072 : 0 : return NULL;
1073 : :
1074 : 2251 : pos = (const u8 *) (bss + 1);
1075 : 2251 : end = pos + bss->ie_len;
1076 : :
1077 [ + + ]: 27109 : while (pos + 1 < end) {
1078 [ - + ]: 24858 : if (pos + 2 + pos[1] > end)
1079 : 0 : break;
1080 [ + + ]: 30476 : if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
[ + - + + ]
1081 : 5618 : vendor_type == WPA_GET_BE32(&pos[2]))
1082 : 1268 : wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
1083 : 24858 : pos += 2 + pos[1];
1084 : : }
1085 : :
1086 [ + + ]: 2251 : if (wpabuf_len(buf) == 0) {
1087 : 989 : wpabuf_free(buf);
1088 : 989 : buf = NULL;
1089 : : }
1090 : :
1091 : 2251 : return buf;
1092 : : }
1093 : :
1094 : :
1095 : : /**
1096 : : * wpa_bss_get_vendor_ie_multi_beacon - Fetch vendor IE data from a BSS entry
1097 : : * @bss: BSS table entry
1098 : : * @vendor_type: Vendor type (four octets starting the IE payload)
1099 : : * Returns: Pointer to the information element payload or %NULL if not found
1100 : : *
1101 : : * This function returns concatenated payload of possibly fragmented vendor
1102 : : * specific information elements in the BSS entry. The caller is responsible for
1103 : : * freeing the returned buffer.
1104 : : *
1105 : : * This function is like wpa_bss_get_vendor_ie_multi(), but uses IE buffer only
1106 : : * from Beacon frames instead of either Beacon or Probe Response frames.
1107 : : */
1108 : 1 : struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss,
1109 : : u32 vendor_type)
1110 : : {
1111 : : struct wpabuf *buf;
1112 : : const u8 *end, *pos;
1113 : :
1114 : 1 : buf = wpabuf_alloc(bss->beacon_ie_len);
1115 [ - + ]: 1 : if (buf == NULL)
1116 : 0 : return NULL;
1117 : :
1118 : 1 : pos = (const u8 *) (bss + 1);
1119 : 1 : pos += bss->ie_len;
1120 : 1 : end = pos + bss->beacon_ie_len;
1121 : :
1122 [ + + ]: 11 : while (pos + 1 < end) {
1123 [ - + ]: 10 : if (pos + 2 + pos[1] > end)
1124 : 0 : break;
1125 [ + + ]: 11 : if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
[ + - - + ]
1126 : 1 : vendor_type == WPA_GET_BE32(&pos[2]))
1127 : 0 : wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
1128 : 10 : pos += 2 + pos[1];
1129 : : }
1130 : :
1131 [ + - ]: 1 : if (wpabuf_len(buf) == 0) {
1132 : 1 : wpabuf_free(buf);
1133 : 1 : buf = NULL;
1134 : : }
1135 : :
1136 : 1 : return buf;
1137 : : }
1138 : :
1139 : :
1140 : : /**
1141 : : * wpa_bss_get_max_rate - Get maximum legacy TX rate supported in a BSS
1142 : : * @bss: BSS table entry
1143 : : * Returns: Maximum legacy rate in units of 500 kbps
1144 : : */
1145 : 0 : int wpa_bss_get_max_rate(const struct wpa_bss *bss)
1146 : : {
1147 : 0 : int rate = 0;
1148 : : const u8 *ie;
1149 : : int i;
1150 : :
1151 : 0 : ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
1152 [ # # ][ # # ]: 0 : for (i = 0; ie && i < ie[1]; i++) {
1153 [ # # ]: 0 : if ((ie[i + 2] & 0x7f) > rate)
1154 : 0 : rate = ie[i + 2] & 0x7f;
1155 : : }
1156 : :
1157 : 0 : ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
1158 [ # # ][ # # ]: 0 : for (i = 0; ie && i < ie[1]; i++) {
1159 [ # # ]: 0 : if ((ie[i + 2] & 0x7f) > rate)
1160 : 0 : rate = ie[i + 2] & 0x7f;
1161 : : }
1162 : :
1163 : 0 : return rate;
1164 : : }
1165 : :
1166 : :
1167 : : /**
1168 : : * wpa_bss_get_bit_rates - Get legacy TX rates supported in a BSS
1169 : : * @bss: BSS table entry
1170 : : * @rates: Buffer for returning a pointer to the rates list (units of 500 kbps)
1171 : : * Returns: number of legacy TX rates or -1 on failure
1172 : : *
1173 : : * The caller is responsible for freeing the returned buffer with os_free() in
1174 : : * case of success.
1175 : : */
1176 : 0 : int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates)
1177 : : {
1178 : : const u8 *ie, *ie2;
1179 : : int i, j;
1180 : : unsigned int len;
1181 : : u8 *r;
1182 : :
1183 : 0 : ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
1184 : 0 : ie2 = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
1185 : :
1186 [ # # ][ # # ]: 0 : len = (ie ? ie[1] : 0) + (ie2 ? ie2[1] : 0);
1187 : :
1188 : 0 : r = os_malloc(len);
1189 [ # # ]: 0 : if (!r)
1190 : 0 : return -1;
1191 : :
1192 [ # # ][ # # ]: 0 : for (i = 0; ie && i < ie[1]; i++)
1193 : 0 : r[i] = ie[i + 2] & 0x7f;
1194 : :
1195 [ # # ][ # # ]: 0 : for (j = 0; ie2 && j < ie2[1]; j++)
1196 : 0 : r[i + j] = ie2[j + 2] & 0x7f;
1197 : :
1198 : 0 : *rates = r;
1199 : 0 : return len;
1200 : : }
|