]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/wpa/src/ap/ap_list.c
Merge sendmail 8.15.2 to HEAD
[FreeBSD/FreeBSD.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 "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 && elems->erp_info_len == 1)
197                 ap->erp = elems->erp_info[0];
198         else
199                 ap->erp = -1;
200
201         if (elems->ds_params && elems->ds_params_len == 1)
202                 ap->channel = elems->ds_params[0];
203         else if (elems->ht_operation && elems->ht_operation_len >= 1)
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 static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
252 {
253         struct hostapd_iface *iface = eloop_ctx;
254         struct os_reltime now;
255         struct ap_info *ap;
256         int set_beacon = 0;
257
258         eloop_register_timeout(10, 0, ap_list_timer, iface, NULL);
259
260         if (!iface->ap_list)
261                 return;
262
263         os_get_reltime(&now);
264
265         while (iface->ap_list) {
266                 ap = iface->ap_list->prev;
267                 if (!os_reltime_expired(&now, &ap->last_beacon,
268                                         iface->conf->ap_table_expiration_time))
269                         break;
270
271                 ap_free_ap(iface, ap);
272         }
273
274         if (iface->olbc || iface->olbc_ht) {
275                 int olbc = 0;
276                 int olbc_ht = 0;
277
278                 ap = iface->ap_list;
279                 while (ap && (olbc == 0 || olbc_ht == 0)) {
280                         if (ap_list_beacon_olbc(iface, ap))
281                                 olbc = 1;
282                         if (!ap->ht_support)
283                                 olbc_ht = 1;
284                         ap = ap->next;
285                 }
286                 if (!olbc && iface->olbc) {
287                         wpa_printf(MSG_DEBUG, "OLBC not detected anymore");
288                         iface->olbc = 0;
289                         set_beacon++;
290                 }
291 #ifdef CONFIG_IEEE80211N
292                 if (!olbc_ht && iface->olbc_ht) {
293                         wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore");
294                         iface->olbc_ht = 0;
295                         hostapd_ht_operation_update(iface);
296                         set_beacon++;
297                 }
298 #endif /* CONFIG_IEEE80211N */
299         }
300
301         if (set_beacon)
302                 ieee802_11_update_beacons(iface);
303 }
304
305
306 int ap_list_init(struct hostapd_iface *iface)
307 {
308         eloop_register_timeout(10, 0, ap_list_timer, iface, NULL);
309         return 0;
310 }
311
312
313 void ap_list_deinit(struct hostapd_iface *iface)
314 {
315         eloop_cancel_timeout(ap_list_timer, iface, NULL);
316         hostapd_free_aps(iface);
317 }