Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / src / ap / ap_list.c
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 == NULL ||
36             iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
37             iface->conf->channel != ap->channel)
38                 return 0;
39
40         if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT))
41                 return 1;
42
43         for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) {
44                 int rate = (ap->supported_rates[i] & 0x7f) * 5;
45                 if (rate == 60 || rate == 90 || rate > 110)
46                         return 0;
47         }
48
49         return 1;
50 }
51
52
53 static struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap)
54 {
55         struct ap_info *s;
56
57         s = iface->ap_hash[STA_HASH(ap)];
58         while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0)
59                 s = s->hnext;
60         return s;
61 }
62
63
64 static void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap)
65 {
66         if (iface->ap_list) {
67                 ap->prev = iface->ap_list->prev;
68                 iface->ap_list->prev = ap;
69         } else
70                 ap->prev = ap;
71         ap->next = iface->ap_list;
72         iface->ap_list = ap;
73 }
74
75
76 static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap)
77 {
78         if (iface->ap_list == ap)
79                 iface->ap_list = ap->next;
80         else
81                 ap->prev->next = ap->next;
82
83         if (ap->next)
84                 ap->next->prev = ap->prev;
85         else if (iface->ap_list)
86                 iface->ap_list->prev = ap->prev;
87 }
88
89
90 static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap)
91 {
92         ap->hnext = iface->ap_hash[STA_HASH(ap->addr)];
93         iface->ap_hash[STA_HASH(ap->addr)] = ap;
94 }
95
96
97 static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap)
98 {
99         struct ap_info *s;
100
101         s = iface->ap_hash[STA_HASH(ap->addr)];
102         if (s == NULL) return;
103         if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) {
104                 iface->ap_hash[STA_HASH(ap->addr)] = s->hnext;
105                 return;
106         }
107
108         while (s->hnext != NULL &&
109                os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0)
110                 s = s->hnext;
111         if (s->hnext != NULL)
112                 s->hnext = s->hnext->hnext;
113         else
114                 wpa_printf(MSG_INFO, "AP: could not remove AP " MACSTR
115                            " from hash table",  MAC2STR(ap->addr));
116 }
117
118
119 static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap)
120 {
121         ap_ap_hash_del(iface, ap);
122         ap_ap_list_del(iface, ap);
123
124         iface->num_ap--;
125         os_free(ap);
126 }
127
128
129 static void hostapd_free_aps(struct hostapd_iface *iface)
130 {
131         struct ap_info *ap, *prev;
132
133         ap = iface->ap_list;
134
135         while (ap) {
136                 prev = ap;
137                 ap = ap->next;
138                 ap_free_ap(iface, prev);
139         }
140
141         iface->ap_list = NULL;
142 }
143
144
145 static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr)
146 {
147         struct ap_info *ap;
148
149         ap = os_zalloc(sizeof(struct ap_info));
150         if (ap == NULL)
151                 return NULL;
152
153         /* initialize AP info data */
154         os_memcpy(ap->addr, addr, ETH_ALEN);
155         ap_ap_list_add(iface, ap);
156         iface->num_ap++;
157         ap_ap_hash_add(iface, ap);
158
159         if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) {
160                 wpa_printf(MSG_DEBUG, "Removing the least recently used AP "
161                            MACSTR " from AP table", MAC2STR(ap->prev->addr));
162                 ap_free_ap(iface, ap->prev);
163         }
164
165         return ap;
166 }
167
168
169 void ap_list_process_beacon(struct hostapd_iface *iface,
170                             const struct ieee80211_mgmt *mgmt,
171                             struct ieee802_11_elems *elems,
172                             struct hostapd_frame_info *fi)
173 {
174         struct ap_info *ap;
175         int new_ap = 0;
176         int set_beacon = 0;
177
178         if (iface->conf->ap_table_max_size < 1)
179                 return;
180
181         ap = ap_get_ap(iface, mgmt->bssid);
182         if (!ap) {
183                 ap = ap_ap_add(iface, mgmt->bssid);
184                 if (!ap) {
185                         wpa_printf(MSG_INFO,
186                                    "Failed to allocate AP information entry");
187                         return;
188                 }
189                 new_ap = 1;
190         }
191
192         merge_byte_arrays(ap->supported_rates, WLAN_SUPP_RATES_MAX,
193                           elems->supp_rates, elems->supp_rates_len,
194                           elems->ext_supp_rates, elems->ext_supp_rates_len);
195
196         if (elems->erp_info)
197                 ap->erp = elems->erp_info[0];
198         else
199                 ap->erp = -1;
200
201         if (elems->ds_params)
202                 ap->channel = elems->ds_params[0];
203         else if (elems->ht_operation)
204                 ap->channel = elems->ht_operation[0];
205         else if (fi)
206                 ap->channel = fi->channel;
207
208         if (elems->ht_capabilities)
209                 ap->ht_support = 1;
210         else
211                 ap->ht_support = 0;
212
213         os_get_reltime(&ap->last_beacon);
214
215         if (!new_ap && ap != iface->ap_list) {
216                 /* move AP entry into the beginning of the list so that the
217                  * oldest entry is always in the end of the list */
218                 ap_ap_list_del(iface, ap);
219                 ap_ap_list_add(iface, ap);
220         }
221
222         if (!iface->olbc &&
223             ap_list_beacon_olbc(iface, ap)) {
224                 iface->olbc = 1;
225                 wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR
226                            " (channel %d) - enable protection",
227                            MAC2STR(ap->addr), ap->channel);
228                 set_beacon++;
229         }
230
231 #ifdef CONFIG_IEEE80211N
232         if (!iface->olbc_ht && !ap->ht_support &&
233             (ap->channel == 0 ||
234              ap->channel == iface->conf->channel ||
235              ap->channel == iface->conf->channel +
236              iface->conf->secondary_channel * 4)) {
237                 iface->olbc_ht = 1;
238                 hostapd_ht_operation_update(iface);
239                 wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR
240                            " (channel %d) - enable protection",
241                            MAC2STR(ap->addr), ap->channel);
242                 set_beacon++;
243         }
244 #endif /* CONFIG_IEEE80211N */
245
246         if (set_beacon)
247                 ieee802_11_update_beacons(iface);
248 }
249
250
251 void ap_list_timer(struct hostapd_iface *iface)
252 {
253         struct os_reltime now;
254         struct ap_info *ap;
255         int set_beacon = 0;
256
257         if (!iface->ap_list)
258                 return;
259
260         os_get_reltime(&now);
261
262         while (iface->ap_list) {
263                 ap = iface->ap_list->prev;
264                 if (!os_reltime_expired(&now, &ap->last_beacon,
265                                         iface->conf->ap_table_expiration_time))
266                         break;
267
268                 ap_free_ap(iface, ap);
269         }
270
271         if (iface->olbc || iface->olbc_ht) {
272                 int olbc = 0;
273                 int olbc_ht = 0;
274
275                 ap = iface->ap_list;
276                 while (ap && (olbc == 0 || olbc_ht == 0)) {
277                         if (ap_list_beacon_olbc(iface, ap))
278                                 olbc = 1;
279                         if (!ap->ht_support)
280                                 olbc_ht = 1;
281                         ap = ap->next;
282                 }
283                 if (!olbc && iface->olbc) {
284                         wpa_printf(MSG_DEBUG, "OLBC not detected anymore");
285                         iface->olbc = 0;
286                         set_beacon++;
287                 }
288 #ifdef CONFIG_IEEE80211N
289                 if (!olbc_ht && iface->olbc_ht) {
290                         wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore");
291                         iface->olbc_ht = 0;
292                         hostapd_ht_operation_update(iface);
293                         set_beacon++;
294                 }
295 #endif /* CONFIG_IEEE80211N */
296         }
297
298         if (set_beacon)
299                 ieee802_11_update_beacons(iface);
300 }
301
302
303 int ap_list_init(struct hostapd_iface *iface)
304 {
305         return 0;
306 }
307
308
309 void ap_list_deinit(struct hostapd_iface *iface)
310 {
311         hostapd_free_aps(iface);
312 }