1214501Srpaulo/* 2214501Srpaulo * hostapd / AP table 3214501Srpaulo * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> 4214501Srpaulo * Copyright (c) 2003-2004, Instant802 Networks, Inc. 5214501Srpaulo * Copyright (c) 2006, Devicescape Software, Inc. 6214501Srpaulo * 7252726Srpaulo * This software may be distributed under the terms of the BSD license. 8252726Srpaulo * See README for more details. 9214501Srpaulo */ 10214501Srpaulo 11214501Srpaulo#include "utils/includes.h" 12214501Srpaulo 13214501Srpaulo#include "utils/common.h" 14214501Srpaulo#include "utils/eloop.h" 15214501Srpaulo#include "common/ieee802_11_defs.h" 16214501Srpaulo#include "common/ieee802_11_common.h" 17214501Srpaulo#include "drivers/driver.h" 18214501Srpaulo#include "hostapd.h" 19214501Srpaulo#include "ap_config.h" 20214501Srpaulo#include "ieee802_11.h" 21214501Srpaulo#include "sta_info.h" 22214501Srpaulo#include "beacon.h" 23214501Srpaulo#include "ap_list.h" 24214501Srpaulo 25214501Srpaulo 26214501Srpaulo/* AP list is a double linked list with head->prev pointing to the end of the 27214501Srpaulo * list and tail->next = NULL. Entries are moved to the head of the list 28214501Srpaulo * whenever a beacon has been received from the AP in question. The tail entry 29214501Srpaulo * in this link will thus be the least recently used entry. */ 30214501Srpaulo 31214501Srpaulo 32214501Srpaulostatic int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap) 33214501Srpaulo{ 34214501Srpaulo int i; 35214501Srpaulo 36214501Srpaulo if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G || 37214501Srpaulo iface->conf->channel != ap->channel) 38214501Srpaulo return 0; 39214501Srpaulo 40214501Srpaulo if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT)) 41214501Srpaulo return 1; 42214501Srpaulo 43214501Srpaulo for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) { 44214501Srpaulo int rate = (ap->supported_rates[i] & 0x7f) * 5; 45214501Srpaulo if (rate == 60 || rate == 90 || rate > 110) 46214501Srpaulo return 0; 47214501Srpaulo } 48214501Srpaulo 49214501Srpaulo return 1; 50214501Srpaulo} 51214501Srpaulo 52214501Srpaulo 53214501Srpaulostruct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap) 54214501Srpaulo{ 55214501Srpaulo struct ap_info *s; 56214501Srpaulo 57214501Srpaulo s = iface->ap_hash[STA_HASH(ap)]; 58214501Srpaulo while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0) 59214501Srpaulo s = s->hnext; 60214501Srpaulo return s; 61214501Srpaulo} 62214501Srpaulo 63214501Srpaulo 64214501Srpaulostatic void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap) 65214501Srpaulo{ 66214501Srpaulo if (iface->ap_list) { 67214501Srpaulo ap->prev = iface->ap_list->prev; 68214501Srpaulo iface->ap_list->prev = ap; 69214501Srpaulo } else 70214501Srpaulo ap->prev = ap; 71214501Srpaulo ap->next = iface->ap_list; 72214501Srpaulo iface->ap_list = ap; 73214501Srpaulo} 74214501Srpaulo 75214501Srpaulo 76214501Srpaulostatic void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap) 77214501Srpaulo{ 78214501Srpaulo if (iface->ap_list == ap) 79214501Srpaulo iface->ap_list = ap->next; 80214501Srpaulo else 81214501Srpaulo ap->prev->next = ap->next; 82214501Srpaulo 83214501Srpaulo if (ap->next) 84214501Srpaulo ap->next->prev = ap->prev; 85214501Srpaulo else if (iface->ap_list) 86214501Srpaulo iface->ap_list->prev = ap->prev; 87214501Srpaulo} 88214501Srpaulo 89214501Srpaulo 90214501Srpaulostatic void ap_ap_iter_list_add(struct hostapd_iface *iface, 91214501Srpaulo struct ap_info *ap) 92214501Srpaulo{ 93214501Srpaulo if (iface->ap_iter_list) { 94214501Srpaulo ap->iter_prev = iface->ap_iter_list->iter_prev; 95214501Srpaulo iface->ap_iter_list->iter_prev = ap; 96214501Srpaulo } else 97214501Srpaulo ap->iter_prev = ap; 98214501Srpaulo ap->iter_next = iface->ap_iter_list; 99214501Srpaulo iface->ap_iter_list = ap; 100214501Srpaulo} 101214501Srpaulo 102214501Srpaulo 103214501Srpaulostatic void ap_ap_iter_list_del(struct hostapd_iface *iface, 104214501Srpaulo struct ap_info *ap) 105214501Srpaulo{ 106214501Srpaulo if (iface->ap_iter_list == ap) 107214501Srpaulo iface->ap_iter_list = ap->iter_next; 108214501Srpaulo else 109214501Srpaulo ap->iter_prev->iter_next = ap->iter_next; 110214501Srpaulo 111214501Srpaulo if (ap->iter_next) 112214501Srpaulo ap->iter_next->iter_prev = ap->iter_prev; 113214501Srpaulo else if (iface->ap_iter_list) 114214501Srpaulo iface->ap_iter_list->iter_prev = ap->iter_prev; 115214501Srpaulo} 116214501Srpaulo 117214501Srpaulo 118214501Srpaulostatic void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap) 119214501Srpaulo{ 120214501Srpaulo ap->hnext = iface->ap_hash[STA_HASH(ap->addr)]; 121214501Srpaulo iface->ap_hash[STA_HASH(ap->addr)] = ap; 122214501Srpaulo} 123214501Srpaulo 124214501Srpaulo 125214501Srpaulostatic void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap) 126214501Srpaulo{ 127214501Srpaulo struct ap_info *s; 128214501Srpaulo 129214501Srpaulo s = iface->ap_hash[STA_HASH(ap->addr)]; 130214501Srpaulo if (s == NULL) return; 131214501Srpaulo if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) { 132214501Srpaulo iface->ap_hash[STA_HASH(ap->addr)] = s->hnext; 133214501Srpaulo return; 134214501Srpaulo } 135214501Srpaulo 136214501Srpaulo while (s->hnext != NULL && 137214501Srpaulo os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0) 138214501Srpaulo s = s->hnext; 139214501Srpaulo if (s->hnext != NULL) 140214501Srpaulo s->hnext = s->hnext->hnext; 141214501Srpaulo else 142214501Srpaulo printf("AP: could not remove AP " MACSTR " from hash table\n", 143214501Srpaulo MAC2STR(ap->addr)); 144214501Srpaulo} 145214501Srpaulo 146214501Srpaulo 147214501Srpaulostatic void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap) 148214501Srpaulo{ 149214501Srpaulo ap_ap_hash_del(iface, ap); 150214501Srpaulo ap_ap_list_del(iface, ap); 151214501Srpaulo ap_ap_iter_list_del(iface, ap); 152214501Srpaulo 153214501Srpaulo iface->num_ap--; 154214501Srpaulo os_free(ap); 155214501Srpaulo} 156214501Srpaulo 157214501Srpaulo 158214501Srpaulostatic void hostapd_free_aps(struct hostapd_iface *iface) 159214501Srpaulo{ 160214501Srpaulo struct ap_info *ap, *prev; 161214501Srpaulo 162214501Srpaulo ap = iface->ap_list; 163214501Srpaulo 164214501Srpaulo while (ap) { 165214501Srpaulo prev = ap; 166214501Srpaulo ap = ap->next; 167214501Srpaulo ap_free_ap(iface, prev); 168214501Srpaulo } 169214501Srpaulo 170214501Srpaulo iface->ap_list = NULL; 171214501Srpaulo} 172214501Srpaulo 173214501Srpaulo 174214501Srpauloint ap_ap_for_each(struct hostapd_iface *iface, 175214501Srpaulo int (*func)(struct ap_info *s, void *data), void *data) 176214501Srpaulo{ 177214501Srpaulo struct ap_info *s; 178214501Srpaulo int ret = 0; 179214501Srpaulo 180214501Srpaulo s = iface->ap_list; 181214501Srpaulo 182214501Srpaulo while (s) { 183214501Srpaulo ret = func(s, data); 184214501Srpaulo if (ret) 185214501Srpaulo break; 186214501Srpaulo s = s->next; 187214501Srpaulo } 188214501Srpaulo 189214501Srpaulo return ret; 190214501Srpaulo} 191214501Srpaulo 192214501Srpaulo 193214501Srpaulostatic struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr) 194214501Srpaulo{ 195214501Srpaulo struct ap_info *ap; 196214501Srpaulo 197214501Srpaulo ap = os_zalloc(sizeof(struct ap_info)); 198214501Srpaulo if (ap == NULL) 199214501Srpaulo return NULL; 200214501Srpaulo 201214501Srpaulo /* initialize AP info data */ 202214501Srpaulo os_memcpy(ap->addr, addr, ETH_ALEN); 203214501Srpaulo ap_ap_list_add(iface, ap); 204214501Srpaulo iface->num_ap++; 205214501Srpaulo ap_ap_hash_add(iface, ap); 206214501Srpaulo ap_ap_iter_list_add(iface, ap); 207214501Srpaulo 208214501Srpaulo if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) { 209214501Srpaulo wpa_printf(MSG_DEBUG, "Removing the least recently used AP " 210214501Srpaulo MACSTR " from AP table", MAC2STR(ap->prev->addr)); 211214501Srpaulo ap_free_ap(iface, ap->prev); 212214501Srpaulo } 213214501Srpaulo 214214501Srpaulo return ap; 215214501Srpaulo} 216214501Srpaulo 217214501Srpaulo 218214501Srpaulovoid ap_list_process_beacon(struct hostapd_iface *iface, 219214501Srpaulo const struct ieee80211_mgmt *mgmt, 220214501Srpaulo struct ieee802_11_elems *elems, 221214501Srpaulo struct hostapd_frame_info *fi) 222214501Srpaulo{ 223214501Srpaulo struct ap_info *ap; 224252726Srpaulo struct os_time now; 225214501Srpaulo int new_ap = 0; 226214501Srpaulo size_t len; 227214501Srpaulo int set_beacon = 0; 228214501Srpaulo 229214501Srpaulo if (iface->conf->ap_table_max_size < 1) 230214501Srpaulo return; 231214501Srpaulo 232214501Srpaulo ap = ap_get_ap(iface, mgmt->bssid); 233214501Srpaulo if (!ap) { 234214501Srpaulo ap = ap_ap_add(iface, mgmt->bssid); 235214501Srpaulo if (!ap) { 236214501Srpaulo printf("Failed to allocate AP information entry\n"); 237214501Srpaulo return; 238214501Srpaulo } 239214501Srpaulo new_ap = 1; 240214501Srpaulo } 241214501Srpaulo 242214501Srpaulo ap->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int); 243214501Srpaulo ap->capability = le_to_host16(mgmt->u.beacon.capab_info); 244214501Srpaulo 245214501Srpaulo if (elems->ssid) { 246214501Srpaulo len = elems->ssid_len; 247214501Srpaulo if (len >= sizeof(ap->ssid)) 248214501Srpaulo len = sizeof(ap->ssid) - 1; 249214501Srpaulo os_memcpy(ap->ssid, elems->ssid, len); 250214501Srpaulo ap->ssid[len] = '\0'; 251214501Srpaulo ap->ssid_len = len; 252214501Srpaulo } 253214501Srpaulo 254252726Srpaulo merge_byte_arrays(ap->supported_rates, WLAN_SUPP_RATES_MAX, 255252726Srpaulo elems->supp_rates, elems->supp_rates_len, 256252726Srpaulo elems->ext_supp_rates, elems->ext_supp_rates_len); 257214501Srpaulo 258214501Srpaulo ap->wpa = elems->wpa_ie != NULL; 259214501Srpaulo 260214501Srpaulo if (elems->erp_info && elems->erp_info_len == 1) 261214501Srpaulo ap->erp = elems->erp_info[0]; 262214501Srpaulo else 263214501Srpaulo ap->erp = -1; 264214501Srpaulo 265214501Srpaulo if (elems->ds_params && elems->ds_params_len == 1) 266214501Srpaulo ap->channel = elems->ds_params[0]; 267214501Srpaulo else if (fi) 268214501Srpaulo ap->channel = fi->channel; 269214501Srpaulo 270214501Srpaulo if (elems->ht_capabilities) 271214501Srpaulo ap->ht_support = 1; 272214501Srpaulo else 273214501Srpaulo ap->ht_support = 0; 274214501Srpaulo 275214501Srpaulo ap->num_beacons++; 276252726Srpaulo os_get_time(&now); 277252726Srpaulo ap->last_beacon = now.sec; 278252726Srpaulo if (fi) 279214501Srpaulo ap->datarate = fi->datarate; 280214501Srpaulo 281214501Srpaulo if (!new_ap && ap != iface->ap_list) { 282214501Srpaulo /* move AP entry into the beginning of the list so that the 283214501Srpaulo * oldest entry is always in the end of the list */ 284214501Srpaulo ap_ap_list_del(iface, ap); 285214501Srpaulo ap_ap_list_add(iface, ap); 286214501Srpaulo } 287214501Srpaulo 288214501Srpaulo if (!iface->olbc && 289214501Srpaulo ap_list_beacon_olbc(iface, ap)) { 290214501Srpaulo iface->olbc = 1; 291214501Srpaulo wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR " - enable " 292214501Srpaulo "protection", MAC2STR(ap->addr)); 293214501Srpaulo set_beacon++; 294214501Srpaulo } 295214501Srpaulo 296214501Srpaulo#ifdef CONFIG_IEEE80211N 297214501Srpaulo if (!iface->olbc_ht && !ap->ht_support) { 298214501Srpaulo iface->olbc_ht = 1; 299214501Srpaulo hostapd_ht_operation_update(iface); 300214501Srpaulo wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR 301214501Srpaulo " - enable protection", MAC2STR(ap->addr)); 302214501Srpaulo set_beacon++; 303214501Srpaulo } 304214501Srpaulo#endif /* CONFIG_IEEE80211N */ 305214501Srpaulo 306214501Srpaulo if (set_beacon) 307252726Srpaulo ieee802_11_update_beacons(iface); 308214501Srpaulo} 309214501Srpaulo 310214501Srpaulo 311214501Srpaulostatic void ap_list_timer(void *eloop_ctx, void *timeout_ctx) 312214501Srpaulo{ 313214501Srpaulo struct hostapd_iface *iface = eloop_ctx; 314252726Srpaulo struct os_time now; 315214501Srpaulo struct ap_info *ap; 316214501Srpaulo int set_beacon = 0; 317214501Srpaulo 318214501Srpaulo eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); 319214501Srpaulo 320214501Srpaulo if (!iface->ap_list) 321214501Srpaulo return; 322214501Srpaulo 323252726Srpaulo os_get_time(&now); 324214501Srpaulo 325214501Srpaulo while (iface->ap_list) { 326214501Srpaulo ap = iface->ap_list->prev; 327214501Srpaulo if (ap->last_beacon + iface->conf->ap_table_expiration_time >= 328252726Srpaulo now.sec) 329214501Srpaulo break; 330214501Srpaulo 331214501Srpaulo ap_free_ap(iface, ap); 332214501Srpaulo } 333214501Srpaulo 334214501Srpaulo if (iface->olbc || iface->olbc_ht) { 335214501Srpaulo int olbc = 0; 336214501Srpaulo int olbc_ht = 0; 337214501Srpaulo 338214501Srpaulo ap = iface->ap_list; 339214501Srpaulo while (ap && (olbc == 0 || olbc_ht == 0)) { 340214501Srpaulo if (ap_list_beacon_olbc(iface, ap)) 341214501Srpaulo olbc = 1; 342214501Srpaulo if (!ap->ht_support) 343214501Srpaulo olbc_ht = 1; 344214501Srpaulo ap = ap->next; 345214501Srpaulo } 346214501Srpaulo if (!olbc && iface->olbc) { 347214501Srpaulo wpa_printf(MSG_DEBUG, "OLBC not detected anymore"); 348214501Srpaulo iface->olbc = 0; 349214501Srpaulo set_beacon++; 350214501Srpaulo } 351214501Srpaulo#ifdef CONFIG_IEEE80211N 352214501Srpaulo if (!olbc_ht && iface->olbc_ht) { 353214501Srpaulo wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore"); 354214501Srpaulo iface->olbc_ht = 0; 355214501Srpaulo hostapd_ht_operation_update(iface); 356214501Srpaulo set_beacon++; 357214501Srpaulo } 358214501Srpaulo#endif /* CONFIG_IEEE80211N */ 359214501Srpaulo } 360214501Srpaulo 361214501Srpaulo if (set_beacon) 362252726Srpaulo ieee802_11_update_beacons(iface); 363214501Srpaulo} 364214501Srpaulo 365214501Srpaulo 366214501Srpauloint ap_list_init(struct hostapd_iface *iface) 367214501Srpaulo{ 368214501Srpaulo eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); 369214501Srpaulo return 0; 370214501Srpaulo} 371214501Srpaulo 372214501Srpaulo 373214501Srpaulovoid ap_list_deinit(struct hostapd_iface *iface) 374214501Srpaulo{ 375214501Srpaulo eloop_cancel_timeout(ap_list_timer, iface, NULL); 376214501Srpaulo hostapd_free_aps(iface); 377214501Srpaulo} 378