xref: /dragonfly/contrib/wpa_supplicant/src/ap/ap_list.c (revision 3a84a4273475ed07d0ab1c2dfeffdfedef35d9cd)
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 
ap_list_beacon_olbc(struct hostapd_iface * iface,struct ap_info * ap)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 
ap_get_ap(struct hostapd_iface * iface,const u8 * ap)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 
ap_ap_list_add(struct hostapd_iface * iface,struct ap_info * ap)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 
ap_ap_list_del(struct hostapd_iface * iface,struct ap_info * ap)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 
ap_ap_hash_add(struct hostapd_iface * iface,struct ap_info * ap)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 
ap_ap_hash_del(struct hostapd_iface * iface,struct ap_info * ap)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 
ap_free_ap(struct hostapd_iface * iface,struct ap_info * ap)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 
hostapd_free_aps(struct hostapd_iface * iface)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 
ap_ap_add(struct hostapd_iface * iface,const u8 * addr)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 
ap_list_process_beacon(struct hostapd_iface * iface,const struct ieee80211_mgmt * mgmt,struct ieee802_11_elems * elems,struct hostapd_frame_info * fi)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)
197                     ap->erp = elems->erp_info[0];
198           else
199                     ap->erp = -1;
200 
201           if (elems->ds_params)
202                     ap->channel = elems->ds_params[0];
203           else if (elems->ht_operation)
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 
ap_list_timer(struct hostapd_iface * iface)251 void ap_list_timer(struct hostapd_iface *iface)
252 {
253           struct os_reltime now;
254           struct ap_info *ap;
255           int set_beacon = 0;
256 
257           if (!iface->ap_list)
258                     return;
259 
260           os_get_reltime(&now);
261 
262           while (iface->ap_list) {
263                     ap = iface->ap_list->prev;
264                     if (!os_reltime_expired(&now, &ap->last_beacon,
265                                                   iface->conf->ap_table_expiration_time))
266                               break;
267 
268                     ap_free_ap(iface, ap);
269           }
270 
271           if (iface->olbc || iface->olbc_ht) {
272                     int olbc = 0;
273                     int olbc_ht = 0;
274 
275                     ap = iface->ap_list;
276                     while (ap && (olbc == 0 || olbc_ht == 0)) {
277                               if (ap_list_beacon_olbc(iface, ap))
278                                         olbc = 1;
279                               if (!ap->ht_support)
280                                         olbc_ht = 1;
281                               ap = ap->next;
282                     }
283                     if (!olbc && iface->olbc) {
284                               wpa_printf(MSG_DEBUG, "OLBC not detected anymore");
285                               iface->olbc = 0;
286                               set_beacon++;
287                     }
288 #ifdef CONFIG_IEEE80211N
289                     if (!olbc_ht && iface->olbc_ht) {
290                               wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore");
291                               iface->olbc_ht = 0;
292                               hostapd_ht_operation_update(iface);
293                               set_beacon++;
294                     }
295 #endif /* CONFIG_IEEE80211N */
296           }
297 
298           if (set_beacon)
299                     ieee802_11_update_beacons(iface);
300 }
301 
302 
ap_list_init(struct hostapd_iface * iface)303 int ap_list_init(struct hostapd_iface *iface)
304 {
305           return 0;
306 }
307 
308 
ap_list_deinit(struct hostapd_iface * iface)309 void ap_list_deinit(struct hostapd_iface *iface)
310 {
311           hostapd_free_aps(iface);
312 }
313