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