1214501Srpaulo/* 2214501Srpaulo * BSS table 3252726Srpaulo * Copyright (c) 2009-2012, Jouni Malinen <j@w1.fi> 4214501Srpaulo * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7214501Srpaulo */ 8214501Srpaulo 9214501Srpaulo#include "utils/includes.h" 10214501Srpaulo 11214501Srpaulo#include "utils/common.h" 12214501Srpaulo#include "utils/eloop.h" 13214501Srpaulo#include "common/ieee802_11_defs.h" 14214501Srpaulo#include "drivers/driver.h" 15214501Srpaulo#include "wpa_supplicant_i.h" 16214501Srpaulo#include "config.h" 17214501Srpaulo#include "notify.h" 18214501Srpaulo#include "scan.h" 19214501Srpaulo#include "bss.h" 20214501Srpaulo 21214501Srpaulo 22214501Srpaulo/** 23214501Srpaulo * WPA_BSS_EXPIRATION_PERIOD - Period of expiration run in seconds 24214501Srpaulo */ 25214501Srpaulo#define WPA_BSS_EXPIRATION_PERIOD 10 26214501Srpaulo 27214501Srpaulo#define WPA_BSS_FREQ_CHANGED_FLAG BIT(0) 28214501Srpaulo#define WPA_BSS_SIGNAL_CHANGED_FLAG BIT(1) 29214501Srpaulo#define WPA_BSS_PRIVACY_CHANGED_FLAG BIT(2) 30214501Srpaulo#define WPA_BSS_MODE_CHANGED_FLAG BIT(3) 31214501Srpaulo#define WPA_BSS_WPAIE_CHANGED_FLAG BIT(4) 32214501Srpaulo#define WPA_BSS_RSNIE_CHANGED_FLAG BIT(5) 33214501Srpaulo#define WPA_BSS_WPS_CHANGED_FLAG BIT(6) 34214501Srpaulo#define WPA_BSS_RATES_CHANGED_FLAG BIT(7) 35214501Srpaulo#define WPA_BSS_IES_CHANGED_FLAG BIT(8) 36214501Srpaulo 37214501Srpaulo 38252726Srpaulostatic void wpa_bss_set_hessid(struct wpa_bss *bss) 39214501Srpaulo{ 40252726Srpaulo#ifdef CONFIG_INTERWORKING 41252726Srpaulo const u8 *ie = wpa_bss_get_ie(bss, WLAN_EID_INTERWORKING); 42252726Srpaulo if (ie == NULL || (ie[1] != 7 && ie[1] != 9)) { 43252726Srpaulo os_memset(bss->hessid, 0, ETH_ALEN); 44252726Srpaulo return; 45252726Srpaulo } 46252726Srpaulo if (ie[1] == 7) 47252726Srpaulo os_memcpy(bss->hessid, ie + 3, ETH_ALEN); 48252726Srpaulo else 49252726Srpaulo os_memcpy(bss->hessid, ie + 5, ETH_ALEN); 50252726Srpaulo#endif /* CONFIG_INTERWORKING */ 51252726Srpaulo} 52252726Srpaulo 53252726Srpaulo 54252726Srpaulo/** 55252726Srpaulo * wpa_bss_anqp_alloc - Allocate ANQP data structure for a BSS entry 56252726Srpaulo * Returns: Allocated ANQP data structure or %NULL on failure 57252726Srpaulo * 58252726Srpaulo * The allocated ANQP data structure has its users count set to 1. It may be 59252726Srpaulo * shared by multiple BSS entries and each shared entry is freed with 60252726Srpaulo * wpa_bss_anqp_free(). 61252726Srpaulo */ 62252726Srpaulostruct wpa_bss_anqp * wpa_bss_anqp_alloc(void) 63252726Srpaulo{ 64252726Srpaulo struct wpa_bss_anqp *anqp; 65252726Srpaulo anqp = os_zalloc(sizeof(*anqp)); 66252726Srpaulo if (anqp == NULL) 67252726Srpaulo return NULL; 68252726Srpaulo anqp->users = 1; 69252726Srpaulo return anqp; 70252726Srpaulo} 71252726Srpaulo 72252726Srpaulo 73252726Srpaulo/** 74252726Srpaulo * wpa_bss_anqp_clone - Clone an ANQP data structure 75252726Srpaulo * @anqp: ANQP data structure from wpa_bss_anqp_alloc() 76252726Srpaulo * Returns: Cloned ANQP data structure or %NULL on failure 77252726Srpaulo */ 78252726Srpaulostatic struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp) 79252726Srpaulo{ 80252726Srpaulo struct wpa_bss_anqp *n; 81252726Srpaulo 82252726Srpaulo n = os_zalloc(sizeof(*n)); 83252726Srpaulo if (n == NULL) 84252726Srpaulo return NULL; 85252726Srpaulo 86252726Srpaulo#define ANQP_DUP(f) if (anqp->f) n->f = wpabuf_dup(anqp->f) 87252726Srpaulo#ifdef CONFIG_INTERWORKING 88252726Srpaulo ANQP_DUP(venue_name); 89252726Srpaulo ANQP_DUP(network_auth_type); 90252726Srpaulo ANQP_DUP(roaming_consortium); 91252726Srpaulo ANQP_DUP(ip_addr_type_availability); 92252726Srpaulo ANQP_DUP(nai_realm); 93252726Srpaulo ANQP_DUP(anqp_3gpp); 94252726Srpaulo ANQP_DUP(domain_name); 95252726Srpaulo#endif /* CONFIG_INTERWORKING */ 96252726Srpaulo#ifdef CONFIG_HS20 97252726Srpaulo ANQP_DUP(hs20_operator_friendly_name); 98252726Srpaulo ANQP_DUP(hs20_wan_metrics); 99252726Srpaulo ANQP_DUP(hs20_connection_capability); 100252726Srpaulo ANQP_DUP(hs20_operating_class); 101252726Srpaulo#endif /* CONFIG_HS20 */ 102252726Srpaulo#undef ANQP_DUP 103252726Srpaulo 104252726Srpaulo return n; 105252726Srpaulo} 106252726Srpaulo 107252726Srpaulo 108252726Srpaulo/** 109252726Srpaulo * wpa_bss_anqp_unshare_alloc - Unshare ANQP data (if shared) in a BSS entry 110252726Srpaulo * @bss: BSS entry 111252726Srpaulo * Returns: 0 on success, -1 on failure 112252726Srpaulo * 113252726Srpaulo * This function ensures the specific BSS entry has an ANQP data structure that 114252726Srpaulo * is not shared with any other BSS entry. 115252726Srpaulo */ 116252726Srpauloint wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss) 117252726Srpaulo{ 118252726Srpaulo struct wpa_bss_anqp *anqp; 119252726Srpaulo 120252726Srpaulo if (bss->anqp && bss->anqp->users > 1) { 121252726Srpaulo /* allocated, but shared - clone an unshared copy */ 122252726Srpaulo anqp = wpa_bss_anqp_clone(bss->anqp); 123252726Srpaulo if (anqp == NULL) 124252726Srpaulo return -1; 125252726Srpaulo anqp->users = 1; 126252726Srpaulo bss->anqp->users--; 127252726Srpaulo bss->anqp = anqp; 128252726Srpaulo return 0; 129252726Srpaulo } 130252726Srpaulo 131252726Srpaulo if (bss->anqp) 132252726Srpaulo return 0; /* already allocated and not shared */ 133252726Srpaulo 134252726Srpaulo /* not allocated - allocate a new storage area */ 135252726Srpaulo bss->anqp = wpa_bss_anqp_alloc(); 136252726Srpaulo return bss->anqp ? 0 : -1; 137252726Srpaulo} 138252726Srpaulo 139252726Srpaulo 140252726Srpaulo/** 141252726Srpaulo * wpa_bss_anqp_free - Free an ANQP data structure 142252726Srpaulo * @anqp: ANQP data structure from wpa_bss_anqp_alloc() or wpa_bss_anqp_clone() 143252726Srpaulo */ 144252726Srpaulostatic void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp) 145252726Srpaulo{ 146252726Srpaulo if (anqp == NULL) 147252726Srpaulo return; 148252726Srpaulo 149252726Srpaulo anqp->users--; 150252726Srpaulo if (anqp->users > 0) { 151252726Srpaulo /* Another BSS entry holds a pointer to this ANQP info */ 152252726Srpaulo return; 153252726Srpaulo } 154252726Srpaulo 155252726Srpaulo#ifdef CONFIG_INTERWORKING 156252726Srpaulo wpabuf_free(anqp->venue_name); 157252726Srpaulo wpabuf_free(anqp->network_auth_type); 158252726Srpaulo wpabuf_free(anqp->roaming_consortium); 159252726Srpaulo wpabuf_free(anqp->ip_addr_type_availability); 160252726Srpaulo wpabuf_free(anqp->nai_realm); 161252726Srpaulo wpabuf_free(anqp->anqp_3gpp); 162252726Srpaulo wpabuf_free(anqp->domain_name); 163252726Srpaulo#endif /* CONFIG_INTERWORKING */ 164252726Srpaulo#ifdef CONFIG_HS20 165252726Srpaulo wpabuf_free(anqp->hs20_operator_friendly_name); 166252726Srpaulo wpabuf_free(anqp->hs20_wan_metrics); 167252726Srpaulo wpabuf_free(anqp->hs20_connection_capability); 168252726Srpaulo wpabuf_free(anqp->hs20_operating_class); 169252726Srpaulo#endif /* CONFIG_HS20 */ 170252726Srpaulo 171252726Srpaulo os_free(anqp); 172252726Srpaulo} 173252726Srpaulo 174252726Srpaulo 175252726Srpaulostatic void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, 176252726Srpaulo const char *reason) 177252726Srpaulo{ 178252726Srpaulo if (wpa_s->last_scan_res) { 179252726Srpaulo unsigned int i; 180252726Srpaulo for (i = 0; i < wpa_s->last_scan_res_used; i++) { 181252726Srpaulo if (wpa_s->last_scan_res[i] == bss) { 182252726Srpaulo os_memmove(&wpa_s->last_scan_res[i], 183252726Srpaulo &wpa_s->last_scan_res[i + 1], 184252726Srpaulo (wpa_s->last_scan_res_used - i - 1) 185252726Srpaulo * sizeof(struct wpa_bss *)); 186252726Srpaulo wpa_s->last_scan_res_used--; 187252726Srpaulo break; 188252726Srpaulo } 189252726Srpaulo } 190252726Srpaulo } 191214501Srpaulo dl_list_del(&bss->list); 192214501Srpaulo dl_list_del(&bss->list_id); 193214501Srpaulo wpa_s->num_bss--; 194252726Srpaulo wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR 195252726Srpaulo " SSID '%s' due to %s", bss->id, MAC2STR(bss->bssid), 196252726Srpaulo wpa_ssid_txt(bss->ssid, bss->ssid_len), reason); 197214501Srpaulo wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id); 198252726Srpaulo wpa_bss_anqp_free(bss->anqp); 199214501Srpaulo os_free(bss); 200214501Srpaulo} 201214501Srpaulo 202214501Srpaulo 203252726Srpaulo/** 204252726Srpaulo * wpa_bss_get - Fetch a BSS table entry based on BSSID and SSID 205252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data 206252726Srpaulo * @bssid: BSSID 207252726Srpaulo * @ssid: SSID 208252726Srpaulo * @ssid_len: Length of @ssid 209252726Srpaulo * Returns: Pointer to the BSS entry or %NULL if not found 210252726Srpaulo */ 211214501Srpaulostruct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid, 212214501Srpaulo const u8 *ssid, size_t ssid_len) 213214501Srpaulo{ 214214501Srpaulo struct wpa_bss *bss; 215252726Srpaulo if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid)) 216252726Srpaulo return NULL; 217214501Srpaulo dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { 218214501Srpaulo if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 && 219214501Srpaulo bss->ssid_len == ssid_len && 220214501Srpaulo os_memcmp(bss->ssid, ssid, ssid_len) == 0) 221214501Srpaulo return bss; 222214501Srpaulo } 223214501Srpaulo return NULL; 224214501Srpaulo} 225214501Srpaulo 226214501Srpaulo 227214501Srpaulostatic void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src) 228214501Srpaulo{ 229214501Srpaulo os_time_t usec; 230214501Srpaulo 231214501Srpaulo dst->flags = src->flags; 232214501Srpaulo os_memcpy(dst->bssid, src->bssid, ETH_ALEN); 233214501Srpaulo dst->freq = src->freq; 234214501Srpaulo dst->beacon_int = src->beacon_int; 235214501Srpaulo dst->caps = src->caps; 236214501Srpaulo dst->qual = src->qual; 237214501Srpaulo dst->noise = src->noise; 238214501Srpaulo dst->level = src->level; 239214501Srpaulo dst->tsf = src->tsf; 240214501Srpaulo 241214501Srpaulo os_get_time(&dst->last_update); 242214501Srpaulo dst->last_update.sec -= src->age / 1000; 243214501Srpaulo usec = (src->age % 1000) * 1000; 244214501Srpaulo if (dst->last_update.usec < usec) { 245214501Srpaulo dst->last_update.sec--; 246214501Srpaulo dst->last_update.usec += 1000000; 247214501Srpaulo } 248214501Srpaulo dst->last_update.usec -= usec; 249214501Srpaulo} 250214501Srpaulo 251214501Srpaulo 252252726Srpaulostatic int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) 253214501Srpaulo{ 254252726Srpaulo struct wpa_ssid *ssid; 255252726Srpaulo 256252726Srpaulo for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { 257252726Srpaulo if (ssid->ssid == NULL || ssid->ssid_len == 0) 258252726Srpaulo continue; 259252726Srpaulo if (ssid->ssid_len == bss->ssid_len && 260252726Srpaulo os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) == 0) 261252726Srpaulo return 1; 262252726Srpaulo } 263252726Srpaulo 264252726Srpaulo return 0; 265252726Srpaulo} 266252726Srpaulo 267252726Srpaulo 268252726Srpaulostatic int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss) 269252726Srpaulo{ 270252726Srpaulo return bss == wpa_s->current_bss || 271252726Srpaulo os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 || 272252726Srpaulo os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0; 273252726Srpaulo} 274252726Srpaulo 275252726Srpaulo 276252726Srpaulostatic int wpa_bss_remove_oldest_unknown(struct wpa_supplicant *wpa_s) 277252726Srpaulo{ 278214501Srpaulo struct wpa_bss *bss; 279214501Srpaulo 280252726Srpaulo dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { 281252726Srpaulo if (!wpa_bss_known(wpa_s, bss)) { 282252726Srpaulo wpa_bss_remove(wpa_s, bss, __func__); 283252726Srpaulo return 0; 284252726Srpaulo } 285252726Srpaulo } 286252726Srpaulo 287252726Srpaulo return -1; 288252726Srpaulo} 289252726Srpaulo 290252726Srpaulo 291252726Srpaulostatic int wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s) 292252726Srpaulo{ 293252726Srpaulo struct wpa_bss *bss; 294252726Srpaulo 295252726Srpaulo /* 296252726Srpaulo * Remove the oldest entry that does not match with any configured 297252726Srpaulo * network. 298252726Srpaulo */ 299252726Srpaulo if (wpa_bss_remove_oldest_unknown(wpa_s) == 0) 300252726Srpaulo return 0; 301252726Srpaulo 302252726Srpaulo /* 303252726Srpaulo * Remove the oldest entry that isn't currently in use. 304252726Srpaulo */ 305252726Srpaulo dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { 306252726Srpaulo if (!wpa_bss_in_use(wpa_s, bss)) { 307252726Srpaulo wpa_bss_remove(wpa_s, bss, __func__); 308252726Srpaulo return 0; 309252726Srpaulo } 310252726Srpaulo } 311252726Srpaulo 312252726Srpaulo return -1; 313252726Srpaulo} 314252726Srpaulo 315252726Srpaulo 316252726Srpaulostatic struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s, 317252726Srpaulo const u8 *ssid, size_t ssid_len, 318252726Srpaulo struct wpa_scan_res *res) 319252726Srpaulo{ 320252726Srpaulo struct wpa_bss *bss; 321252726Srpaulo 322214501Srpaulo bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len); 323214501Srpaulo if (bss == NULL) 324252726Srpaulo return NULL; 325214501Srpaulo bss->id = wpa_s->bss_next_id++; 326214501Srpaulo bss->last_update_idx = wpa_s->bss_update_idx; 327214501Srpaulo wpa_bss_copy_res(bss, res); 328214501Srpaulo os_memcpy(bss->ssid, ssid, ssid_len); 329214501Srpaulo bss->ssid_len = ssid_len; 330214501Srpaulo bss->ie_len = res->ie_len; 331214501Srpaulo bss->beacon_ie_len = res->beacon_ie_len; 332214501Srpaulo os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len); 333252726Srpaulo wpa_bss_set_hessid(bss); 334214501Srpaulo 335214501Srpaulo dl_list_add_tail(&wpa_s->bss, &bss->list); 336214501Srpaulo dl_list_add_tail(&wpa_s->bss_id, &bss->list_id); 337214501Srpaulo wpa_s->num_bss++; 338252726Srpaulo wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR 339252726Srpaulo " SSID '%s'", 340252726Srpaulo bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len)); 341214501Srpaulo wpas_notify_bss_added(wpa_s, bss->bssid, bss->id); 342252726Srpaulo if (wpa_s->num_bss > wpa_s->conf->bss_max_count && 343252726Srpaulo wpa_bss_remove_oldest(wpa_s) != 0) { 344252726Srpaulo wpa_printf(MSG_ERROR, "Increasing the MAX BSS count to %d " 345252726Srpaulo "because all BSSes are in use. We should normally " 346252726Srpaulo "not get here!", (int) wpa_s->num_bss); 347252726Srpaulo wpa_s->conf->bss_max_count = wpa_s->num_bss; 348214501Srpaulo } 349252726Srpaulo return bss; 350214501Srpaulo} 351214501Srpaulo 352214501Srpaulo 353214501Srpaulostatic int are_ies_equal(const struct wpa_bss *old, 354214501Srpaulo const struct wpa_scan_res *new, u32 ie) 355214501Srpaulo{ 356214501Srpaulo const u8 *old_ie, *new_ie; 357214501Srpaulo struct wpabuf *old_ie_buff = NULL; 358214501Srpaulo struct wpabuf *new_ie_buff = NULL; 359214501Srpaulo int new_ie_len, old_ie_len, ret, is_multi; 360214501Srpaulo 361214501Srpaulo switch (ie) { 362214501Srpaulo case WPA_IE_VENDOR_TYPE: 363214501Srpaulo old_ie = wpa_bss_get_vendor_ie(old, ie); 364214501Srpaulo new_ie = wpa_scan_get_vendor_ie(new, ie); 365214501Srpaulo is_multi = 0; 366214501Srpaulo break; 367214501Srpaulo case WPS_IE_VENDOR_TYPE: 368214501Srpaulo old_ie_buff = wpa_bss_get_vendor_ie_multi(old, ie); 369214501Srpaulo new_ie_buff = wpa_scan_get_vendor_ie_multi(new, ie); 370214501Srpaulo is_multi = 1; 371214501Srpaulo break; 372214501Srpaulo case WLAN_EID_RSN: 373214501Srpaulo case WLAN_EID_SUPP_RATES: 374214501Srpaulo case WLAN_EID_EXT_SUPP_RATES: 375214501Srpaulo old_ie = wpa_bss_get_ie(old, ie); 376214501Srpaulo new_ie = wpa_scan_get_ie(new, ie); 377214501Srpaulo is_multi = 0; 378214501Srpaulo break; 379214501Srpaulo default: 380214501Srpaulo wpa_printf(MSG_DEBUG, "bss: %s: cannot compare IEs", __func__); 381214501Srpaulo return 0; 382214501Srpaulo } 383214501Srpaulo 384214501Srpaulo if (is_multi) { 385214501Srpaulo /* in case of multiple IEs stored in buffer */ 386214501Srpaulo old_ie = old_ie_buff ? wpabuf_head_u8(old_ie_buff) : NULL; 387214501Srpaulo new_ie = new_ie_buff ? wpabuf_head_u8(new_ie_buff) : NULL; 388214501Srpaulo old_ie_len = old_ie_buff ? wpabuf_len(old_ie_buff) : 0; 389214501Srpaulo new_ie_len = new_ie_buff ? wpabuf_len(new_ie_buff) : 0; 390214501Srpaulo } else { 391214501Srpaulo /* in case of single IE */ 392214501Srpaulo old_ie_len = old_ie ? old_ie[1] + 2 : 0; 393214501Srpaulo new_ie_len = new_ie ? new_ie[1] + 2 : 0; 394214501Srpaulo } 395214501Srpaulo 396252726Srpaulo if (!old_ie || !new_ie) 397252726Srpaulo ret = !old_ie && !new_ie; 398252726Srpaulo else 399252726Srpaulo ret = (old_ie_len == new_ie_len && 400252726Srpaulo os_memcmp(old_ie, new_ie, old_ie_len) == 0); 401214501Srpaulo 402214501Srpaulo wpabuf_free(old_ie_buff); 403214501Srpaulo wpabuf_free(new_ie_buff); 404214501Srpaulo 405214501Srpaulo return ret; 406214501Srpaulo} 407214501Srpaulo 408214501Srpaulo 409214501Srpaulostatic u32 wpa_bss_compare_res(const struct wpa_bss *old, 410214501Srpaulo const struct wpa_scan_res *new) 411214501Srpaulo{ 412214501Srpaulo u32 changes = 0; 413214501Srpaulo int caps_diff = old->caps ^ new->caps; 414214501Srpaulo 415214501Srpaulo if (old->freq != new->freq) 416214501Srpaulo changes |= WPA_BSS_FREQ_CHANGED_FLAG; 417214501Srpaulo 418214501Srpaulo if (old->level != new->level) 419214501Srpaulo changes |= WPA_BSS_SIGNAL_CHANGED_FLAG; 420214501Srpaulo 421214501Srpaulo if (caps_diff & IEEE80211_CAP_PRIVACY) 422214501Srpaulo changes |= WPA_BSS_PRIVACY_CHANGED_FLAG; 423214501Srpaulo 424214501Srpaulo if (caps_diff & IEEE80211_CAP_IBSS) 425214501Srpaulo changes |= WPA_BSS_MODE_CHANGED_FLAG; 426214501Srpaulo 427214501Srpaulo if (old->ie_len == new->ie_len && 428214501Srpaulo os_memcmp(old + 1, new + 1, old->ie_len) == 0) 429214501Srpaulo return changes; 430214501Srpaulo changes |= WPA_BSS_IES_CHANGED_FLAG; 431214501Srpaulo 432214501Srpaulo if (!are_ies_equal(old, new, WPA_IE_VENDOR_TYPE)) 433214501Srpaulo changes |= WPA_BSS_WPAIE_CHANGED_FLAG; 434214501Srpaulo 435214501Srpaulo if (!are_ies_equal(old, new, WLAN_EID_RSN)) 436214501Srpaulo changes |= WPA_BSS_RSNIE_CHANGED_FLAG; 437214501Srpaulo 438214501Srpaulo if (!are_ies_equal(old, new, WPS_IE_VENDOR_TYPE)) 439214501Srpaulo changes |= WPA_BSS_WPS_CHANGED_FLAG; 440214501Srpaulo 441214501Srpaulo if (!are_ies_equal(old, new, WLAN_EID_SUPP_RATES) || 442214501Srpaulo !are_ies_equal(old, new, WLAN_EID_EXT_SUPP_RATES)) 443214501Srpaulo changes |= WPA_BSS_RATES_CHANGED_FLAG; 444214501Srpaulo 445214501Srpaulo return changes; 446214501Srpaulo} 447214501Srpaulo 448214501Srpaulo 449214501Srpaulostatic void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes, 450214501Srpaulo const struct wpa_bss *bss) 451214501Srpaulo{ 452214501Srpaulo if (changes & WPA_BSS_FREQ_CHANGED_FLAG) 453214501Srpaulo wpas_notify_bss_freq_changed(wpa_s, bss->id); 454214501Srpaulo 455214501Srpaulo if (changes & WPA_BSS_SIGNAL_CHANGED_FLAG) 456214501Srpaulo wpas_notify_bss_signal_changed(wpa_s, bss->id); 457214501Srpaulo 458214501Srpaulo if (changes & WPA_BSS_PRIVACY_CHANGED_FLAG) 459214501Srpaulo wpas_notify_bss_privacy_changed(wpa_s, bss->id); 460214501Srpaulo 461214501Srpaulo if (changes & WPA_BSS_MODE_CHANGED_FLAG) 462214501Srpaulo wpas_notify_bss_mode_changed(wpa_s, bss->id); 463214501Srpaulo 464214501Srpaulo if (changes & WPA_BSS_WPAIE_CHANGED_FLAG) 465214501Srpaulo wpas_notify_bss_wpaie_changed(wpa_s, bss->id); 466214501Srpaulo 467214501Srpaulo if (changes & WPA_BSS_RSNIE_CHANGED_FLAG) 468214501Srpaulo wpas_notify_bss_rsnie_changed(wpa_s, bss->id); 469214501Srpaulo 470214501Srpaulo if (changes & WPA_BSS_WPS_CHANGED_FLAG) 471214501Srpaulo wpas_notify_bss_wps_changed(wpa_s, bss->id); 472214501Srpaulo 473214501Srpaulo if (changes & WPA_BSS_IES_CHANGED_FLAG) 474214501Srpaulo wpas_notify_bss_ies_changed(wpa_s, bss->id); 475214501Srpaulo 476214501Srpaulo if (changes & WPA_BSS_RATES_CHANGED_FLAG) 477214501Srpaulo wpas_notify_bss_rates_changed(wpa_s, bss->id); 478214501Srpaulo} 479214501Srpaulo 480214501Srpaulo 481252726Srpaulostatic struct wpa_bss * 482252726Srpaulowpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, 483252726Srpaulo struct wpa_scan_res *res) 484214501Srpaulo{ 485214501Srpaulo u32 changes; 486214501Srpaulo 487214501Srpaulo changes = wpa_bss_compare_res(bss, res); 488214501Srpaulo bss->scan_miss_count = 0; 489214501Srpaulo bss->last_update_idx = wpa_s->bss_update_idx; 490214501Srpaulo wpa_bss_copy_res(bss, res); 491214501Srpaulo /* Move the entry to the end of the list */ 492214501Srpaulo dl_list_del(&bss->list); 493214501Srpaulo if (bss->ie_len + bss->beacon_ie_len >= 494214501Srpaulo res->ie_len + res->beacon_ie_len) { 495214501Srpaulo os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len); 496214501Srpaulo bss->ie_len = res->ie_len; 497214501Srpaulo bss->beacon_ie_len = res->beacon_ie_len; 498214501Srpaulo } else { 499214501Srpaulo struct wpa_bss *nbss; 500214501Srpaulo struct dl_list *prev = bss->list_id.prev; 501214501Srpaulo dl_list_del(&bss->list_id); 502214501Srpaulo nbss = os_realloc(bss, sizeof(*bss) + res->ie_len + 503214501Srpaulo res->beacon_ie_len); 504214501Srpaulo if (nbss) { 505252726Srpaulo unsigned int i; 506252726Srpaulo for (i = 0; i < wpa_s->last_scan_res_used; i++) { 507252726Srpaulo if (wpa_s->last_scan_res[i] == bss) { 508252726Srpaulo wpa_s->last_scan_res[i] = nbss; 509252726Srpaulo break; 510252726Srpaulo } 511252726Srpaulo } 512252726Srpaulo if (wpa_s->current_bss == bss) 513252726Srpaulo wpa_s->current_bss = nbss; 514214501Srpaulo bss = nbss; 515214501Srpaulo os_memcpy(bss + 1, res + 1, 516214501Srpaulo res->ie_len + res->beacon_ie_len); 517214501Srpaulo bss->ie_len = res->ie_len; 518214501Srpaulo bss->beacon_ie_len = res->beacon_ie_len; 519214501Srpaulo } 520214501Srpaulo dl_list_add(prev, &bss->list_id); 521214501Srpaulo } 522252726Srpaulo if (changes & WPA_BSS_IES_CHANGED_FLAG) 523252726Srpaulo wpa_bss_set_hessid(bss); 524214501Srpaulo dl_list_add_tail(&wpa_s->bss, &bss->list); 525214501Srpaulo 526214501Srpaulo notify_bss_changes(wpa_s, changes, bss); 527214501Srpaulo 528252726Srpaulo return bss; 529214501Srpaulo} 530214501Srpaulo 531214501Srpaulo 532252726Srpaulo/** 533252726Srpaulo * wpa_bss_update_start - Start a BSS table update from scan results 534252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data 535252726Srpaulo * 536252726Srpaulo * This function is called at the start of each BSS table update round for new 537252726Srpaulo * scan results. The actual scan result entries are indicated with calls to 538252726Srpaulo * wpa_bss_update_scan_res() and the update round is finished with a call to 539252726Srpaulo * wpa_bss_update_end(). 540252726Srpaulo */ 541214501Srpaulovoid wpa_bss_update_start(struct wpa_supplicant *wpa_s) 542214501Srpaulo{ 543214501Srpaulo wpa_s->bss_update_idx++; 544252726Srpaulo wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Start scan result update %u", 545252726Srpaulo wpa_s->bss_update_idx); 546252726Srpaulo wpa_s->last_scan_res_used = 0; 547214501Srpaulo} 548214501Srpaulo 549214501Srpaulo 550252726Srpaulo/** 551252726Srpaulo * wpa_bss_update_scan_res - Update a BSS table entry based on a scan result 552252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data 553252726Srpaulo * @res: Scan result 554252726Srpaulo * 555252726Srpaulo * This function updates a BSS table entry (or adds one) based on a scan result. 556252726Srpaulo * This is called separately for each scan result between the calls to 557252726Srpaulo * wpa_bss_update_start() and wpa_bss_update_end(). 558252726Srpaulo */ 559214501Srpaulovoid wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s, 560214501Srpaulo struct wpa_scan_res *res) 561214501Srpaulo{ 562252726Srpaulo const u8 *ssid, *p2p; 563214501Srpaulo struct wpa_bss *bss; 564214501Srpaulo 565214501Srpaulo ssid = wpa_scan_get_ie(res, WLAN_EID_SSID); 566214501Srpaulo if (ssid == NULL) { 567252726Srpaulo wpa_dbg(wpa_s, MSG_DEBUG, "BSS: No SSID IE included for " 568252726Srpaulo MACSTR, MAC2STR(res->bssid)); 569214501Srpaulo return; 570214501Srpaulo } 571214501Srpaulo if (ssid[1] > 32) { 572252726Srpaulo wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Too long SSID IE included for " 573252726Srpaulo MACSTR, MAC2STR(res->bssid)); 574214501Srpaulo return; 575214501Srpaulo } 576214501Srpaulo 577252726Srpaulo p2p = wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE); 578252726Srpaulo#ifdef CONFIG_P2P 579252726Srpaulo if (p2p == NULL && 580252726Srpaulo wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) { 581252726Srpaulo /* 582252726Srpaulo * If it's a P2P specific interface, then don't update 583252726Srpaulo * the scan result without a P2P IE. 584252726Srpaulo */ 585252726Srpaulo wpa_printf(MSG_DEBUG, "BSS: No P2P IE - skipping BSS " MACSTR 586252726Srpaulo " update for P2P interface", MAC2STR(res->bssid)); 587252726Srpaulo return; 588252726Srpaulo } 589252726Srpaulo#endif /* CONFIG_P2P */ 590252726Srpaulo if (p2p && ssid[1] == P2P_WILDCARD_SSID_LEN && 591252726Srpaulo os_memcmp(ssid + 2, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) == 0) 592252726Srpaulo return; /* Skip P2P listen discovery results here */ 593252726Srpaulo 594214501Srpaulo /* TODO: add option for ignoring BSSes we are not interested in 595214501Srpaulo * (to save memory) */ 596214501Srpaulo bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]); 597214501Srpaulo if (bss == NULL) 598252726Srpaulo bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res); 599214501Srpaulo else 600252726Srpaulo bss = wpa_bss_update(wpa_s, bss, res); 601252726Srpaulo 602252726Srpaulo if (bss == NULL) 603252726Srpaulo return; 604252726Srpaulo if (wpa_s->last_scan_res_used >= wpa_s->last_scan_res_size) { 605252726Srpaulo struct wpa_bss **n; 606252726Srpaulo unsigned int siz; 607252726Srpaulo if (wpa_s->last_scan_res_size == 0) 608252726Srpaulo siz = 32; 609252726Srpaulo else 610252726Srpaulo siz = wpa_s->last_scan_res_size * 2; 611252726Srpaulo n = os_realloc_array(wpa_s->last_scan_res, siz, 612252726Srpaulo sizeof(struct wpa_bss *)); 613252726Srpaulo if (n == NULL) 614252726Srpaulo return; 615252726Srpaulo wpa_s->last_scan_res = n; 616252726Srpaulo wpa_s->last_scan_res_size = siz; 617252726Srpaulo } 618252726Srpaulo 619252726Srpaulo wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss; 620214501Srpaulo} 621214501Srpaulo 622214501Srpaulo 623214501Srpaulostatic int wpa_bss_included_in_scan(const struct wpa_bss *bss, 624214501Srpaulo const struct scan_info *info) 625214501Srpaulo{ 626214501Srpaulo int found; 627214501Srpaulo size_t i; 628214501Srpaulo 629214501Srpaulo if (info == NULL) 630214501Srpaulo return 1; 631214501Srpaulo 632214501Srpaulo if (info->num_freqs) { 633214501Srpaulo found = 0; 634214501Srpaulo for (i = 0; i < info->num_freqs; i++) { 635214501Srpaulo if (bss->freq == info->freqs[i]) { 636214501Srpaulo found = 1; 637214501Srpaulo break; 638214501Srpaulo } 639214501Srpaulo } 640214501Srpaulo if (!found) 641214501Srpaulo return 0; 642214501Srpaulo } 643214501Srpaulo 644214501Srpaulo if (info->num_ssids) { 645214501Srpaulo found = 0; 646214501Srpaulo for (i = 0; i < info->num_ssids; i++) { 647214501Srpaulo const struct wpa_driver_scan_ssid *s = &info->ssids[i]; 648214501Srpaulo if ((s->ssid == NULL || s->ssid_len == 0) || 649214501Srpaulo (s->ssid_len == bss->ssid_len && 650214501Srpaulo os_memcmp(s->ssid, bss->ssid, bss->ssid_len) == 651214501Srpaulo 0)) { 652214501Srpaulo found = 1; 653214501Srpaulo break; 654214501Srpaulo } 655214501Srpaulo } 656214501Srpaulo if (!found) 657214501Srpaulo return 0; 658214501Srpaulo } 659214501Srpaulo 660214501Srpaulo return 1; 661214501Srpaulo} 662214501Srpaulo 663214501Srpaulo 664252726Srpaulo/** 665252726Srpaulo * wpa_bss_update_end - End a BSS table update from scan results 666252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data 667252726Srpaulo * @info: Information about scan parameters 668252726Srpaulo * @new_scan: Whether this update round was based on a new scan 669252726Srpaulo * 670252726Srpaulo * This function is called at the end of each BSS table update round for new 671252726Srpaulo * scan results. The start of the update was indicated with a call to 672252726Srpaulo * wpa_bss_update_start(). 673252726Srpaulo */ 674214501Srpaulovoid wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info, 675214501Srpaulo int new_scan) 676214501Srpaulo{ 677214501Srpaulo struct wpa_bss *bss, *n; 678214501Srpaulo 679252726Srpaulo wpa_s->last_scan_full = 0; 680252726Srpaulo os_get_time(&wpa_s->last_scan); 681214501Srpaulo if (!new_scan) 682214501Srpaulo return; /* do not expire entries without new scan */ 683214501Srpaulo 684252726Srpaulo if (info && !info->aborted && !info->freqs) { 685252726Srpaulo size_t i; 686252726Srpaulo if (info->num_ssids == 0) { 687252726Srpaulo wpa_s->last_scan_full = 1; 688252726Srpaulo } else { 689252726Srpaulo for (i = 0; i < info->num_ssids; i++) { 690252726Srpaulo if (info->ssids[i].ssid == NULL || 691252726Srpaulo info->ssids[i].ssid_len == 0) { 692252726Srpaulo wpa_s->last_scan_full = 1; 693252726Srpaulo break; 694252726Srpaulo } 695252726Srpaulo } 696252726Srpaulo } 697252726Srpaulo } 698252726Srpaulo 699214501Srpaulo dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) { 700214501Srpaulo if (wpa_bss_in_use(wpa_s, bss)) 701214501Srpaulo continue; 702214501Srpaulo if (!wpa_bss_included_in_scan(bss, info)) 703214501Srpaulo continue; /* expire only BSSes that were scanned */ 704214501Srpaulo if (bss->last_update_idx < wpa_s->bss_update_idx) 705214501Srpaulo bss->scan_miss_count++; 706252726Srpaulo if (bss->scan_miss_count >= 707252726Srpaulo wpa_s->conf->bss_expiration_scan_count) { 708252726Srpaulo wpa_bss_remove(wpa_s, bss, "no match in scan"); 709214501Srpaulo } 710214501Srpaulo } 711252726Srpaulo 712252726Srpaulo wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%u/%u " 713252726Srpaulo "last_scan_full=%d", 714252726Srpaulo wpa_s->last_scan_res_used, wpa_s->last_scan_res_size, 715252726Srpaulo wpa_s->last_scan_full); 716214501Srpaulo} 717214501Srpaulo 718214501Srpaulo 719252726Srpaulo/** 720252726Srpaulo * wpa_bss_flush_by_age - Flush old BSS entries 721252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data 722252726Srpaulo * @age: Maximum entry age in seconds 723252726Srpaulo * 724252726Srpaulo * Remove BSS entries that have not been updated during the last @age seconds. 725252726Srpaulo */ 726252726Srpaulovoid wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age) 727214501Srpaulo{ 728214501Srpaulo struct wpa_bss *bss, *n; 729214501Srpaulo struct os_time t; 730214501Srpaulo 731214501Srpaulo if (dl_list_empty(&wpa_s->bss)) 732214501Srpaulo return; 733214501Srpaulo 734214501Srpaulo os_get_time(&t); 735252726Srpaulo t.sec -= age; 736214501Srpaulo 737214501Srpaulo dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) { 738214501Srpaulo if (wpa_bss_in_use(wpa_s, bss)) 739214501Srpaulo continue; 740214501Srpaulo 741214501Srpaulo if (os_time_before(&bss->last_update, &t)) { 742252726Srpaulo wpa_bss_remove(wpa_s, bss, __func__); 743214501Srpaulo } else 744214501Srpaulo break; 745214501Srpaulo } 746252726Srpaulo} 747252726Srpaulo 748252726Srpaulo 749252726Srpaulostatic void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx) 750252726Srpaulo{ 751252726Srpaulo struct wpa_supplicant *wpa_s = eloop_ctx; 752252726Srpaulo 753252726Srpaulo wpa_bss_flush_by_age(wpa_s, wpa_s->conf->bss_expiration_age); 754214501Srpaulo eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0, 755214501Srpaulo wpa_bss_timeout, wpa_s, NULL); 756214501Srpaulo} 757214501Srpaulo 758214501Srpaulo 759252726Srpaulo/** 760252726Srpaulo * wpa_bss_init - Initialize BSS table 761252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data 762252726Srpaulo * Returns: 0 on success, -1 on failure 763252726Srpaulo * 764252726Srpaulo * This prepares BSS table lists and timer for periodic updates. The BSS table 765252726Srpaulo * is deinitialized with wpa_bss_deinit() once not needed anymore. 766252726Srpaulo */ 767214501Srpauloint wpa_bss_init(struct wpa_supplicant *wpa_s) 768214501Srpaulo{ 769214501Srpaulo dl_list_init(&wpa_s->bss); 770214501Srpaulo dl_list_init(&wpa_s->bss_id); 771214501Srpaulo eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0, 772214501Srpaulo wpa_bss_timeout, wpa_s, NULL); 773214501Srpaulo return 0; 774214501Srpaulo} 775214501Srpaulo 776214501Srpaulo 777252726Srpaulo/** 778252726Srpaulo * wpa_bss_flush - Flush all unused BSS entries 779252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data 780252726Srpaulo */ 781252726Srpaulovoid wpa_bss_flush(struct wpa_supplicant *wpa_s) 782214501Srpaulo{ 783214501Srpaulo struct wpa_bss *bss, *n; 784252726Srpaulo 785214501Srpaulo if (wpa_s->bss.next == NULL) 786214501Srpaulo return; /* BSS table not yet initialized */ 787252726Srpaulo 788252726Srpaulo dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) { 789252726Srpaulo if (wpa_bss_in_use(wpa_s, bss)) 790252726Srpaulo continue; 791252726Srpaulo wpa_bss_remove(wpa_s, bss, __func__); 792252726Srpaulo } 793214501Srpaulo} 794214501Srpaulo 795214501Srpaulo 796252726Srpaulo/** 797252726Srpaulo * wpa_bss_deinit - Deinitialize BSS table 798252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data 799252726Srpaulo */ 800252726Srpaulovoid wpa_bss_deinit(struct wpa_supplicant *wpa_s) 801252726Srpaulo{ 802252726Srpaulo eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL); 803252726Srpaulo wpa_bss_flush(wpa_s); 804252726Srpaulo} 805252726Srpaulo 806252726Srpaulo 807252726Srpaulo/** 808252726Srpaulo * wpa_bss_get_bssid - Fetch a BSS table entry based on BSSID 809252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data 810252726Srpaulo * @bssid: BSSID 811252726Srpaulo * Returns: Pointer to the BSS entry or %NULL if not found 812252726Srpaulo */ 813214501Srpaulostruct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s, 814214501Srpaulo const u8 *bssid) 815214501Srpaulo{ 816214501Srpaulo struct wpa_bss *bss; 817252726Srpaulo if (!wpa_supplicant_filter_bssid_match(wpa_s, bssid)) 818252726Srpaulo return NULL; 819252726Srpaulo dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) { 820214501Srpaulo if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0) 821214501Srpaulo return bss; 822214501Srpaulo } 823214501Srpaulo return NULL; 824214501Srpaulo} 825214501Srpaulo 826214501Srpaulo 827252726Srpaulo#ifdef CONFIG_P2P 828252726Srpaulo/** 829252726Srpaulo * wpa_bss_get_p2p_dev_addr - Fetch a BSS table entry based on P2P Device Addr 830252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data 831252726Srpaulo * @dev_addr: P2P Device Address of the GO 832252726Srpaulo * Returns: Pointer to the BSS entry or %NULL if not found 833252726Srpaulo */ 834252726Srpaulostruct wpa_bss * wpa_bss_get_p2p_dev_addr(struct wpa_supplicant *wpa_s, 835252726Srpaulo const u8 *dev_addr) 836252726Srpaulo{ 837252726Srpaulo struct wpa_bss *bss; 838252726Srpaulo dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss, list) { 839252726Srpaulo u8 addr[ETH_ALEN]; 840252726Srpaulo if (p2p_parse_dev_addr((const u8 *) (bss + 1), bss->ie_len, 841252726Srpaulo addr) == 0 && 842252726Srpaulo os_memcmp(addr, dev_addr, ETH_ALEN) == 0) 843252726Srpaulo return bss; 844252726Srpaulo } 845252726Srpaulo return NULL; 846252726Srpaulo} 847252726Srpaulo#endif /* CONFIG_P2P */ 848252726Srpaulo 849252726Srpaulo 850252726Srpaulo/** 851252726Srpaulo * wpa_bss_get_id - Fetch a BSS table entry based on identifier 852252726Srpaulo * @wpa_s: Pointer to wpa_supplicant data 853252726Srpaulo * @id: Unique identifier (struct wpa_bss::id) assigned for the entry 854252726Srpaulo * Returns: Pointer to the BSS entry or %NULL if not found 855252726Srpaulo */ 856214501Srpaulostruct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id) 857214501Srpaulo{ 858214501Srpaulo struct wpa_bss *bss; 859214501Srpaulo dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { 860214501Srpaulo if (bss->id == id) 861214501Srpaulo return bss; 862214501Srpaulo } 863214501Srpaulo return NULL; 864214501Srpaulo} 865214501Srpaulo 866214501Srpaulo 867252726Srpaulo/** 868252726Srpaulo * wpa_bss_get_ie - Fetch a specified information element from a BSS entry 869252726Srpaulo * @bss: BSS table entry 870252726Srpaulo * @ie: Information element identitifier (WLAN_EID_*) 871252726Srpaulo * Returns: Pointer to the information element (id field) or %NULL if not found 872252726Srpaulo * 873252726Srpaulo * This function returns the first matching information element in the BSS 874252726Srpaulo * entry. 875252726Srpaulo */ 876214501Srpauloconst u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie) 877214501Srpaulo{ 878214501Srpaulo const u8 *end, *pos; 879214501Srpaulo 880214501Srpaulo pos = (const u8 *) (bss + 1); 881214501Srpaulo end = pos + bss->ie_len; 882214501Srpaulo 883214501Srpaulo while (pos + 1 < end) { 884214501Srpaulo if (pos + 2 + pos[1] > end) 885214501Srpaulo break; 886214501Srpaulo if (pos[0] == ie) 887214501Srpaulo return pos; 888214501Srpaulo pos += 2 + pos[1]; 889214501Srpaulo } 890214501Srpaulo 891214501Srpaulo return NULL; 892214501Srpaulo} 893214501Srpaulo 894214501Srpaulo 895252726Srpaulo/** 896252726Srpaulo * wpa_bss_get_vendor_ie - Fetch a vendor information element from a BSS entry 897252726Srpaulo * @bss: BSS table entry 898252726Srpaulo * @vendor_type: Vendor type (four octets starting the IE payload) 899252726Srpaulo * Returns: Pointer to the information element (id field) or %NULL if not found 900252726Srpaulo * 901252726Srpaulo * This function returns the first matching information element in the BSS 902252726Srpaulo * entry. 903252726Srpaulo */ 904214501Srpauloconst u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type) 905214501Srpaulo{ 906214501Srpaulo const u8 *end, *pos; 907214501Srpaulo 908214501Srpaulo pos = (const u8 *) (bss + 1); 909214501Srpaulo end = pos + bss->ie_len; 910214501Srpaulo 911214501Srpaulo while (pos + 1 < end) { 912214501Srpaulo if (pos + 2 + pos[1] > end) 913214501Srpaulo break; 914214501Srpaulo if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && 915214501Srpaulo vendor_type == WPA_GET_BE32(&pos[2])) 916214501Srpaulo return pos; 917214501Srpaulo pos += 2 + pos[1]; 918214501Srpaulo } 919214501Srpaulo 920214501Srpaulo return NULL; 921214501Srpaulo} 922214501Srpaulo 923214501Srpaulo 924252726Srpaulo/** 925252726Srpaulo * wpa_bss_get_vendor_ie_multi - Fetch vendor IE data from a BSS entry 926252726Srpaulo * @bss: BSS table entry 927252726Srpaulo * @vendor_type: Vendor type (four octets starting the IE payload) 928252726Srpaulo * Returns: Pointer to the information element payload or %NULL if not found 929252726Srpaulo * 930252726Srpaulo * This function returns concatenated payload of possibly fragmented vendor 931252726Srpaulo * specific information elements in the BSS entry. The caller is responsible for 932252726Srpaulo * freeing the returned buffer. 933252726Srpaulo */ 934214501Srpaulostruct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss, 935214501Srpaulo u32 vendor_type) 936214501Srpaulo{ 937214501Srpaulo struct wpabuf *buf; 938214501Srpaulo const u8 *end, *pos; 939214501Srpaulo 940214501Srpaulo buf = wpabuf_alloc(bss->ie_len); 941214501Srpaulo if (buf == NULL) 942214501Srpaulo return NULL; 943214501Srpaulo 944214501Srpaulo pos = (const u8 *) (bss + 1); 945214501Srpaulo end = pos + bss->ie_len; 946214501Srpaulo 947214501Srpaulo while (pos + 1 < end) { 948214501Srpaulo if (pos + 2 + pos[1] > end) 949214501Srpaulo break; 950214501Srpaulo if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && 951214501Srpaulo vendor_type == WPA_GET_BE32(&pos[2])) 952214501Srpaulo wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4); 953214501Srpaulo pos += 2 + pos[1]; 954214501Srpaulo } 955214501Srpaulo 956214501Srpaulo if (wpabuf_len(buf) == 0) { 957214501Srpaulo wpabuf_free(buf); 958214501Srpaulo buf = NULL; 959214501Srpaulo } 960214501Srpaulo 961214501Srpaulo return buf; 962214501Srpaulo} 963214501Srpaulo 964214501Srpaulo 965252726Srpaulo/** 966252726Srpaulo * wpa_bss_get_vendor_ie_multi_beacon - Fetch vendor IE data from a BSS entry 967252726Srpaulo * @bss: BSS table entry 968252726Srpaulo * @vendor_type: Vendor type (four octets starting the IE payload) 969252726Srpaulo * Returns: Pointer to the information element payload or %NULL if not found 970252726Srpaulo * 971252726Srpaulo * This function returns concatenated payload of possibly fragmented vendor 972252726Srpaulo * specific information elements in the BSS entry. The caller is responsible for 973252726Srpaulo * freeing the returned buffer. 974252726Srpaulo * 975252726Srpaulo * This function is like wpa_bss_get_vendor_ie_multi(), but uses IE buffer only 976252726Srpaulo * from Beacon frames instead of either Beacon or Probe Response frames. 977252726Srpaulo */ 978252726Srpaulostruct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss, 979252726Srpaulo u32 vendor_type) 980252726Srpaulo{ 981252726Srpaulo struct wpabuf *buf; 982252726Srpaulo const u8 *end, *pos; 983252726Srpaulo 984252726Srpaulo buf = wpabuf_alloc(bss->beacon_ie_len); 985252726Srpaulo if (buf == NULL) 986252726Srpaulo return NULL; 987252726Srpaulo 988252726Srpaulo pos = (const u8 *) (bss + 1); 989252726Srpaulo pos += bss->ie_len; 990252726Srpaulo end = pos + bss->beacon_ie_len; 991252726Srpaulo 992252726Srpaulo while (pos + 1 < end) { 993252726Srpaulo if (pos + 2 + pos[1] > end) 994252726Srpaulo break; 995252726Srpaulo if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && 996252726Srpaulo vendor_type == WPA_GET_BE32(&pos[2])) 997252726Srpaulo wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4); 998252726Srpaulo pos += 2 + pos[1]; 999252726Srpaulo } 1000252726Srpaulo 1001252726Srpaulo if (wpabuf_len(buf) == 0) { 1002252726Srpaulo wpabuf_free(buf); 1003252726Srpaulo buf = NULL; 1004252726Srpaulo } 1005252726Srpaulo 1006252726Srpaulo return buf; 1007252726Srpaulo} 1008252726Srpaulo 1009252726Srpaulo 1010252726Srpaulo/** 1011252726Srpaulo * wpa_bss_get_max_rate - Get maximum legacy TX rate supported in a BSS 1012252726Srpaulo * @bss: BSS table entry 1013252726Srpaulo * Returns: Maximum legacy rate in units of 500 kbps 1014252726Srpaulo */ 1015214501Srpauloint wpa_bss_get_max_rate(const struct wpa_bss *bss) 1016214501Srpaulo{ 1017214501Srpaulo int rate = 0; 1018214501Srpaulo const u8 *ie; 1019214501Srpaulo int i; 1020214501Srpaulo 1021214501Srpaulo ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES); 1022214501Srpaulo for (i = 0; ie && i < ie[1]; i++) { 1023214501Srpaulo if ((ie[i + 2] & 0x7f) > rate) 1024214501Srpaulo rate = ie[i + 2] & 0x7f; 1025214501Srpaulo } 1026214501Srpaulo 1027214501Srpaulo ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); 1028214501Srpaulo for (i = 0; ie && i < ie[1]; i++) { 1029214501Srpaulo if ((ie[i + 2] & 0x7f) > rate) 1030214501Srpaulo rate = ie[i + 2] & 0x7f; 1031214501Srpaulo } 1032214501Srpaulo 1033214501Srpaulo return rate; 1034214501Srpaulo} 1035214501Srpaulo 1036214501Srpaulo 1037252726Srpaulo/** 1038252726Srpaulo * wpa_bss_get_bit_rates - Get legacy TX rates supported in a BSS 1039252726Srpaulo * @bss: BSS table entry 1040252726Srpaulo * @rates: Buffer for returning a pointer to the rates list (units of 500 kbps) 1041252726Srpaulo * Returns: number of legacy TX rates or -1 on failure 1042252726Srpaulo * 1043252726Srpaulo * The caller is responsible for freeing the returned buffer with os_free() in 1044252726Srpaulo * case of success. 1045252726Srpaulo */ 1046214501Srpauloint wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates) 1047214501Srpaulo{ 1048214501Srpaulo const u8 *ie, *ie2; 1049214501Srpaulo int i, j; 1050214501Srpaulo unsigned int len; 1051214501Srpaulo u8 *r; 1052214501Srpaulo 1053214501Srpaulo ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES); 1054214501Srpaulo ie2 = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); 1055214501Srpaulo 1056214501Srpaulo len = (ie ? ie[1] : 0) + (ie2 ? ie2[1] : 0); 1057214501Srpaulo 1058214501Srpaulo r = os_malloc(len); 1059214501Srpaulo if (!r) 1060214501Srpaulo return -1; 1061214501Srpaulo 1062214501Srpaulo for (i = 0; ie && i < ie[1]; i++) 1063214501Srpaulo r[i] = ie[i + 2] & 0x7f; 1064214501Srpaulo 1065214501Srpaulo for (j = 0; ie2 && j < ie2[1]; j++) 1066214501Srpaulo r[i + j] = ie2[j + 2] & 0x7f; 1067214501Srpaulo 1068214501Srpaulo *rates = r; 1069214501Srpaulo return len; 1070214501Srpaulo} 1071