1 //------------------------------------------------------------------------------
2 // <copyright file="wlan_node.c" company="Atheros">
3 // Copyright (c) 2004-2010 Atheros Corporation. All rights reserved.
4 //
5 //
6 // Permission to use, copy, modify, and/or distribute this software for any
7 // purpose with or without fee is hereby granted, provided that the above
8 // copyright notice and this permission notice appear in all copies.
9 //
10 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 //
18 //
19 //------------------------------------------------------------------------------
20 //==============================================================================
21 // IEEE 802.11 node handling support.
22 //
23 // Author(s): ="Atheros"
24 //==============================================================================
25 #include <a_config.h>
26 #include <athdefs.h>
27 #include <a_types.h>
28 #include <a_osapi.h>
29 #define ATH_MODULE_NAME wlan
30 #include <a_debug.h>
31 #include "htc.h"
32 #include "htc_api.h"
33 #include <wmi.h>
34 #include <ieee80211.h>
35 #include <wlan_api.h>
36 #include <wmi_api.h>
37 #include <ieee80211_node.h>
38
39 #define ATH_DEBUG_WLAN ATH_DEBUG_MAKE_MODULE_MASK(0)
40
41 #ifdef ATH_DEBUG_MODULE
42
43 static struct ath_debug_mask_description wlan_debug_desc[] = {
44 { ATH_DEBUG_WLAN , "General WLAN Node Tracing"},
45 };
46
47 ATH_DEBUG_INSTANTIATE_MODULE_VAR(wlan,
48 "wlan",
49 "WLAN Node Management",
50 ATH_DEBUG_MASK_DEFAULTS,
51 ATH_DEBUG_DESCRIPTION_COUNT(wlan_debug_desc),
52 wlan_debug_desc);
53
54 #endif
55
56 #ifdef THREAD_X
57 static void wlan_node_timeout(A_ATH_TIMER arg);
58 #endif
59
60 static bss_t * _ieee80211_find_node (struct ieee80211_node_table *nt,
61 const u8 *macaddr);
62
63 bss_t *
wlan_node_alloc(struct ieee80211_node_table * nt,int wh_size)64 wlan_node_alloc(struct ieee80211_node_table *nt, int wh_size)
65 {
66 bss_t *ni;
67
68 ni = A_MALLOC_NOWAIT(sizeof(bss_t));
69
70 if (ni != NULL) {
71 if (wh_size)
72 {
73 ni->ni_buf = A_MALLOC_NOWAIT(wh_size);
74 if (ni->ni_buf == NULL) {
75 A_FREE(ni);
76 ni = NULL;
77 return ni;
78 }
79 }
80 } else {
81 return ni;
82 }
83
84 /* Make sure our lists are clean */
85 ni->ni_list_next = NULL;
86 ni->ni_list_prev = NULL;
87 ni->ni_hash_next = NULL;
88 ni->ni_hash_prev = NULL;
89
90 //
91 // ni_scangen never initialized before and during suspend/resume of winmobile,
92 // that some junk has been stored in this, due to this scan list didn't properly updated
93 //
94 ni->ni_scangen = 0;
95
96 #ifdef OS_ROAM_MANAGEMENT
97 ni->ni_si_gen = 0;
98 #endif
99
100 return ni;
101 }
102
103 void
wlan_node_free(bss_t * ni)104 wlan_node_free(bss_t *ni)
105 {
106 if (ni->ni_buf != NULL) {
107 A_FREE(ni->ni_buf);
108 }
109 A_FREE(ni);
110 }
111
112 void
wlan_setup_node(struct ieee80211_node_table * nt,bss_t * ni,const u8 * macaddr)113 wlan_setup_node(struct ieee80211_node_table *nt, bss_t *ni,
114 const u8 *macaddr)
115 {
116 int hash;
117 u32 timeoutValue = 0;
118
119 memcpy(ni->ni_macaddr, macaddr, IEEE80211_ADDR_LEN);
120 hash = IEEE80211_NODE_HASH (macaddr);
121 ieee80211_node_initref (ni); /* mark referenced */
122
123 timeoutValue = nt->nt_nodeAge;
124
125 ni->ni_tstamp = A_GET_MS (0);
126 ni->ni_actcnt = WLAN_NODE_INACT_CNT;
127
128 IEEE80211_NODE_LOCK_BH(nt);
129
130 /* Insert at the end of the node list */
131 ni->ni_list_next = NULL;
132 ni->ni_list_prev = nt->nt_node_last;
133 if(nt->nt_node_last != NULL)
134 {
135 nt->nt_node_last->ni_list_next = ni;
136 }
137 nt->nt_node_last = ni;
138 if(nt->nt_node_first == NULL)
139 {
140 nt->nt_node_first = ni;
141 }
142
143 /* Insert into the hash list i.e. the bucket */
144 if((ni->ni_hash_next = nt->nt_hash[hash]) != NULL)
145 {
146 nt->nt_hash[hash]->ni_hash_prev = ni;
147 }
148 ni->ni_hash_prev = NULL;
149 nt->nt_hash[hash] = ni;
150
151 #ifdef THREAD_X
152 if (!nt->isTimerArmed) {
153 A_TIMEOUT_MS(&nt->nt_inact_timer, timeoutValue, 0);
154 nt->isTimerArmed = true;
155 }
156 #endif
157
158 IEEE80211_NODE_UNLOCK_BH(nt);
159 }
160
161 static bss_t *
_ieee80211_find_node(struct ieee80211_node_table * nt,const u8 * macaddr)162 _ieee80211_find_node(struct ieee80211_node_table *nt,
163 const u8 *macaddr)
164 {
165 bss_t *ni;
166 int hash;
167
168 IEEE80211_NODE_LOCK_ASSERT(nt);
169
170 hash = IEEE80211_NODE_HASH(macaddr);
171 for(ni = nt->nt_hash[hash]; ni; ni = ni->ni_hash_next) {
172 if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
173 ieee80211_node_incref(ni); /* mark referenced */
174 return ni;
175 }
176 }
177 return NULL;
178 }
179
180 bss_t *
wlan_find_node(struct ieee80211_node_table * nt,const u8 * macaddr)181 wlan_find_node(struct ieee80211_node_table *nt, const u8 *macaddr)
182 {
183 bss_t *ni;
184
185 IEEE80211_NODE_LOCK(nt);
186 ni = _ieee80211_find_node(nt, macaddr);
187 IEEE80211_NODE_UNLOCK(nt);
188 return ni;
189 }
190
191 /*
192 * Reclaim a node. If this is the last reference count then
193 * do the normal free work. Otherwise remove it from the node
194 * table and mark it gone by clearing the back-reference.
195 */
196 void
wlan_node_reclaim(struct ieee80211_node_table * nt,bss_t * ni)197 wlan_node_reclaim(struct ieee80211_node_table *nt, bss_t *ni)
198 {
199 IEEE80211_NODE_LOCK(nt);
200
201 if(ni->ni_list_prev == NULL)
202 {
203 /* First in list so fix the list head */
204 nt->nt_node_first = ni->ni_list_next;
205 }
206 else
207 {
208 ni->ni_list_prev->ni_list_next = ni->ni_list_next;
209 }
210
211 if(ni->ni_list_next == NULL)
212 {
213 /* Last in list so fix list tail */
214 nt->nt_node_last = ni->ni_list_prev;
215 }
216 else
217 {
218 ni->ni_list_next->ni_list_prev = ni->ni_list_prev;
219 }
220
221 if(ni->ni_hash_prev == NULL)
222 {
223 /* First in list so fix the list head */
224 int hash;
225 hash = IEEE80211_NODE_HASH(ni->ni_macaddr);
226 nt->nt_hash[hash] = ni->ni_hash_next;
227 }
228 else
229 {
230 ni->ni_hash_prev->ni_hash_next = ni->ni_hash_next;
231 }
232
233 if(ni->ni_hash_next != NULL)
234 {
235 ni->ni_hash_next->ni_hash_prev = ni->ni_hash_prev;
236 }
237 wlan_node_free(ni);
238
239 IEEE80211_NODE_UNLOCK(nt);
240 }
241
242 static void
wlan_node_dec_free(bss_t * ni)243 wlan_node_dec_free(bss_t *ni)
244 {
245 if (ieee80211_node_dectestref(ni)) {
246 wlan_node_free(ni);
247 }
248 }
249
250 void
wlan_free_allnodes(struct ieee80211_node_table * nt)251 wlan_free_allnodes(struct ieee80211_node_table *nt)
252 {
253 bss_t *ni;
254
255 while ((ni = nt->nt_node_first) != NULL) {
256 wlan_node_reclaim(nt, ni);
257 }
258 }
259
260 void
wlan_iterate_nodes(struct ieee80211_node_table * nt,wlan_node_iter_func * f,void * arg)261 wlan_iterate_nodes(struct ieee80211_node_table *nt, wlan_node_iter_func *f,
262 void *arg)
263 {
264 bss_t *ni;
265 u32 gen;
266
267 gen = ++nt->nt_scangen;
268
269 IEEE80211_NODE_LOCK(nt);
270 for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) {
271 if (ni->ni_scangen != gen) {
272 ni->ni_scangen = gen;
273 (void) ieee80211_node_incref(ni);
274 (*f)(arg, ni);
275 wlan_node_dec_free(ni);
276 }
277 }
278 IEEE80211_NODE_UNLOCK(nt);
279 }
280
281 /*
282 * Node table support.
283 */
284 void
wlan_node_table_init(void * wmip,struct ieee80211_node_table * nt)285 wlan_node_table_init(void *wmip, struct ieee80211_node_table *nt)
286 {
287 int i;
288
289 AR_DEBUG_PRINTF(ATH_DEBUG_WLAN, ("node table = 0x%lx\n", (unsigned long)nt));
290 IEEE80211_NODE_LOCK_INIT(nt);
291
292 A_REGISTER_MODULE_DEBUG_INFO(wlan);
293
294 nt->nt_node_first = nt->nt_node_last = NULL;
295 for(i = 0; i < IEEE80211_NODE_HASHSIZE; i++)
296 {
297 nt->nt_hash[i] = NULL;
298 }
299
300 #ifdef THREAD_X
301 A_INIT_TIMER(&nt->nt_inact_timer, wlan_node_timeout, nt);
302 nt->isTimerArmed = false;
303 #endif
304 nt->nt_wmip = wmip;
305 nt->nt_nodeAge = WLAN_NODE_INACT_TIMEOUT_MSEC;
306
307 //
308 // nt_scangen never initialized before and during suspend/resume of winmobile,
309 // that some junk has been stored in this, due to this scan list didn't properly updated
310 //
311 nt->nt_scangen = 0;
312
313 #ifdef OS_ROAM_MANAGEMENT
314 nt->nt_si_gen = 0;
315 #endif
316 }
317
318 void
wlan_set_nodeage(struct ieee80211_node_table * nt,u32 nodeAge)319 wlan_set_nodeage(struct ieee80211_node_table *nt, u32 nodeAge)
320 {
321 nt->nt_nodeAge = nodeAge;
322 return;
323 }
324 void
wlan_refresh_inactive_nodes(struct ieee80211_node_table * nt)325 wlan_refresh_inactive_nodes (struct ieee80211_node_table *nt)
326 {
327 #ifdef THREAD_X
328 bss_t *bss, *nextBss;
329 u8 myBssid[IEEE80211_ADDR_LEN], reArmTimer = false;
330
331 wmi_get_current_bssid(nt->nt_wmip, myBssid);
332
333 bss = nt->nt_node_first;
334 while (bss != NULL)
335 {
336 nextBss = bss->ni_list_next;
337 if (memcmp(myBssid, bss->ni_macaddr, sizeof(myBssid)) != 0)
338 {
339 /*
340 * free up all but the current bss - if set
341 */
342 wlan_node_reclaim(nt, bss);
343
344 }
345 bss = nextBss;
346 }
347 #else
348 bss_t *bss, *nextBss;
349 u8 myBssid[IEEE80211_ADDR_LEN];
350 u32 timeoutValue = 0;
351 u32 now = A_GET_MS(0);
352 timeoutValue = nt->nt_nodeAge;
353
354 wmi_get_current_bssid(nt->nt_wmip, myBssid);
355
356 bss = nt->nt_node_first;
357 while (bss != NULL)
358 {
359 nextBss = bss->ni_list_next;
360 if (memcmp(myBssid, bss->ni_macaddr, sizeof(myBssid)) != 0)
361 {
362
363 if (((now - bss->ni_tstamp) > timeoutValue) || --bss->ni_actcnt == 0)
364 {
365 /*
366 * free up all but the current bss - if set
367 */
368 wlan_node_reclaim(nt, bss);
369 }
370 }
371 bss = nextBss;
372 }
373 #endif
374 }
375
376 #ifdef THREAD_X
377 static void
wlan_node_timeout(A_ATH_TIMER arg)378 wlan_node_timeout (A_ATH_TIMER arg)
379 {
380 struct ieee80211_node_table *nt = (struct ieee80211_node_table *)arg;
381 bss_t *bss, *nextBss;
382 u8 myBssid[IEEE80211_ADDR_LEN], reArmTimer = false;
383 u32 timeoutValue = 0;
384 u32 now = A_GET_MS(0);
385
386 timeoutValue = nt->nt_nodeAge;
387
388 wmi_get_current_bssid(nt->nt_wmip, myBssid);
389
390 bss = nt->nt_node_first;
391 while (bss != NULL)
392 {
393 nextBss = bss->ni_list_next;
394 if (memcmp(myBssid, bss->ni_macaddr, sizeof(myBssid)) != 0)
395 {
396
397 if ((now - bss->ni_tstamp) > timeoutValue)
398 {
399 /*
400 * free up all but the current bss - if set
401 */
402 wlan_node_reclaim(nt, bss);
403 }
404 else
405 {
406 /*
407 * Re-arm timer, only when we have a bss other than
408 * current bss AND it is not aged-out.
409 */
410 reArmTimer = true;
411 }
412 }
413 bss = nextBss;
414 }
415
416 if (reArmTimer)
417 A_TIMEOUT_MS (&nt->nt_inact_timer, timeoutValue, 0);
418
419 nt->isTimerArmed = reArmTimer;
420 }
421 #endif
422
423 void
wlan_node_table_cleanup(struct ieee80211_node_table * nt)424 wlan_node_table_cleanup(struct ieee80211_node_table *nt)
425 {
426 #ifdef THREAD_X
427 A_UNTIMEOUT(&nt->nt_inact_timer);
428 A_DELETE_TIMER(&nt->nt_inact_timer);
429 #endif
430 wlan_free_allnodes(nt);
431 IEEE80211_NODE_LOCK_DESTROY(nt);
432 }
433
434 bss_t *
wlan_find_Ssidnode(struct ieee80211_node_table * nt,u8 * pSsid,u32 ssidLength,bool bIsWPA2,bool bMatchSSID)435 wlan_find_Ssidnode (struct ieee80211_node_table *nt, u8 *pSsid,
436 u32 ssidLength, bool bIsWPA2, bool bMatchSSID)
437 {
438 bss_t *ni = NULL;
439 u8 *pIESsid = NULL;
440
441 IEEE80211_NODE_LOCK (nt);
442
443 for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) {
444 pIESsid = ni->ni_cie.ie_ssid;
445 if (pIESsid[1] <= 32) {
446
447 // Step 1 : Check SSID
448 if (0x00 == memcmp (pSsid, &pIESsid[2], ssidLength)) {
449
450 //
451 // Step 2.1 : Check MatchSSID is true, if so, return Matched SSID
452 // Profile, otherwise check whether WPA2 or WPA
453 //
454 if (true == bMatchSSID) {
455 ieee80211_node_incref (ni); /* mark referenced */
456 IEEE80211_NODE_UNLOCK (nt);
457 return ni;
458 }
459
460 // Step 2 : if SSID matches, check WPA or WPA2
461 if (true == bIsWPA2 && NULL != ni->ni_cie.ie_rsn) {
462 ieee80211_node_incref (ni); /* mark referenced */
463 IEEE80211_NODE_UNLOCK (nt);
464 return ni;
465 }
466 if (false == bIsWPA2 && NULL != ni->ni_cie.ie_wpa) {
467 ieee80211_node_incref(ni); /* mark referenced */
468 IEEE80211_NODE_UNLOCK (nt);
469 return ni;
470 }
471 }
472 }
473 }
474
475 IEEE80211_NODE_UNLOCK (nt);
476
477 return NULL;
478 }
479
480 void
wlan_node_return(struct ieee80211_node_table * nt,bss_t * ni)481 wlan_node_return (struct ieee80211_node_table *nt, bss_t *ni)
482 {
483 IEEE80211_NODE_LOCK (nt);
484 wlan_node_dec_free (ni);
485 IEEE80211_NODE_UNLOCK (nt);
486 }
487
488 void
wlan_node_remove_core(struct ieee80211_node_table * nt,bss_t * ni)489 wlan_node_remove_core (struct ieee80211_node_table *nt, bss_t *ni)
490 {
491 if(ni->ni_list_prev == NULL)
492 {
493 /* First in list so fix the list head */
494 nt->nt_node_first = ni->ni_list_next;
495 }
496 else
497 {
498 ni->ni_list_prev->ni_list_next = ni->ni_list_next;
499 }
500
501 if(ni->ni_list_next == NULL)
502 {
503 /* Last in list so fix list tail */
504 nt->nt_node_last = ni->ni_list_prev;
505 }
506 else
507 {
508 ni->ni_list_next->ni_list_prev = ni->ni_list_prev;
509 }
510
511 if(ni->ni_hash_prev == NULL)
512 {
513 /* First in list so fix the list head */
514 int hash;
515 hash = IEEE80211_NODE_HASH(ni->ni_macaddr);
516 nt->nt_hash[hash] = ni->ni_hash_next;
517 }
518 else
519 {
520 ni->ni_hash_prev->ni_hash_next = ni->ni_hash_next;
521 }
522
523 if(ni->ni_hash_next != NULL)
524 {
525 ni->ni_hash_next->ni_hash_prev = ni->ni_hash_prev;
526 }
527 }
528
529 bss_t *
wlan_node_remove(struct ieee80211_node_table * nt,u8 * bssid)530 wlan_node_remove(struct ieee80211_node_table *nt, u8 *bssid)
531 {
532 bss_t *bss, *nextBss;
533
534 IEEE80211_NODE_LOCK(nt);
535
536 bss = nt->nt_node_first;
537
538 while (bss != NULL)
539 {
540 nextBss = bss->ni_list_next;
541
542 if (memcmp(bssid, bss->ni_macaddr, 6) == 0)
543 {
544 wlan_node_remove_core (nt, bss);
545 IEEE80211_NODE_UNLOCK(nt);
546 return bss;
547 }
548
549 bss = nextBss;
550 }
551
552 IEEE80211_NODE_UNLOCK(nt);
553 return NULL;
554 }
555
556 bss_t *
wlan_find_matching_Ssidnode(struct ieee80211_node_table * nt,u8 * pSsid,u32 ssidLength,u32 dot11AuthMode,u32 authMode,u32 pairwiseCryptoType,u32 grpwiseCryptoTyp)557 wlan_find_matching_Ssidnode (struct ieee80211_node_table *nt, u8 *pSsid,
558 u32 ssidLength, u32 dot11AuthMode, u32 authMode,
559 u32 pairwiseCryptoType, u32 grpwiseCryptoTyp)
560 {
561 bss_t *ni = NULL;
562 bss_t *best_ni = NULL;
563 u8 *pIESsid = NULL;
564
565 IEEE80211_NODE_LOCK (nt);
566
567 for (ni = nt->nt_node_first; ni; ni = ni->ni_list_next) {
568 pIESsid = ni->ni_cie.ie_ssid;
569 if (pIESsid[1] <= 32) {
570
571 // Step 1 : Check SSID
572 if (0x00 == memcmp (pSsid, &pIESsid[2], ssidLength)) {
573
574 if (ni->ni_cie.ie_capInfo & 0x10)
575 {
576
577 if ((NULL != ni->ni_cie.ie_rsn) && (WPA2_PSK_AUTH == authMode))
578 {
579 /* WPA2 */
580 if (NULL == best_ni)
581 {
582 best_ni = ni;
583 }
584 else if (ni->ni_rssi > best_ni->ni_rssi)
585 {
586 best_ni = ni;
587 }
588 }
589 else if ((NULL != ni->ni_cie.ie_wpa) && (WPA_PSK_AUTH == authMode))
590 {
591 /* WPA */
592 if (NULL == best_ni)
593 {
594 best_ni = ni;
595 }
596 else if (ni->ni_rssi > best_ni->ni_rssi)
597 {
598 best_ni = ni;
599 }
600 }
601 else if (WEP_CRYPT == pairwiseCryptoType)
602 {
603 /* WEP */
604 if (NULL == best_ni)
605 {
606 best_ni = ni;
607 }
608 else if (ni->ni_rssi > best_ni->ni_rssi)
609 {
610 best_ni = ni;
611 }
612 }
613 }
614 else
615 {
616 /* open AP */
617 if ((OPEN_AUTH == authMode) && (NONE_CRYPT == pairwiseCryptoType))
618 {
619 if (NULL == best_ni)
620 {
621 best_ni = ni;
622 }
623 else if (ni->ni_rssi > best_ni->ni_rssi)
624 {
625 best_ni = ni;
626 }
627 }
628 }
629 }
630 }
631 }
632
633 IEEE80211_NODE_UNLOCK (nt);
634
635 return best_ni;
636 }
637
638