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