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