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