1 /* 2 * hostapd / AP table 3 * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> 4 * Copyright (c) 2003-2004, Instant802 Networks, Inc. 5 * Copyright (c) 2006, Devicescape Software, Inc. 6 * 7 * This software may be distributed under the terms of the BSD license. 8 * See README for more details. 9 */ 10 11 #include "utils/includes.h" 12 13 #include "utils/common.h" 14 #include "utils/eloop.h" 15 #include "common/ieee802_11_defs.h" 16 #include "common/ieee802_11_common.h" 17 #include "hostapd.h" 18 #include "ap_config.h" 19 #include "ieee802_11.h" 20 #include "sta_info.h" 21 #include "beacon.h" 22 #include "ap_list.h" 23 24 25 /* AP list is a double linked list with head->prev pointing to the end of the 26 * list and tail->next = NULL. Entries are moved to the head of the list 27 * whenever a beacon has been received from the AP in question. The tail entry 28 * in this link will thus be the least recently used entry. */ 29 30 31 static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap) 32 { 33 int i; 34 35 if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G || 36 iface->conf->channel != ap->channel) 37 return 0; 38 39 if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT)) 40 return 1; 41 42 for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) { 43 int rate = (ap->supported_rates[i] & 0x7f) * 5; 44 if (rate == 60 || rate == 90 || rate > 110) 45 return 0; 46 } 47 48 return 1; 49 } 50 51 52 static struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap) 53 { 54 struct ap_info *s; 55 56 s = iface->ap_hash[STA_HASH(ap)]; 57 while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0) 58 s = s->hnext; 59 return s; 60 } 61 62 63 static void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap) 64 { 65 if (iface->ap_list) { 66 ap->prev = iface->ap_list->prev; 67 iface->ap_list->prev = ap; 68 } else 69 ap->prev = ap; 70 ap->next = iface->ap_list; 71 iface->ap_list = ap; 72 } 73 74 75 static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap) 76 { 77 if (iface->ap_list == ap) 78 iface->ap_list = ap->next; 79 else 80 ap->prev->next = ap->next; 81 82 if (ap->next) 83 ap->next->prev = ap->prev; 84 else if (iface->ap_list) 85 iface->ap_list->prev = ap->prev; 86 } 87 88 89 static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap) 90 { 91 ap->hnext = iface->ap_hash[STA_HASH(ap->addr)]; 92 iface->ap_hash[STA_HASH(ap->addr)] = ap; 93 } 94 95 96 static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap) 97 { 98 struct ap_info *s; 99 100 s = iface->ap_hash[STA_HASH(ap->addr)]; 101 if (s == NULL) return; 102 if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) { 103 iface->ap_hash[STA_HASH(ap->addr)] = s->hnext; 104 return; 105 } 106 107 while (s->hnext != NULL && 108 os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0) 109 s = s->hnext; 110 if (s->hnext != NULL) 111 s->hnext = s->hnext->hnext; 112 else 113 printf("AP: could not remove AP " MACSTR " from hash table\n", 114 MAC2STR(ap->addr)); 115 } 116 117 118 static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap) 119 { 120 ap_ap_hash_del(iface, ap); 121 ap_ap_list_del(iface, ap); 122 123 iface->num_ap--; 124 os_free(ap); 125 } 126 127 128 static void hostapd_free_aps(struct hostapd_iface *iface) 129 { 130 struct ap_info *ap, *prev; 131 132 ap = iface->ap_list; 133 134 while (ap) { 135 prev = ap; 136 ap = ap->next; 137 ap_free_ap(iface, prev); 138 } 139 140 iface->ap_list = NULL; 141 } 142 143 144 static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr) 145 { 146 struct ap_info *ap; 147 148 ap = os_zalloc(sizeof(struct ap_info)); 149 if (ap == NULL) 150 return NULL; 151 152 /* initialize AP info data */ 153 os_memcpy(ap->addr, addr, ETH_ALEN); 154 ap_ap_list_add(iface, ap); 155 iface->num_ap++; 156 ap_ap_hash_add(iface, ap); 157 158 if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) { 159 wpa_printf(MSG_DEBUG, "Removing the least recently used AP " 160 MACSTR " from AP table", MAC2STR(ap->prev->addr)); 161 ap_free_ap(iface, ap->prev); 162 } 163 164 return ap; 165 } 166 167 168 void ap_list_process_beacon(struct hostapd_iface *iface, 169 const struct ieee80211_mgmt *mgmt, 170 struct ieee802_11_elems *elems, 171 struct hostapd_frame_info *fi) 172 { 173 struct ap_info *ap; 174 int new_ap = 0; 175 int set_beacon = 0; 176 177 if (iface->conf->ap_table_max_size < 1) 178 return; 179 180 ap = ap_get_ap(iface, mgmt->bssid); 181 if (!ap) { 182 ap = ap_ap_add(iface, mgmt->bssid); 183 if (!ap) { 184 printf("Failed to allocate AP information entry\n"); 185 return; 186 } 187 new_ap = 1; 188 } 189 190 merge_byte_arrays(ap->supported_rates, WLAN_SUPP_RATES_MAX, 191 elems->supp_rates, elems->supp_rates_len, 192 elems->ext_supp_rates, elems->ext_supp_rates_len); 193 194 if (elems->erp_info && elems->erp_info_len == 1) 195 ap->erp = elems->erp_info[0]; 196 else 197 ap->erp = -1; 198 199 if (elems->ds_params && elems->ds_params_len == 1) 200 ap->channel = elems->ds_params[0]; 201 else if (elems->ht_operation && elems->ht_operation_len >= 1) 202 ap->channel = elems->ht_operation[0]; 203 else if (fi) 204 ap->channel = fi->channel; 205 206 if (elems->ht_capabilities) 207 ap->ht_support = 1; 208 else 209 ap->ht_support = 0; 210 211 os_get_reltime(&ap->last_beacon); 212 213 if (!new_ap && ap != iface->ap_list) { 214 /* move AP entry into the beginning of the list so that the 215 * oldest entry is always in the end of the list */ 216 ap_ap_list_del(iface, ap); 217 ap_ap_list_add(iface, ap); 218 } 219 220 if (!iface->olbc && 221 ap_list_beacon_olbc(iface, ap)) { 222 iface->olbc = 1; 223 wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR 224 " (channel %d) - enable protection", 225 MAC2STR(ap->addr), ap->channel); 226 set_beacon++; 227 } 228 229 #ifdef CONFIG_IEEE80211N 230 if (!iface->olbc_ht && !ap->ht_support && 231 (ap->channel == 0 || 232 ap->channel == iface->conf->channel || 233 ap->channel == iface->conf->channel + 234 iface->conf->secondary_channel * 4)) { 235 iface->olbc_ht = 1; 236 hostapd_ht_operation_update(iface); 237 wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR 238 " (channel %d) - enable protection", 239 MAC2STR(ap->addr), ap->channel); 240 set_beacon++; 241 } 242 #endif /* CONFIG_IEEE80211N */ 243 244 if (set_beacon) 245 ieee802_11_update_beacons(iface); 246 } 247 248 249 static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) 250 { 251 struct hostapd_iface *iface = eloop_ctx; 252 struct os_reltime now; 253 struct ap_info *ap; 254 int set_beacon = 0; 255 256 eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); 257 258 if (!iface->ap_list) 259 return; 260 261 os_get_reltime(&now); 262 263 while (iface->ap_list) { 264 ap = iface->ap_list->prev; 265 if (!os_reltime_expired(&now, &ap->last_beacon, 266 iface->conf->ap_table_expiration_time)) 267 break; 268 269 ap_free_ap(iface, ap); 270 } 271 272 if (iface->olbc || iface->olbc_ht) { 273 int olbc = 0; 274 int olbc_ht = 0; 275 276 ap = iface->ap_list; 277 while (ap && (olbc == 0 || olbc_ht == 0)) { 278 if (ap_list_beacon_olbc(iface, ap)) 279 olbc = 1; 280 if (!ap->ht_support) 281 olbc_ht = 1; 282 ap = ap->next; 283 } 284 if (!olbc && iface->olbc) { 285 wpa_printf(MSG_DEBUG, "OLBC not detected anymore"); 286 iface->olbc = 0; 287 set_beacon++; 288 } 289 #ifdef CONFIG_IEEE80211N 290 if (!olbc_ht && iface->olbc_ht) { 291 wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore"); 292 iface->olbc_ht = 0; 293 hostapd_ht_operation_update(iface); 294 set_beacon++; 295 } 296 #endif /* CONFIG_IEEE80211N */ 297 } 298 299 if (set_beacon) 300 ieee802_11_update_beacons(iface); 301 } 302 303 304 int ap_list_init(struct hostapd_iface *iface) 305 { 306 eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); 307 return 0; 308 } 309 310 311 void ap_list_deinit(struct hostapd_iface *iface) 312 { 313 eloop_cancel_timeout(ap_list_timer, iface, NULL); 314 hostapd_free_aps(iface); 315 } 316