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