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 && !ether_addr_equal(s->addr, ap))
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 (ether_addr_equal(s->addr, ap->addr)) {
104  		iface->ap_hash[STA_HASH(ap->addr)] = s->hnext;
105  		return;
106  	}
107  
108  	while (s->hnext != NULL &&
109  	       !ether_addr_equal(s->hnext->addr, ap->addr))
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  	if (!iface->olbc_ht && !ap->ht_support &&
232  	    (ap->channel == 0 ||
233  	     ap->channel == iface->conf->channel ||
234  	     ap->channel == iface->conf->channel +
235  	     iface->conf->secondary_channel * 4)) {
236  		iface->olbc_ht = 1;
237  		hostapd_ht_operation_update(iface);
238  		wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR
239  			   " (channel %d) - enable protection",
240  			   MAC2STR(ap->addr), ap->channel);
241  		set_beacon++;
242  	}
243  
244  	if (set_beacon)
245  		ieee802_11_update_beacons(iface);
246  }
247  
248  
ap_list_timer(struct hostapd_iface * iface)249  void ap_list_timer(struct hostapd_iface *iface)
250  {
251  	struct os_reltime now;
252  	struct ap_info *ap;
253  	int set_beacon = 0;
254  
255  	if (!iface->ap_list)
256  		return;
257  
258  	os_get_reltime(&now);
259  
260  	while (iface->ap_list) {
261  		ap = iface->ap_list->prev;
262  		if (!os_reltime_expired(&now, &ap->last_beacon,
263  					iface->conf->ap_table_expiration_time))
264  			break;
265  
266  		ap_free_ap(iface, ap);
267  	}
268  
269  	if (iface->olbc || iface->olbc_ht) {
270  		int olbc = 0;
271  		int olbc_ht = 0;
272  
273  		ap = iface->ap_list;
274  		while (ap && (olbc == 0 || olbc_ht == 0)) {
275  			if (ap_list_beacon_olbc(iface, ap))
276  				olbc = 1;
277  			if (!ap->ht_support)
278  				olbc_ht = 1;
279  			ap = ap->next;
280  		}
281  		if (!olbc && iface->olbc) {
282  			wpa_printf(MSG_DEBUG, "OLBC not detected anymore");
283  			iface->olbc = 0;
284  			set_beacon++;
285  		}
286  		if (!olbc_ht && iface->olbc_ht) {
287  			wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore");
288  			iface->olbc_ht = 0;
289  			hostapd_ht_operation_update(iface);
290  			set_beacon++;
291  		}
292  	}
293  
294  	if (set_beacon)
295  		ieee802_11_update_beacons(iface);
296  }
297  
298  
ap_list_init(struct hostapd_iface * iface)299  int ap_list_init(struct hostapd_iface *iface)
300  {
301  	return 0;
302  }
303  
304  
ap_list_deinit(struct hostapd_iface * iface)305  void ap_list_deinit(struct hostapd_iface *iface)
306  {
307  	hostapd_free_aps(iface);
308  }
309