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