]>
Commit | Line | Data |
---|---|---|
6fc6879b JM |
1 | /* |
2 | * hostapd / AP table | |
1057d78e | 3 | * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> |
71b6ae14 TW |
4 | * Copyright (c) 2003-2004, Instant802 Networks, Inc. |
5 | * Copyright (c) 2006, Devicescape Software, Inc. | |
6fc6879b JM |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License version 2 as | |
9 | * published by the Free Software Foundation. | |
10 | * | |
11 | * Alternatively, this software may be distributed under the terms of BSD | |
12 | * license. | |
13 | * | |
14 | * See README and COPYING for more details. | |
15 | */ | |
16 | ||
1057d78e | 17 | #include "utils/includes.h" |
6fc6879b | 18 | |
1057d78e JM |
19 | #include "utils/common.h" |
20 | #include "utils/eloop.h" | |
81f4f619 JM |
21 | #include "common/ieee802_11_defs.h" |
22 | #include "common/ieee802_11_common.h" | |
1057d78e | 23 | #include "drivers/driver.h" |
6fc6879b | 24 | #include "hostapd.h" |
6226e38d | 25 | #include "ap_config.h" |
6fc6879b | 26 | #include "ieee802_11.h" |
97234b50 | 27 | #include "sta_info.h" |
6fc6879b | 28 | #include "beacon.h" |
1057d78e | 29 | #include "ap_list.h" |
6fc6879b JM |
30 | |
31 | ||
6fc6879b JM |
32 | /* AP list is a double linked list with head->prev pointing to the end of the |
33 | * list and tail->next = NULL. Entries are moved to the head of the list | |
34 | * whenever a beacon has been received from the AP in question. The tail entry | |
35 | * in this link will thus be the least recently used entry. */ | |
36 | ||
37 | ||
6fc6879b JM |
38 | static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap) |
39 | { | |
40 | int i; | |
41 | ||
42 | if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G || | |
6fc6879b JM |
43 | iface->conf->channel != ap->channel) |
44 | return 0; | |
45 | ||
46 | if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT)) | |
47 | return 1; | |
48 | ||
49 | for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) { | |
50 | int rate = (ap->supported_rates[i] & 0x7f) * 5; | |
51 | if (rate == 60 || rate == 90 || rate > 110) | |
52 | return 0; | |
53 | } | |
54 | ||
55 | return 1; | |
56 | } | |
57 | ||
58 | ||
b57e086c | 59 | struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap) |
6fc6879b JM |
60 | { |
61 | struct ap_info *s; | |
62 | ||
63 | s = iface->ap_hash[STA_HASH(ap)]; | |
64 | while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0) | |
65 | s = s->hnext; | |
66 | return s; | |
67 | } | |
68 | ||
69 | ||
70 | static void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap) | |
71 | { | |
72 | if (iface->ap_list) { | |
73 | ap->prev = iface->ap_list->prev; | |
74 | iface->ap_list->prev = ap; | |
75 | } else | |
76 | ap->prev = ap; | |
77 | ap->next = iface->ap_list; | |
78 | iface->ap_list = ap; | |
79 | } | |
80 | ||
81 | ||
82 | static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap) | |
83 | { | |
84 | if (iface->ap_list == ap) | |
85 | iface->ap_list = ap->next; | |
86 | else | |
87 | ap->prev->next = ap->next; | |
88 | ||
89 | if (ap->next) | |
90 | ap->next->prev = ap->prev; | |
91 | else if (iface->ap_list) | |
92 | iface->ap_list->prev = ap->prev; | |
93 | } | |
94 | ||
95 | ||
96 | static void ap_ap_iter_list_add(struct hostapd_iface *iface, | |
97 | struct ap_info *ap) | |
98 | { | |
99 | if (iface->ap_iter_list) { | |
100 | ap->iter_prev = iface->ap_iter_list->iter_prev; | |
101 | iface->ap_iter_list->iter_prev = ap; | |
102 | } else | |
103 | ap->iter_prev = ap; | |
104 | ap->iter_next = iface->ap_iter_list; | |
105 | iface->ap_iter_list = ap; | |
106 | } | |
107 | ||
108 | ||
109 | static void ap_ap_iter_list_del(struct hostapd_iface *iface, | |
110 | struct ap_info *ap) | |
111 | { | |
112 | if (iface->ap_iter_list == ap) | |
113 | iface->ap_iter_list = ap->iter_next; | |
114 | else | |
115 | ap->iter_prev->iter_next = ap->iter_next; | |
116 | ||
117 | if (ap->iter_next) | |
118 | ap->iter_next->iter_prev = ap->iter_prev; | |
119 | else if (iface->ap_iter_list) | |
120 | iface->ap_iter_list->iter_prev = ap->iter_prev; | |
121 | } | |
122 | ||
123 | ||
124 | static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap) | |
125 | { | |
126 | ap->hnext = iface->ap_hash[STA_HASH(ap->addr)]; | |
127 | iface->ap_hash[STA_HASH(ap->addr)] = ap; | |
128 | } | |
129 | ||
130 | ||
131 | static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap) | |
132 | { | |
133 | struct ap_info *s; | |
134 | ||
135 | s = iface->ap_hash[STA_HASH(ap->addr)]; | |
136 | if (s == NULL) return; | |
137 | if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) { | |
138 | iface->ap_hash[STA_HASH(ap->addr)] = s->hnext; | |
139 | return; | |
140 | } | |
141 | ||
142 | while (s->hnext != NULL && | |
143 | os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0) | |
144 | s = s->hnext; | |
145 | if (s->hnext != NULL) | |
146 | s->hnext = s->hnext->hnext; | |
147 | else | |
148 | printf("AP: could not remove AP " MACSTR " from hash table\n", | |
149 | MAC2STR(ap->addr)); | |
150 | } | |
151 | ||
152 | ||
153 | static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap) | |
154 | { | |
155 | ap_ap_hash_del(iface, ap); | |
156 | ap_ap_list_del(iface, ap); | |
157 | ap_ap_iter_list_del(iface, ap); | |
158 | ||
159 | iface->num_ap--; | |
160 | os_free(ap); | |
161 | } | |
162 | ||
163 | ||
164 | static void hostapd_free_aps(struct hostapd_iface *iface) | |
165 | { | |
166 | struct ap_info *ap, *prev; | |
167 | ||
168 | ap = iface->ap_list; | |
169 | ||
170 | while (ap) { | |
171 | prev = ap; | |
172 | ap = ap->next; | |
173 | ap_free_ap(iface, prev); | |
174 | } | |
175 | ||
176 | iface->ap_list = NULL; | |
177 | } | |
178 | ||
179 | ||
180 | int ap_ap_for_each(struct hostapd_iface *iface, | |
181 | int (*func)(struct ap_info *s, void *data), void *data) | |
182 | { | |
183 | struct ap_info *s; | |
184 | int ret = 0; | |
185 | ||
186 | s = iface->ap_list; | |
187 | ||
188 | while (s) { | |
189 | ret = func(s, data); | |
190 | if (ret) | |
191 | break; | |
192 | s = s->next; | |
193 | } | |
194 | ||
195 | return ret; | |
196 | } | |
197 | ||
198 | ||
b57e086c | 199 | static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr) |
6fc6879b JM |
200 | { |
201 | struct ap_info *ap; | |
202 | ||
203 | ap = os_zalloc(sizeof(struct ap_info)); | |
204 | if (ap == NULL) | |
205 | return NULL; | |
206 | ||
207 | /* initialize AP info data */ | |
208 | os_memcpy(ap->addr, addr, ETH_ALEN); | |
209 | ap_ap_list_add(iface, ap); | |
210 | iface->num_ap++; | |
211 | ap_ap_hash_add(iface, ap); | |
212 | ap_ap_iter_list_add(iface, ap); | |
213 | ||
214 | if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) { | |
215 | wpa_printf(MSG_DEBUG, "Removing the least recently used AP " | |
216 | MACSTR " from AP table", MAC2STR(ap->prev->addr)); | |
6fc6879b JM |
217 | ap_free_ap(iface, ap->prev); |
218 | } | |
219 | ||
220 | return ap; | |
221 | } | |
222 | ||
223 | ||
224 | void ap_list_process_beacon(struct hostapd_iface *iface, | |
b57e086c | 225 | const struct ieee80211_mgmt *mgmt, |
6fc6879b JM |
226 | struct ieee802_11_elems *elems, |
227 | struct hostapd_frame_info *fi) | |
228 | { | |
229 | struct ap_info *ap; | |
230 | int new_ap = 0; | |
231 | size_t len; | |
de9289c8 | 232 | int set_beacon = 0; |
6fc6879b JM |
233 | |
234 | if (iface->conf->ap_table_max_size < 1) | |
235 | return; | |
236 | ||
237 | ap = ap_get_ap(iface, mgmt->bssid); | |
238 | if (!ap) { | |
239 | ap = ap_ap_add(iface, mgmt->bssid); | |
240 | if (!ap) { | |
241 | printf("Failed to allocate AP information entry\n"); | |
242 | return; | |
243 | } | |
244 | new_ap = 1; | |
245 | } | |
246 | ||
247 | ap->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int); | |
248 | ap->capability = le_to_host16(mgmt->u.beacon.capab_info); | |
249 | ||
250 | if (elems->ssid) { | |
251 | len = elems->ssid_len; | |
252 | if (len >= sizeof(ap->ssid)) | |
253 | len = sizeof(ap->ssid) - 1; | |
254 | os_memcpy(ap->ssid, elems->ssid, len); | |
255 | ap->ssid[len] = '\0'; | |
256 | ap->ssid_len = len; | |
257 | } | |
258 | ||
259 | os_memset(ap->supported_rates, 0, WLAN_SUPP_RATES_MAX); | |
260 | len = 0; | |
261 | if (elems->supp_rates) { | |
262 | len = elems->supp_rates_len; | |
263 | if (len > WLAN_SUPP_RATES_MAX) | |
264 | len = WLAN_SUPP_RATES_MAX; | |
265 | os_memcpy(ap->supported_rates, elems->supp_rates, len); | |
266 | } | |
267 | if (elems->ext_supp_rates) { | |
268 | int len2; | |
269 | if (len + elems->ext_supp_rates_len > WLAN_SUPP_RATES_MAX) | |
270 | len2 = WLAN_SUPP_RATES_MAX - len; | |
271 | else | |
272 | len2 = elems->ext_supp_rates_len; | |
273 | os_memcpy(ap->supported_rates + len, elems->ext_supp_rates, | |
274 | len2); | |
275 | } | |
276 | ||
277 | ap->wpa = elems->wpa_ie != NULL; | |
278 | ||
279 | if (elems->erp_info && elems->erp_info_len == 1) | |
280 | ap->erp = elems->erp_info[0]; | |
281 | else | |
282 | ap->erp = -1; | |
283 | ||
284 | if (elems->ds_params && elems->ds_params_len == 1) | |
285 | ap->channel = elems->ds_params[0]; | |
286 | else if (fi) | |
287 | ap->channel = fi->channel; | |
288 | ||
de9289c8 JM |
289 | if (elems->ht_capabilities) |
290 | ap->ht_support = 1; | |
291 | else | |
292 | ap->ht_support = 0; | |
293 | ||
6fc6879b JM |
294 | ap->num_beacons++; |
295 | time(&ap->last_beacon); | |
296 | if (fi) { | |
6fc6879b JM |
297 | ap->ssi_signal = fi->ssi_signal; |
298 | ap->datarate = fi->datarate; | |
299 | } | |
300 | ||
61693eaa | 301 | if (!new_ap && ap != iface->ap_list) { |
6fc6879b JM |
302 | /* move AP entry into the beginning of the list so that the |
303 | * oldest entry is always in the end of the list */ | |
304 | ap_ap_list_del(iface, ap); | |
305 | ap_ap_list_add(iface, ap); | |
306 | } | |
307 | ||
308 | if (!iface->olbc && | |
309 | ap_list_beacon_olbc(iface, ap)) { | |
6fc6879b JM |
310 | iface->olbc = 1; |
311 | wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR " - enable " | |
312 | "protection", MAC2STR(ap->addr)); | |
de9289c8 | 313 | set_beacon++; |
6fc6879b | 314 | } |
de9289c8 JM |
315 | |
316 | #ifdef CONFIG_IEEE80211N | |
eb53b752 | 317 | if (!iface->olbc_ht && !ap->ht_support) { |
de9289c8 JM |
318 | iface->olbc_ht = 1; |
319 | hostapd_ht_operation_update(iface); | |
320 | wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR | |
321 | " - enable protection", MAC2STR(ap->addr)); | |
322 | set_beacon++; | |
323 | } | |
324 | #endif /* CONFIG_IEEE80211N */ | |
325 | ||
326 | if (set_beacon) | |
327 | ieee802_11_set_beacons(iface); | |
6fc6879b JM |
328 | } |
329 | ||
330 | ||
331 | static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) | |
332 | { | |
333 | struct hostapd_iface *iface = eloop_ctx; | |
334 | time_t now; | |
335 | struct ap_info *ap; | |
de9289c8 | 336 | int set_beacon = 0; |
6fc6879b JM |
337 | |
338 | eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); | |
339 | ||
340 | if (!iface->ap_list) | |
341 | return; | |
342 | ||
343 | time(&now); | |
344 | ||
6fc6879b JM |
345 | while (iface->ap_list) { |
346 | ap = iface->ap_list->prev; | |
347 | if (ap->last_beacon + iface->conf->ap_table_expiration_time >= | |
348 | now) | |
349 | break; | |
350 | ||
6fc6879b JM |
351 | ap_free_ap(iface, ap); |
352 | } | |
353 | ||
de9289c8 | 354 | if (iface->olbc || iface->olbc_ht) { |
6fc6879b | 355 | int olbc = 0; |
de9289c8 JM |
356 | int olbc_ht = 0; |
357 | ||
6fc6879b | 358 | ap = iface->ap_list; |
de9289c8 JM |
359 | while (ap && (olbc == 0 || olbc_ht == 0)) { |
360 | if (ap_list_beacon_olbc(iface, ap)) | |
6fc6879b | 361 | olbc = 1; |
eb53b752 | 362 | if (!ap->ht_support) |
de9289c8 | 363 | olbc_ht = 1; |
6fc6879b JM |
364 | ap = ap->next; |
365 | } | |
de9289c8 | 366 | if (!olbc && iface->olbc) { |
6fc6879b JM |
367 | wpa_printf(MSG_DEBUG, "OLBC not detected anymore"); |
368 | iface->olbc = 0; | |
de9289c8 JM |
369 | set_beacon++; |
370 | } | |
371 | #ifdef CONFIG_IEEE80211N | |
372 | if (!olbc_ht && iface->olbc_ht) { | |
373 | wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore"); | |
374 | iface->olbc_ht = 0; | |
375 | hostapd_ht_operation_update(iface); | |
376 | set_beacon++; | |
6fc6879b | 377 | } |
de9289c8 | 378 | #endif /* CONFIG_IEEE80211N */ |
6fc6879b | 379 | } |
de9289c8 JM |
380 | |
381 | if (set_beacon) | |
382 | ieee802_11_set_beacons(iface); | |
6fc6879b JM |
383 | } |
384 | ||
385 | ||
386 | int ap_list_init(struct hostapd_iface *iface) | |
387 | { | |
388 | eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); | |
389 | return 0; | |
390 | } | |
391 | ||
392 | ||
393 | void ap_list_deinit(struct hostapd_iface *iface) | |
394 | { | |
395 | eloop_cancel_timeout(ap_list_timer, iface, NULL); | |
396 | hostapd_free_aps(iface); | |
397 | } |