udp: Merge udp_send and udp_output
[dragonfly.git] / contrib / hostapd / 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  * Copyright (c) 2007-2008, Intel Corporation
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  *
12  * Alternatively, this software may be distributed under the terms of BSD
13  * license.
14  *
15  * See README and COPYING for more details.
16  */
17
18 #include "includes.h"
19
20 #include "hostapd.h"
21 #include "ieee802_11.h"
22 #include "eloop.h"
23 #include "ap_list.h"
24 #include "hw_features.h"
25 #include "beacon.h"
26
27
28 struct ieee80211_frame_info {
29         u32 version;
30         u32 length;
31         u64 mactime;
32         u64 hosttime;
33         u32 phytype;
34         u32 channel;
35         u32 datarate;
36         u32 antenna;
37         u32 priority;
38         u32 ssi_type;
39         u32 ssi_signal;
40         u32 ssi_noise;
41         u32 preamble;
42         u32 encoding;
43
44         /* Note: this structure is otherwise identical to capture format used
45          * in linux-wlan-ng, but this additional field is used to provide meta
46          * data about the frame to hostapd. This was the easiest method for
47          * providing this information, but this might change in the future. */
48         u32 msg_type;
49 } __attribute__ ((packed));
50
51
52 enum ieee80211_phytype {
53         ieee80211_phytype_fhss_dot11_97  = 1,
54         ieee80211_phytype_dsss_dot11_97  = 2,
55         ieee80211_phytype_irbaseband     = 3,
56         ieee80211_phytype_dsss_dot11_b   = 4,
57         ieee80211_phytype_pbcc_dot11_b   = 5,
58         ieee80211_phytype_ofdm_dot11_g   = 6,
59         ieee80211_phytype_pbcc_dot11_g   = 7,
60         ieee80211_phytype_ofdm_dot11_a   = 8,
61         ieee80211_phytype_dsss_dot11_turbog = 255,
62         ieee80211_phytype_dsss_dot11_turbo = 256,
63 };
64
65
66 /* AP list is a double linked list with head->prev pointing to the end of the
67  * list and tail->next = NULL. Entries are moved to the head of the list
68  * whenever a beacon has been received from the AP in question. The tail entry
69  * in this link will thus be the least recently used entry. */
70
71
72 static void ap_list_new_ap(struct hostapd_iface *iface, struct ap_info *ap)
73 {
74         wpa_printf(MSG_DEBUG, "New AP detected: " MACSTR, MAC2STR(ap->addr));
75
76         /* TODO: could send a notification message to an external program that
77          * would then determine whether a rogue AP has been detected */
78 }
79
80
81 static void ap_list_expired_ap(struct hostapd_iface *iface, struct ap_info *ap)
82 {
83         wpa_printf(MSG_DEBUG, "AP info expired: " MACSTR, MAC2STR(ap->addr));
84
85         /* TODO: could send a notification message to an external program */
86 }
87
88
89 static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap)
90 {
91         int i;
92
93         if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
94             ap->phytype != ieee80211_phytype_pbcc_dot11_g ||
95             iface->conf->channel != ap->channel)
96                 return 0;
97
98         if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT))
99                 return 1;
100
101         for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) {
102                 int rate = (ap->supported_rates[i] & 0x7f) * 5;
103                 if (rate == 60 || rate == 90 || rate > 110)
104                         return 0;
105         }
106
107         return 1;
108 }
109
110
111 #ifdef CONFIG_IEEE80211N
112 static int ap_list_beacon_olbc_ht(struct hostapd_iface *iface,
113                                   struct ap_info *ap)
114 {
115         return !ap->ht_support;
116 }
117 #endif /* CONFIG_IEEE80211N */
118
119
120 struct ap_info * ap_get_ap(struct hostapd_iface *iface, u8 *ap)
121 {
122         struct ap_info *s;
123
124         s = iface->ap_hash[STA_HASH(ap)];
125         while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0)
126                 s = s->hnext;
127         return s;
128 }
129
130
131 static void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap)
132 {
133         if (iface->ap_list) {
134                 ap->prev = iface->ap_list->prev;
135                 iface->ap_list->prev = ap;
136         } else
137                 ap->prev = ap;
138         ap->next = iface->ap_list;
139         iface->ap_list = ap;
140 }
141
142
143 static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap)
144 {
145         if (iface->ap_list == ap)
146                 iface->ap_list = ap->next;
147         else
148                 ap->prev->next = ap->next;
149
150         if (ap->next)
151                 ap->next->prev = ap->prev;
152         else if (iface->ap_list)
153                 iface->ap_list->prev = ap->prev;
154 }
155
156
157 static void ap_ap_iter_list_add(struct hostapd_iface *iface,
158                                 struct ap_info *ap)
159 {
160         if (iface->ap_iter_list) {
161                 ap->iter_prev = iface->ap_iter_list->iter_prev;
162                 iface->ap_iter_list->iter_prev = ap;
163         } else
164                 ap->iter_prev = ap;
165         ap->iter_next = iface->ap_iter_list;
166         iface->ap_iter_list = ap;
167 }
168
169
170 static void ap_ap_iter_list_del(struct hostapd_iface *iface,
171                                 struct ap_info *ap)
172 {
173         if (iface->ap_iter_list == ap)
174                 iface->ap_iter_list = ap->iter_next;
175         else
176                 ap->iter_prev->iter_next = ap->iter_next;
177
178         if (ap->iter_next)
179                 ap->iter_next->iter_prev = ap->iter_prev;
180         else if (iface->ap_iter_list)
181                 iface->ap_iter_list->iter_prev = ap->iter_prev;
182 }
183
184
185 static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap)
186 {
187         ap->hnext = iface->ap_hash[STA_HASH(ap->addr)];
188         iface->ap_hash[STA_HASH(ap->addr)] = ap;
189 }
190
191
192 static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap)
193 {
194         struct ap_info *s;
195
196         s = iface->ap_hash[STA_HASH(ap->addr)];
197         if (s == NULL) return;
198         if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) {
199                 iface->ap_hash[STA_HASH(ap->addr)] = s->hnext;
200                 return;
201         }
202
203         while (s->hnext != NULL &&
204                os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0)
205                 s = s->hnext;
206         if (s->hnext != NULL)
207                 s->hnext = s->hnext->hnext;
208         else
209                 printf("AP: could not remove AP " MACSTR " from hash table\n",
210                        MAC2STR(ap->addr));
211 }
212
213
214 static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap)
215 {
216         ap_ap_hash_del(iface, ap);
217         ap_ap_list_del(iface, ap);
218         ap_ap_iter_list_del(iface, ap);
219
220         iface->num_ap--;
221         os_free(ap);
222 }
223
224
225 static void hostapd_free_aps(struct hostapd_iface *iface)
226 {
227         struct ap_info *ap, *prev;
228
229         ap = iface->ap_list;
230
231         while (ap) {
232                 prev = ap;
233                 ap = ap->next;
234                 ap_free_ap(iface, prev);
235         }
236
237         iface->ap_list = NULL;
238 }
239
240
241 int ap_ap_for_each(struct hostapd_iface *iface,
242                    int (*func)(struct ap_info *s, void *data), void *data)
243 {
244         struct ap_info *s;
245         int ret = 0;
246
247         s = iface->ap_list;
248
249         while (s) {
250                 ret = func(s, data);
251                 if (ret)
252                         break;
253                 s = s->next;
254         }
255
256         return ret;
257 }
258
259
260 static struct ap_info * ap_ap_add(struct hostapd_iface *iface, u8 *addr)
261 {
262         struct ap_info *ap;
263
264         ap = os_zalloc(sizeof(struct ap_info));
265         if (ap == NULL)
266                 return NULL;
267
268         /* initialize AP info data */
269         os_memcpy(ap->addr, addr, ETH_ALEN);
270         ap_ap_list_add(iface, ap);
271         iface->num_ap++;
272         ap_ap_hash_add(iface, ap);
273         ap_ap_iter_list_add(iface, ap);
274
275         if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) {
276                 wpa_printf(MSG_DEBUG, "Removing the least recently used AP "
277                            MACSTR " from AP table", MAC2STR(ap->prev->addr));
278                 if (iface->conf->passive_scan_interval > 0)
279                         ap_list_expired_ap(iface, ap->prev);
280                 ap_free_ap(iface, ap->prev);
281         }
282
283         return ap;
284 }
285
286
287 void ap_list_process_beacon(struct hostapd_iface *iface,
288                             struct ieee80211_mgmt *mgmt,
289                             struct ieee802_11_elems *elems,
290                             struct hostapd_frame_info *fi)
291 {
292         struct ap_info *ap;
293         int new_ap = 0;
294         size_t len;
295         int set_beacon = 0;
296
297         if (iface->conf->ap_table_max_size < 1)
298                 return;
299
300         ap = ap_get_ap(iface, mgmt->bssid);
301         if (!ap) {
302                 ap = ap_ap_add(iface, mgmt->bssid);
303                 if (!ap) {
304                         printf("Failed to allocate AP information entry\n");
305                         return;
306                 }
307                 new_ap = 1;
308         }
309
310         ap->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int);
311         ap->capability = le_to_host16(mgmt->u.beacon.capab_info);
312
313         if (elems->ssid) {
314                 len = elems->ssid_len;
315                 if (len >= sizeof(ap->ssid))
316                         len = sizeof(ap->ssid) - 1;
317                 os_memcpy(ap->ssid, elems->ssid, len);
318                 ap->ssid[len] = '\0';
319                 ap->ssid_len = len;
320         }
321
322         os_memset(ap->supported_rates, 0, WLAN_SUPP_RATES_MAX);
323         len = 0;
324         if (elems->supp_rates) {
325                 len = elems->supp_rates_len;
326                 if (len > WLAN_SUPP_RATES_MAX)
327                         len = WLAN_SUPP_RATES_MAX;
328                 os_memcpy(ap->supported_rates, elems->supp_rates, len);
329         }
330         if (elems->ext_supp_rates) {
331                 int len2;
332                 if (len + elems->ext_supp_rates_len > WLAN_SUPP_RATES_MAX)
333                         len2 = WLAN_SUPP_RATES_MAX - len;
334                 else
335                         len2 = elems->ext_supp_rates_len;
336                 os_memcpy(ap->supported_rates + len, elems->ext_supp_rates,
337                           len2);
338         }
339
340         ap->wpa = elems->wpa_ie != NULL;
341
342         if (elems->erp_info && elems->erp_info_len == 1)
343                 ap->erp = elems->erp_info[0];
344         else
345                 ap->erp = -1;
346
347         if (elems->ds_params && elems->ds_params_len == 1)
348                 ap->channel = elems->ds_params[0];
349         else if (fi)
350                 ap->channel = fi->channel;
351
352         if (elems->ht_capabilities)
353                 ap->ht_support = 1;
354         else
355                 ap->ht_support = 0;
356
357         ap->num_beacons++;
358         time(&ap->last_beacon);
359         if (fi) {
360                 ap->phytype = fi->phytype;
361                 ap->ssi_signal = fi->ssi_signal;
362                 ap->datarate = fi->datarate;
363         }
364
365         if (new_ap) {
366                 if (iface->conf->passive_scan_interval > 0)
367                         ap_list_new_ap(iface, ap);
368         } else if (ap != iface->ap_list) {
369                 /* move AP entry into the beginning of the list so that the
370                  * oldest entry is always in the end of the list */
371                 ap_ap_list_del(iface, ap);
372                 ap_ap_list_add(iface, ap);
373         }
374
375         if (!iface->olbc &&
376             ap_list_beacon_olbc(iface, ap)) {
377                 iface->olbc = 1;
378                 wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR " - enable "
379                            "protection", MAC2STR(ap->addr));
380                 set_beacon++;
381         }
382
383 #ifdef CONFIG_IEEE80211N
384         if (!iface->olbc_ht && ap_list_beacon_olbc_ht(iface, ap)) {
385                 iface->olbc_ht = 1;
386                 hostapd_ht_operation_update(iface);
387                 wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR
388                            " - enable protection", MAC2STR(ap->addr));
389                 set_beacon++;
390         }
391 #endif /* CONFIG_IEEE80211N */
392
393         if (set_beacon)
394                 ieee802_11_set_beacons(iface);
395 }
396
397
398 static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
399 {
400         struct hostapd_iface *iface = eloop_ctx;
401         time_t now;
402         struct ap_info *ap;
403         int set_beacon = 0;
404
405         eloop_register_timeout(10, 0, ap_list_timer, iface, NULL);
406
407         if (!iface->ap_list)
408                 return;
409
410         time(&now);
411
412         /* FIX: it looks like jkm-Purina ended up in busy loop in this
413          * function. Apparently, something can still cause a loop in the AP
414          * list.. */
415
416         while (iface->ap_list) {
417                 ap = iface->ap_list->prev;
418                 if (ap->last_beacon + iface->conf->ap_table_expiration_time >=
419                     now)
420                         break;
421
422                 if (iface->conf->passive_scan_interval > 0)
423                         ap_list_expired_ap(iface, ap);
424                 ap_free_ap(iface, ap);
425         }
426
427         if (iface->olbc || iface->olbc_ht) {
428                 int olbc = 0;
429                 int olbc_ht = 0;
430
431                 ap = iface->ap_list;
432                 while (ap && (olbc == 0 || olbc_ht == 0)) {
433                         if (ap_list_beacon_olbc(iface, ap))
434                                 olbc = 1;
435 #ifdef CONFIG_IEEE80211N
436                         if (ap_list_beacon_olbc_ht(iface, ap))
437                                 olbc_ht = 1;
438 #endif /* CONFIG_IEEE80211N */
439                         ap = ap->next;
440                 }
441                 if (!olbc && iface->olbc) {
442                         wpa_printf(MSG_DEBUG, "OLBC not detected anymore");
443                         iface->olbc = 0;
444                         set_beacon++;
445                 }
446 #ifdef CONFIG_IEEE80211N
447                 if (!olbc_ht && iface->olbc_ht) {
448                         wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore");
449                         iface->olbc_ht = 0;
450                         hostapd_ht_operation_update(iface);
451                         set_beacon++;
452                 }
453 #endif /* CONFIG_IEEE80211N */
454         }
455
456         if (set_beacon)
457                 ieee802_11_set_beacons(iface);
458 }
459
460
461 int ap_list_init(struct hostapd_iface *iface)
462 {
463         eloop_register_timeout(10, 0, ap_list_timer, iface, NULL);
464         return 0;
465 }
466
467
468 void ap_list_deinit(struct hostapd_iface *iface)
469 {
470         eloop_cancel_timeout(ap_list_timer, iface, NULL);
471         hostapd_free_aps(iface);
472 }
473
474
475 int ap_list_reconfig(struct hostapd_iface *iface,
476                      struct hostapd_config *oldconf)
477 {
478         time_t now;
479         struct ap_info *ap;
480
481         if (iface->conf->ap_table_max_size == oldconf->ap_table_max_size &&
482             iface->conf->ap_table_expiration_time ==
483             oldconf->ap_table_expiration_time)
484                 return 0;
485
486         time(&now);
487
488         while (iface->ap_list) {
489                 ap = iface->ap_list->prev;
490                 if (iface->num_ap <= iface->conf->ap_table_max_size &&
491                     ap->last_beacon + iface->conf->ap_table_expiration_time >=
492                     now)
493                         break;
494
495                 if (iface->conf->passive_scan_interval > 0)
496                         ap_list_expired_ap(iface, iface->ap_list->prev);
497                 ap_free_ap(iface, iface->ap_list->prev);
498         }
499
500         return 0;
501 }