Home | History | Annotate | Download | only in ap
      1 /*
      2  * hostapd / AP table
      3  * Copyright (c) 2002-2009, Jouni Malinen <j (at) w1.fi>
      4  * Copyright (c) 2003-2004, Instant802 Networks, Inc.
      5  * Copyright (c) 2006, Devicescape Software, Inc.
      6  *
      7  * This program is free software; you can redistribute it and/or modify
      8  * it under the terms of the GNU General Public License version 2 as
      9  * published by the Free Software Foundation.
     10  *
     11  * Alternatively, this software may be distributed under the terms of BSD
     12  * license.
     13  *
     14  * See README and COPYING for more details.
     15  */
     16 
     17 #include "utils/includes.h"
     18 
     19 #include "utils/common.h"
     20 #include "utils/eloop.h"
     21 #include "common/ieee802_11_defs.h"
     22 #include "common/ieee802_11_common.h"
     23 #include "drivers/driver.h"
     24 #include "hostapd.h"
     25 #include "ap_config.h"
     26 #include "ieee802_11.h"
     27 #include "sta_info.h"
     28 #include "beacon.h"
     29 #include "ap_list.h"
     30 
     31 
     32 /* AP list is a double linked list with head->prev pointing to the end of the
     33  * list and tail->next = NULL. Entries are moved to the head of the list
     34  * whenever a beacon has been received from the AP in question. The tail entry
     35  * in this link will thus be the least recently used entry. */
     36 
     37 
     38 static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap)
     39 {
     40 	int i;
     41 
     42 	if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
     43 	    iface->conf->channel != ap->channel)
     44 		return 0;
     45 
     46 	if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT))
     47 		return 1;
     48 
     49 	for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) {
     50 		int rate = (ap->supported_rates[i] & 0x7f) * 5;
     51 		if (rate == 60 || rate == 90 || rate > 110)
     52 			return 0;
     53 	}
     54 
     55 	return 1;
     56 }
     57 
     58 
     59 struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap)
     60 {
     61 	struct ap_info *s;
     62 
     63 	s = iface->ap_hash[STA_HASH(ap)];
     64 	while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0)
     65 		s = s->hnext;
     66 	return s;
     67 }
     68 
     69 
     70 static void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap)
     71 {
     72 	if (iface->ap_list) {
     73 		ap->prev = iface->ap_list->prev;
     74 		iface->ap_list->prev = ap;
     75 	} else
     76 		ap->prev = ap;
     77 	ap->next = iface->ap_list;
     78 	iface->ap_list = ap;
     79 }
     80 
     81 
     82 static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap)
     83 {
     84 	if (iface->ap_list == ap)
     85 		iface->ap_list = ap->next;
     86 	else
     87 		ap->prev->next = ap->next;
     88 
     89 	if (ap->next)
     90 		ap->next->prev = ap->prev;
     91 	else if (iface->ap_list)
     92 		iface->ap_list->prev = ap->prev;
     93 }
     94 
     95 
     96 static void ap_ap_iter_list_add(struct hostapd_iface *iface,
     97 				struct ap_info *ap)
     98 {
     99 	if (iface->ap_iter_list) {
    100 		ap->iter_prev = iface->ap_iter_list->iter_prev;
    101 		iface->ap_iter_list->iter_prev = ap;
    102 	} else
    103 		ap->iter_prev = ap;
    104 	ap->iter_next = iface->ap_iter_list;
    105 	iface->ap_iter_list = ap;
    106 }
    107 
    108 
    109 static void ap_ap_iter_list_del(struct hostapd_iface *iface,
    110 				struct ap_info *ap)
    111 {
    112 	if (iface->ap_iter_list == ap)
    113 		iface->ap_iter_list = ap->iter_next;
    114 	else
    115 		ap->iter_prev->iter_next = ap->iter_next;
    116 
    117 	if (ap->iter_next)
    118 		ap->iter_next->iter_prev = ap->iter_prev;
    119 	else if (iface->ap_iter_list)
    120 		iface->ap_iter_list->iter_prev = ap->iter_prev;
    121 }
    122 
    123 
    124 static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap)
    125 {
    126 	ap->hnext = iface->ap_hash[STA_HASH(ap->addr)];
    127 	iface->ap_hash[STA_HASH(ap->addr)] = ap;
    128 }
    129 
    130 
    131 static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap)
    132 {
    133 	struct ap_info *s;
    134 
    135 	s = iface->ap_hash[STA_HASH(ap->addr)];
    136 	if (s == NULL) return;
    137 	if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) {
    138 		iface->ap_hash[STA_HASH(ap->addr)] = s->hnext;
    139 		return;
    140 	}
    141 
    142 	while (s->hnext != NULL &&
    143 	       os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0)
    144 		s = s->hnext;
    145 	if (s->hnext != NULL)
    146 		s->hnext = s->hnext->hnext;
    147 	else
    148 		printf("AP: could not remove AP " MACSTR " from hash table\n",
    149 		       MAC2STR(ap->addr));
    150 }
    151 
    152 
    153 static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap)
    154 {
    155 	ap_ap_hash_del(iface, ap);
    156 	ap_ap_list_del(iface, ap);
    157 	ap_ap_iter_list_del(iface, ap);
    158 
    159 	iface->num_ap--;
    160 	os_free(ap);
    161 }
    162 
    163 
    164 static void hostapd_free_aps(struct hostapd_iface *iface)
    165 {
    166 	struct ap_info *ap, *prev;
    167 
    168 	ap = iface->ap_list;
    169 
    170 	while (ap) {
    171 		prev = ap;
    172 		ap = ap->next;
    173 		ap_free_ap(iface, prev);
    174 	}
    175 
    176 	iface->ap_list = NULL;
    177 }
    178 
    179 
    180 int ap_ap_for_each(struct hostapd_iface *iface,
    181 		   int (*func)(struct ap_info *s, void *data), void *data)
    182 {
    183 	struct ap_info *s;
    184 	int ret = 0;
    185 
    186 	s = iface->ap_list;
    187 
    188 	while (s) {
    189 		ret = func(s, data);
    190 		if (ret)
    191 			break;
    192 		s = s->next;
    193 	}
    194 
    195 	return ret;
    196 }
    197 
    198 
    199 static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr)
    200 {
    201 	struct ap_info *ap;
    202 
    203 	ap = os_zalloc(sizeof(struct ap_info));
    204 	if (ap == NULL)
    205 		return NULL;
    206 
    207 	/* initialize AP info data */
    208 	os_memcpy(ap->addr, addr, ETH_ALEN);
    209 	ap_ap_list_add(iface, ap);
    210 	iface->num_ap++;
    211 	ap_ap_hash_add(iface, ap);
    212 	ap_ap_iter_list_add(iface, ap);
    213 
    214 	if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) {
    215 		wpa_printf(MSG_DEBUG, "Removing the least recently used AP "
    216 			   MACSTR " from AP table", MAC2STR(ap->prev->addr));
    217 		ap_free_ap(iface, ap->prev);
    218 	}
    219 
    220 	return ap;
    221 }
    222 
    223 
    224 void ap_list_process_beacon(struct hostapd_iface *iface,
    225 			    const struct ieee80211_mgmt *mgmt,
    226 			    struct ieee802_11_elems *elems,
    227 			    struct hostapd_frame_info *fi)
    228 {
    229 	struct ap_info *ap;
    230 	struct os_time now;
    231 	int new_ap = 0;
    232 	size_t len;
    233 	int set_beacon = 0;
    234 
    235 	if (iface->conf->ap_table_max_size < 1)
    236 		return;
    237 
    238 	ap = ap_get_ap(iface, mgmt->bssid);
    239 	if (!ap) {
    240 		ap = ap_ap_add(iface, mgmt->bssid);
    241 		if (!ap) {
    242 			printf("Failed to allocate AP information entry\n");
    243 			return;
    244 		}
    245 		new_ap = 1;
    246 	}
    247 
    248 	ap->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int);
    249 	ap->capability = le_to_host16(mgmt->u.beacon.capab_info);
    250 
    251 	if (elems->ssid) {
    252 		len = elems->ssid_len;
    253 		if (len >= sizeof(ap->ssid))
    254 			len = sizeof(ap->ssid) - 1;
    255 		os_memcpy(ap->ssid, elems->ssid, len);
    256 		ap->ssid[len] = '\0';
    257 		ap->ssid_len = len;
    258 	}
    259 
    260 	os_memset(ap->supported_rates, 0, WLAN_SUPP_RATES_MAX);
    261 	len = 0;
    262 	if (elems->supp_rates) {
    263 		len = elems->supp_rates_len;
    264 		if (len > WLAN_SUPP_RATES_MAX)
    265 			len = WLAN_SUPP_RATES_MAX;
    266 		os_memcpy(ap->supported_rates, elems->supp_rates, len);
    267 	}
    268 	if (elems->ext_supp_rates) {
    269 		int len2;
    270 		if (len + elems->ext_supp_rates_len > WLAN_SUPP_RATES_MAX)
    271 			len2 = WLAN_SUPP_RATES_MAX - len;
    272 		else
    273 			len2 = elems->ext_supp_rates_len;
    274 		os_memcpy(ap->supported_rates + len, elems->ext_supp_rates,
    275 			  len2);
    276 	}
    277 
    278 	ap->wpa = elems->wpa_ie != NULL;
    279 
    280 	if (elems->erp_info && elems->erp_info_len == 1)
    281 		ap->erp = elems->erp_info[0];
    282 	else
    283 		ap->erp = -1;
    284 
    285 	if (elems->ds_params && elems->ds_params_len == 1)
    286 		ap->channel = elems->ds_params[0];
    287 	else if (fi)
    288 		ap->channel = fi->channel;
    289 
    290 	if (elems->ht_capabilities)
    291 		ap->ht_support = 1;
    292 	else
    293 		ap->ht_support = 0;
    294 
    295 	ap->num_beacons++;
    296 	os_get_time(&now);
    297 	ap->last_beacon = now.sec;
    298 	if (fi) {
    299 		ap->ssi_signal = fi->ssi_signal;
    300 		ap->datarate = fi->datarate;
    301 	}
    302 
    303 	if (!new_ap && ap != iface->ap_list) {
    304 		/* move AP entry into the beginning of the list so that the
    305 		 * oldest entry is always in the end of the list */
    306 		ap_ap_list_del(iface, ap);
    307 		ap_ap_list_add(iface, ap);
    308 	}
    309 
    310 	if (!iface->olbc &&
    311 	    ap_list_beacon_olbc(iface, ap)) {
    312 		iface->olbc = 1;
    313 		wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR " - enable "
    314 			   "protection", MAC2STR(ap->addr));
    315 		set_beacon++;
    316 	}
    317 
    318 #ifdef CONFIG_IEEE80211N
    319 	if (!iface->olbc_ht && !ap->ht_support) {
    320 		iface->olbc_ht = 1;
    321 		hostapd_ht_operation_update(iface);
    322 		wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR
    323 			   " - enable protection", MAC2STR(ap->addr));
    324 		set_beacon++;
    325 	}
    326 #endif /* CONFIG_IEEE80211N */
    327 
    328 	if (set_beacon)
    329 		ieee802_11_set_beacons(iface);
    330 }
    331 
    332 
    333 static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
    334 {
    335 	struct hostapd_iface *iface = eloop_ctx;
    336 	struct os_time now;
    337 	struct ap_info *ap;
    338 	int set_beacon = 0;
    339 
    340 	eloop_register_timeout(10, 0, ap_list_timer, iface, NULL);
    341 
    342 	if (!iface->ap_list)
    343 		return;
    344 
    345 	os_get_time(&now);
    346 
    347 	while (iface->ap_list) {
    348 		ap = iface->ap_list->prev;
    349 		if (ap->last_beacon + iface->conf->ap_table_expiration_time >=
    350 		    now.sec)
    351 			break;
    352 
    353 		ap_free_ap(iface, ap);
    354 	}
    355 
    356 	if (iface->olbc || iface->olbc_ht) {
    357 		int olbc = 0;
    358 		int olbc_ht = 0;
    359 
    360 		ap = iface->ap_list;
    361 		while (ap && (olbc == 0 || olbc_ht == 0)) {
    362 			if (ap_list_beacon_olbc(iface, ap))
    363 				olbc = 1;
    364 			if (!ap->ht_support)
    365 				olbc_ht = 1;
    366 			ap = ap->next;
    367 		}
    368 		if (!olbc && iface->olbc) {
    369 			wpa_printf(MSG_DEBUG, "OLBC not detected anymore");
    370 			iface->olbc = 0;
    371 			set_beacon++;
    372 		}
    373 #ifdef CONFIG_IEEE80211N
    374 		if (!olbc_ht && iface->olbc_ht) {
    375 			wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore");
    376 			iface->olbc_ht = 0;
    377 			hostapd_ht_operation_update(iface);
    378 			set_beacon++;
    379 		}
    380 #endif /* CONFIG_IEEE80211N */
    381 	}
    382 
    383 	if (set_beacon)
    384 		ieee802_11_set_beacons(iface);
    385 }
    386 
    387 
    388 int ap_list_init(struct hostapd_iface *iface)
    389 {
    390 	eloop_register_timeout(10, 0, ap_list_timer, iface, NULL);
    391 	return 0;
    392 }
    393 
    394 
    395 void ap_list_deinit(struct hostapd_iface *iface)
    396 {
    397 	eloop_cancel_timeout(ap_list_timer, iface, NULL);
    398 	hostapd_free_aps(iface);
    399 }
    400