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