13147Sxc151355 /* 28594SFei.Feng@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 33147Sxc151355 * Use is subject to license terms. 43147Sxc151355 */ 53147Sxc151355 63147Sxc151355 /* 73147Sxc151355 * Copyright (c) 2001 Atsushi Onoe 88594SFei.Feng@Sun.COM * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting 93147Sxc151355 * All rights reserved. 103147Sxc151355 * 113147Sxc151355 * Redistribution and use in source and binary forms, with or without 123147Sxc151355 * modification, are permitted provided that the following conditions 133147Sxc151355 * are met: 143147Sxc151355 * 1. Redistributions of source code must retain the above copyright 153147Sxc151355 * notice, this list of conditions and the following disclaimer. 163147Sxc151355 * 2. Redistributions in binary form must reproduce the above copyright 173147Sxc151355 * notice, this list of conditions and the following disclaimer in the 183147Sxc151355 * documentation and/or other materials provided with the distribution. 193147Sxc151355 * 3. The name of the author may not be used to endorse or promote products 203147Sxc151355 * derived from this software without specific prior written permission. 213147Sxc151355 * 223147Sxc151355 * Alternatively, this software may be distributed under the terms of the 233147Sxc151355 * GNU General Public License ("GPL") version 2 as published by the Free 243147Sxc151355 * Software Foundation. 253147Sxc151355 * 263147Sxc151355 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 273147Sxc151355 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 283147Sxc151355 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 293147Sxc151355 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 303147Sxc151355 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 313147Sxc151355 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 323147Sxc151355 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 333147Sxc151355 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 343147Sxc151355 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 353147Sxc151355 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 363147Sxc151355 */ 373147Sxc151355 383147Sxc151355 /* 393147Sxc151355 * Node management routines 403147Sxc151355 */ 413147Sxc151355 423147Sxc151355 #include "net80211_impl.h" 433147Sxc151355 443147Sxc151355 static ieee80211_node_t *ieee80211_node_alloc(ieee80211com_t *); 453147Sxc151355 static void ieee80211_node_cleanup(ieee80211_node_t *); 463147Sxc151355 static void ieee80211_node_free(ieee80211_node_t *); 473147Sxc151355 static uint8_t ieee80211_node_getrssi(const ieee80211_node_t *); 483147Sxc151355 static void ieee80211_setup_node(ieee80211com_t *, ieee80211_node_table_t *, 493147Sxc151355 ieee80211_node_t *, const uint8_t *); 503147Sxc151355 static void ieee80211_node_reclaim(ieee80211_node_table_t *, 513147Sxc151355 ieee80211_node_t *); 523147Sxc151355 static void ieee80211_free_node_locked(ieee80211_node_t *); 533147Sxc151355 static void ieee80211_free_allnodes(ieee80211_node_table_t *); 543147Sxc151355 static void ieee80211_node_leave(ieee80211com_t *, ieee80211_node_t *); 553147Sxc151355 static void ieee80211_timeout_scan_candidates(ieee80211_node_table_t *); 563147Sxc151355 static void ieee80211_timeout_stations(ieee80211_node_table_t *); 573147Sxc151355 static void ieee80211_node_table_init(ieee80211com_t *, 583147Sxc151355 ieee80211_node_table_t *, const char *, int, int, 593147Sxc151355 void (*timeout)(ieee80211_node_table_t *)); 603147Sxc151355 static void ieee80211_node_table_cleanup(ieee80211_node_table_t *); 613147Sxc151355 623147Sxc151355 /* 633147Sxc151355 * association failures before ignored 643147Sxc151355 * The failure may be caused by the response frame is lost for 653147Sxc151355 * environmental reason. So Try associate more than once before 663147Sxc151355 * ignore the node 673147Sxc151355 */ 683147Sxc151355 #define IEEE80211_STA_FAILS_MAX 2 693147Sxc151355 703147Sxc151355 /* 713147Sxc151355 * Initialize node database management callbacks for the interface. 723147Sxc151355 * This function is called by ieee80211_attach(). These callback 733147Sxc151355 * functions may be overridden in special circumstances, as long as 743147Sxc151355 * as this is done after calling ieee80211_attach() and prior to any 753147Sxc151355 * other call which may allocate a node 763147Sxc151355 */ 773147Sxc151355 void 783147Sxc151355 ieee80211_node_attach(ieee80211com_t *ic) 793147Sxc151355 { 803147Sxc151355 struct ieee80211_impl *im = ic->ic_private; 813147Sxc151355 823147Sxc151355 ic->ic_node_alloc = ieee80211_node_alloc; 833147Sxc151355 ic->ic_node_free = ieee80211_node_free; 843147Sxc151355 ic->ic_node_cleanup = ieee80211_node_cleanup; 853147Sxc151355 ic->ic_node_getrssi = ieee80211_node_getrssi; 863147Sxc151355 873147Sxc151355 /* default station inactivity timer setings */ 883147Sxc151355 im->im_inact_init = IEEE80211_INACT_INIT; 893147Sxc151355 im->im_inact_assoc = IEEE80211_INACT_ASSOC; 903147Sxc151355 im->im_inact_run = IEEE80211_INACT_RUN; 913147Sxc151355 im->im_inact_probe = IEEE80211_INACT_PROBE; 923147Sxc151355 } 933147Sxc151355 943147Sxc151355 /* 953147Sxc151355 * Initialize node databases and the ic_bss node element. 963147Sxc151355 */ 973147Sxc151355 void 983147Sxc151355 ieee80211_node_lateattach(ieee80211com_t *ic) 993147Sxc151355 { 1003147Sxc151355 /* 1013147Sxc151355 * Calculate ic_tim_bitmap size in bytes 1023147Sxc151355 * IEEE80211_AID_MAX defines maximum bits in ic_tim_bitmap 1033147Sxc151355 */ 1043147Sxc151355 ic->ic_tim_len = howmany(IEEE80211_AID_MAX, 8) * sizeof (uint8_t); 1053147Sxc151355 1063147Sxc151355 ieee80211_node_table_init(ic, &ic->ic_sta, "station", 1074499Shx147065 IEEE80211_INACT_INIT, IEEE80211_WEP_NKID, 1084499Shx147065 ieee80211_timeout_stations); 1093147Sxc151355 ieee80211_node_table_init(ic, &ic->ic_scan, "scan", 1104499Shx147065 IEEE80211_INACT_SCAN, 0, ieee80211_timeout_scan_candidates); 1113147Sxc151355 1123147Sxc151355 ieee80211_reset_bss(ic); 1133147Sxc151355 } 1143147Sxc151355 1153147Sxc151355 /* 1163147Sxc151355 * Destroy all node databases and is usually called during device detach 1173147Sxc151355 */ 1183147Sxc151355 void 1193147Sxc151355 ieee80211_node_detach(ieee80211com_t *ic) 1203147Sxc151355 { 1213147Sxc151355 /* Node Detach */ 1223147Sxc151355 if (ic->ic_bss != NULL) { 1233147Sxc151355 ieee80211_free_node(ic->ic_bss); 1243147Sxc151355 ic->ic_bss = NULL; 1253147Sxc151355 } 1263147Sxc151355 ieee80211_node_table_cleanup(&ic->ic_scan); 1273147Sxc151355 ieee80211_node_table_cleanup(&ic->ic_sta); 1283147Sxc151355 } 1293147Sxc151355 1303147Sxc151355 /* 1313147Sxc151355 * Increase a node's reference count 1323147Sxc151355 * 1333147Sxc151355 * Return pointer to the node 1343147Sxc151355 */ 1353147Sxc151355 ieee80211_node_t * 1363147Sxc151355 ieee80211_ref_node(ieee80211_node_t *in) 1373147Sxc151355 { 1383147Sxc151355 ieee80211_node_incref(in); 1393147Sxc151355 return (in); 1403147Sxc151355 } 1413147Sxc151355 1423147Sxc151355 /* 1433147Sxc151355 * Dexrease a node's reference count 1443147Sxc151355 */ 1453147Sxc151355 void 1463147Sxc151355 ieee80211_unref_node(ieee80211_node_t **in) 1473147Sxc151355 { 1483147Sxc151355 ieee80211_node_decref(*in); 1493147Sxc151355 *in = NULL; /* guard against use */ 1503147Sxc151355 } 1513147Sxc151355 1523147Sxc151355 /* 1533147Sxc151355 * Mark ports authorized for data traffic. This function is usually 1543147Sxc151355 * used by 802.1x authenticator. 1553147Sxc151355 */ 1563147Sxc151355 void 1573147Sxc151355 ieee80211_node_authorize(ieee80211_node_t *in) 1583147Sxc151355 { 1593147Sxc151355 ieee80211_impl_t *im = in->in_ic->ic_private; 1603147Sxc151355 1613147Sxc151355 in->in_flags |= IEEE80211_NODE_AUTH; 1623147Sxc151355 in->in_inact_reload = im->im_inact_run; 1638594SFei.Feng@Sun.COM in->in_inact = in->in_inact_reload; 1643147Sxc151355 } 1653147Sxc151355 1663147Sxc151355 /* 1673147Sxc151355 * Mark ports unauthorized for data traffic. This function is usually 1683147Sxc151355 * used by 802.1x authenticator. 1693147Sxc151355 */ 1703147Sxc151355 void 1713147Sxc151355 ieee80211_node_unauthorize(ieee80211_node_t *in) 1723147Sxc151355 { 1733147Sxc151355 in->in_flags &= ~IEEE80211_NODE_AUTH; 1743147Sxc151355 } 1753147Sxc151355 1763147Sxc151355 /* 1773147Sxc151355 * Set/change the channel. The rate set is also updated as 1783147Sxc151355 * to insure a consistent view by drivers. 1793147Sxc151355 */ 1803147Sxc151355 static void 1813147Sxc151355 ieee80211_node_setchan(ieee80211com_t *ic, ieee80211_node_t *in, 1823147Sxc151355 struct ieee80211_channel *chan) 1833147Sxc151355 { 1843147Sxc151355 if (chan == IEEE80211_CHAN_ANYC) 1853147Sxc151355 chan = ic->ic_curchan; 1863147Sxc151355 in->in_chan = chan; 187*10266SQuaker.Fang@Sun.COM if (IEEE80211_IS_CHAN_HT(chan)) { 188*10266SQuaker.Fang@Sun.COM /* 189*10266SQuaker.Fang@Sun.COM * Gotta be careful here; the rate set returned by 190*10266SQuaker.Fang@Sun.COM * ieee80211_get_suprates is actually any HT rate 191*10266SQuaker.Fang@Sun.COM * set so blindly copying it will be bad. We must 192*10266SQuaker.Fang@Sun.COM * install the legacy rate est in ni_rates and the 193*10266SQuaker.Fang@Sun.COM * HT rate set in ni_htrates. 194*10266SQuaker.Fang@Sun.COM */ 195*10266SQuaker.Fang@Sun.COM in->in_htrates = *ieee80211_get_suphtrates(ic, chan); 196*10266SQuaker.Fang@Sun.COM } 197*10266SQuaker.Fang@Sun.COM in->in_rates = *ieee80211_get_suprates(ic, chan); 198*10266SQuaker.Fang@Sun.COM /* in->in_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)]; */ 1993147Sxc151355 } 2003147Sxc151355 2013147Sxc151355 /* 2023147Sxc151355 * Initialize the channel set to scan based on the available channels 2033147Sxc151355 * and the current PHY mode. 2043147Sxc151355 */ 2053147Sxc151355 static void 2063147Sxc151355 ieee80211_reset_scan(ieee80211com_t *ic) 2073147Sxc151355 { 2083147Sxc151355 ieee80211_impl_t *im = ic->ic_private; 2093147Sxc151355 2103147Sxc151355 if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) { 2113147Sxc151355 (void) memset(im->im_chan_scan, 0, sizeof (im->im_chan_scan)); 2123147Sxc151355 ieee80211_setbit(im->im_chan_scan, 2134499Shx147065 ieee80211_chan2ieee(ic, ic->ic_des_chan)); 2143147Sxc151355 } else { 2153147Sxc151355 bcopy(ic->ic_chan_active, im->im_chan_scan, 2164499Shx147065 sizeof (ic->ic_chan_active)); 2173147Sxc151355 } 2183147Sxc151355 ieee80211_dbg(IEEE80211_MSG_SCAN, "ieee80211_reset_scan(): " 2194499Shx147065 "start chan %u\n", ieee80211_chan2ieee(ic, ic->ic_curchan)); 2203147Sxc151355 } 2213147Sxc151355 2223147Sxc151355 /* 2233147Sxc151355 * Begin an active scan. Initialize the node cache. The scan 2243147Sxc151355 * begins on the next radio channel by calling ieee80211_next_scan(). 2253147Sxc151355 * The actual scanning is not automated. The driver itself 2263147Sxc151355 * only handles setting the radio frequency and stepping through 2273147Sxc151355 * the channels. 2283147Sxc151355 */ 2293147Sxc151355 void 2303147Sxc151355 ieee80211_begin_scan(ieee80211com_t *ic, boolean_t reset) 2313147Sxc151355 { 2323147Sxc151355 IEEE80211_LOCK(ic); 2333147Sxc151355 2343147Sxc151355 if (ic->ic_opmode != IEEE80211_M_HOSTAP) 2353147Sxc151355 ic->ic_flags |= IEEE80211_F_ASCAN; 2363147Sxc151355 ieee80211_dbg(IEEE80211_MSG_SCAN, 2374499Shx147065 "begin %s scan in %s mode on channel %u\n", 2384499Shx147065 (ic->ic_flags & IEEE80211_F_ASCAN) ? "active" : "passive", 2394499Shx147065 ieee80211_phymode_name[ic->ic_curmode], 2404499Shx147065 ieee80211_chan2ieee(ic, ic->ic_curchan)); 2413147Sxc151355 2423147Sxc151355 /* 2433147Sxc151355 * Clear scan state and flush any previously seen AP's. 2443147Sxc151355 */ 2453147Sxc151355 ieee80211_reset_scan(ic); 2463147Sxc151355 if (reset) 2473147Sxc151355 ieee80211_free_allnodes(&ic->ic_scan); 2483147Sxc151355 2493147Sxc151355 ic->ic_flags |= IEEE80211_F_SCAN; 2503147Sxc151355 IEEE80211_UNLOCK(ic); 2513147Sxc151355 2523147Sxc151355 /* Scan the next channel. */ 2533147Sxc151355 ieee80211_next_scan(ic); 2543147Sxc151355 } 2553147Sxc151355 2563147Sxc151355 /* 2573147Sxc151355 * Switch to the next channel marked for scanning. 2583147Sxc151355 * A driver is expected to first call ieee80211_begin_scan(), 2593147Sxc151355 * to initialize the node cache, then set the radio channel 2603147Sxc151355 * on the device. And then after a certain time has elapsed, 2613147Sxc151355 * call ieee80211_next_scan() to move to the next channel. 2623147Sxc151355 * Typically, a timeout routine is used to automate this process. 2633147Sxc151355 */ 2643147Sxc151355 void 2653147Sxc151355 ieee80211_next_scan(ieee80211com_t *ic) 2663147Sxc151355 { 2673147Sxc151355 ieee80211_impl_t *im = ic->ic_private; 2683147Sxc151355 struct ieee80211_channel *chan; 2693147Sxc151355 2703147Sxc151355 IEEE80211_LOCK(ic); 2713147Sxc151355 /* 2723147Sxc151355 * Insure any previous mgt frame timeouts don't fire. 2733147Sxc151355 * This assumes the driver does the right thing in 2743147Sxc151355 * flushing anything queued in the driver and below. 2753147Sxc151355 */ 2763147Sxc151355 im->im_mgt_timer = 0; 2773147Sxc151355 2783147Sxc151355 chan = ic->ic_curchan; 2793147Sxc151355 do { 2803147Sxc151355 if (++chan > &ic->ic_sup_channels[IEEE80211_CHAN_MAX]) 2813147Sxc151355 chan = &ic->ic_sup_channels[0]; 2823147Sxc151355 if (ieee80211_isset(im->im_chan_scan, 2833147Sxc151355 ieee80211_chan2ieee(ic, chan))) { 2843147Sxc151355 ieee80211_clrbit(im->im_chan_scan, 2854499Shx147065 ieee80211_chan2ieee(ic, chan)); 2863147Sxc151355 ieee80211_dbg(IEEE80211_MSG_SCAN, 2874499Shx147065 "ieee80211_next_scan: chan %d->%d\n", 2884499Shx147065 ieee80211_chan2ieee(ic, ic->ic_curchan), 2894499Shx147065 ieee80211_chan2ieee(ic, chan)); 2903147Sxc151355 ic->ic_curchan = chan; 2913147Sxc151355 /* 2923147Sxc151355 * drivers should do this as needed, 2933147Sxc151355 * for now maintain compatibility 2943147Sxc151355 */ 2953147Sxc151355 ic->ic_bss->in_rates = 2964499Shx147065 ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)]; 2973147Sxc151355 IEEE80211_UNLOCK(ic); 2983147Sxc151355 ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 2993147Sxc151355 return; 3003147Sxc151355 } 3013147Sxc151355 } while (chan != ic->ic_curchan); 3023147Sxc151355 IEEE80211_UNLOCK(ic); 3033147Sxc151355 ieee80211_end_scan(ic); 3043147Sxc151355 } 3053147Sxc151355 3063147Sxc151355 /* 3073147Sxc151355 * Copy useful state from node obss into nbss. 3083147Sxc151355 */ 3093147Sxc151355 static void 3103147Sxc151355 ieee80211_copy_bss(ieee80211_node_t *nbss, const ieee80211_node_t *obss) 3113147Sxc151355 { 3123147Sxc151355 /* propagate useful state */ 3133147Sxc151355 nbss->in_authmode = obss->in_authmode; 3143147Sxc151355 nbss->in_txpower = obss->in_txpower; 3153147Sxc151355 nbss->in_vlan = obss->in_vlan; 3163147Sxc151355 } 3173147Sxc151355 3183147Sxc151355 /* 3193147Sxc151355 * Setup the net80211 specific portion of an interface's softc, ic, 3203147Sxc151355 * for use in IBSS mode 3213147Sxc151355 */ 3223147Sxc151355 void 3233147Sxc151355 ieee80211_create_ibss(ieee80211com_t *ic, struct ieee80211_channel *chan) 3243147Sxc151355 { 3253147Sxc151355 ieee80211_impl_t *im = ic->ic_private; 3263147Sxc151355 ieee80211_node_table_t *nt; 3273147Sxc151355 ieee80211_node_t *in; 3283147Sxc151355 3293147Sxc151355 IEEE80211_LOCK_ASSERT(ic); 3303147Sxc151355 ieee80211_dbg(IEEE80211_MSG_SCAN, "ieee80211_create_ibss: " 3314499Shx147065 "creating ibss\n"); 3323147Sxc151355 3333147Sxc151355 /* 3343147Sxc151355 * Create the station/neighbor table. Note that for adhoc 3353147Sxc151355 * mode we make the initial inactivity timer longer since 3363147Sxc151355 * we create nodes only through discovery and they typically 3373147Sxc151355 * are long-lived associations. 3383147Sxc151355 */ 3393147Sxc151355 nt = &ic->ic_sta; 3403147Sxc151355 IEEE80211_NODE_LOCK(nt); 3413147Sxc151355 nt->nt_name = "neighbor"; 3423147Sxc151355 nt->nt_inact_init = im->im_inact_run; 3433147Sxc151355 IEEE80211_NODE_UNLOCK(nt); 3443147Sxc151355 3453147Sxc151355 in = ieee80211_alloc_node(ic, &ic->ic_sta, ic->ic_macaddr); 3463147Sxc151355 if (in == NULL) { 3473147Sxc151355 ieee80211_err("ieee80211_create_ibss(): alloc node failed\n"); 3483147Sxc151355 return; 3493147Sxc151355 } 3503147Sxc151355 IEEE80211_ADDR_COPY(in->in_bssid, ic->ic_macaddr); 3513147Sxc151355 in->in_esslen = ic->ic_des_esslen; 3523147Sxc151355 (void) memcpy(in->in_essid, ic->ic_des_essid, in->in_esslen); 3533147Sxc151355 ieee80211_copy_bss(in, ic->ic_bss); 3543147Sxc151355 in->in_intval = ic->ic_bintval; 3553147Sxc151355 if (ic->ic_flags & IEEE80211_F_PRIVACY) 3563147Sxc151355 in->in_capinfo |= IEEE80211_CAPINFO_PRIVACY; 3573147Sxc151355 if (ic->ic_phytype == IEEE80211_T_FH) { 3583147Sxc151355 in->in_fhdwell = 200; 3593147Sxc151355 in->in_fhindex = 1; 3603147Sxc151355 } 3613147Sxc151355 switch (ic->ic_opmode) { 3623147Sxc151355 case IEEE80211_M_IBSS: 3633147Sxc151355 ic->ic_flags |= IEEE80211_F_SIBSS; 3643147Sxc151355 in->in_capinfo |= IEEE80211_CAPINFO_IBSS; 3653147Sxc151355 if (ic->ic_flags & IEEE80211_F_DESBSSID) 3663147Sxc151355 IEEE80211_ADDR_COPY(in->in_bssid, ic->ic_des_bssid); 3673147Sxc151355 else 3683147Sxc151355 in->in_bssid[0] |= 0x02; /* local bit for IBSS */ 3693147Sxc151355 break; 3703147Sxc151355 case IEEE80211_M_AHDEMO: 3713147Sxc151355 if (ic->ic_flags & IEEE80211_F_DESBSSID) 3723147Sxc151355 IEEE80211_ADDR_COPY(in->in_bssid, ic->ic_des_bssid); 3733147Sxc151355 else 3743147Sxc151355 (void) memset(in->in_bssid, 0, IEEE80211_ADDR_LEN); 3753147Sxc151355 break; 3763147Sxc151355 default: 3773147Sxc151355 ieee80211_err("ieee80211_create_ibss(): " 3784499Shx147065 "wrong opmode %u to creat IBSS, abort\n", 3794499Shx147065 ic->ic_opmode); 3803147Sxc151355 ieee80211_free_node(in); 3813147Sxc151355 return; 3823147Sxc151355 } 3833147Sxc151355 3843147Sxc151355 /* 3853147Sxc151355 * Fix the channel and related attributes. 3863147Sxc151355 */ 3873147Sxc151355 ieee80211_node_setchan(ic, in, chan); 3883147Sxc151355 ic->ic_curchan = chan; 3893147Sxc151355 ic->ic_curmode = ieee80211_chan2mode(ic, chan); 3903147Sxc151355 /* 3913147Sxc151355 * Do mode-specific rate setup. 3923147Sxc151355 */ 3933147Sxc151355 ieee80211_setbasicrates(&in->in_rates, ic->ic_curmode); 3943147Sxc151355 IEEE80211_UNLOCK(ic); 3954126Szf162725 ieee80211_sta_join(ic, ieee80211_ref_node(in)); 3963147Sxc151355 IEEE80211_LOCK(ic); 3973147Sxc151355 } 3983147Sxc151355 3993147Sxc151355 void 4003147Sxc151355 ieee80211_reset_bss(ieee80211com_t *ic) 4013147Sxc151355 { 4023147Sxc151355 ieee80211_node_t *in; 4033147Sxc151355 ieee80211_node_t *obss; 4043147Sxc151355 4058594SFei.Feng@Sun.COM ieee80211_node_table_reset(&ic->ic_sta); 4068594SFei.Feng@Sun.COM ieee80211_reset_erp(ic); 4078594SFei.Feng@Sun.COM 4083147Sxc151355 in = ieee80211_alloc_node(ic, &ic->ic_scan, ic->ic_macaddr); 4093147Sxc151355 ASSERT(in != NULL); 4103147Sxc151355 obss = ic->ic_bss; 4113147Sxc151355 ic->ic_bss = ieee80211_ref_node(in); 4123147Sxc151355 if (obss != NULL) { 4133147Sxc151355 ieee80211_copy_bss(in, obss); 4143147Sxc151355 in->in_intval = ic->ic_bintval; 4153147Sxc151355 ieee80211_free_node(obss); 4163147Sxc151355 } 4173147Sxc151355 } 4183147Sxc151355 4193147Sxc151355 static int 4203147Sxc151355 ieee80211_match_bss(ieee80211com_t *ic, ieee80211_node_t *in) 4213147Sxc151355 { 4223147Sxc151355 uint8_t rate; 4233147Sxc151355 int fail; 4243147Sxc151355 4253147Sxc151355 fail = 0; 4263147Sxc151355 if (ieee80211_isclr(ic->ic_chan_active, 4273147Sxc151355 ieee80211_chan2ieee(ic, in->in_chan))) { 4283147Sxc151355 fail |= IEEE80211_BADCHAN; 4293147Sxc151355 } 4303147Sxc151355 if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && 4313147Sxc151355 in->in_chan != ic->ic_des_chan) { 4323147Sxc151355 fail |= IEEE80211_BADCHAN; 4333147Sxc151355 } 4343147Sxc151355 if (ic->ic_opmode == IEEE80211_M_IBSS) { 4353147Sxc151355 if (!(in->in_capinfo & IEEE80211_CAPINFO_IBSS)) 4363147Sxc151355 fail |= IEEE80211_BADOPMODE; 4373147Sxc151355 } else { 4383147Sxc151355 if (!(in->in_capinfo & IEEE80211_CAPINFO_ESS)) 4393147Sxc151355 fail |= IEEE80211_BADOPMODE; 4403147Sxc151355 } 4413147Sxc151355 if (ic->ic_flags & IEEE80211_F_PRIVACY) { 4423147Sxc151355 if (!(in->in_capinfo & IEEE80211_CAPINFO_PRIVACY)) 4433147Sxc151355 fail |= IEEE80211_BADPRIVACY; 4443147Sxc151355 } else { 4453147Sxc151355 if (in->in_capinfo & IEEE80211_CAPINFO_PRIVACY) 4463147Sxc151355 fail |= IEEE80211_BADPRIVACY; 4473147Sxc151355 } 448*10266SQuaker.Fang@Sun.COM rate = ieee80211_fix_rate(in, &in->in_rates, 449*10266SQuaker.Fang@Sun.COM IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE); 4503147Sxc151355 if (rate & IEEE80211_RATE_BASIC) 4513147Sxc151355 fail |= IEEE80211_BADRATE; 4523147Sxc151355 if (ic->ic_des_esslen != 0 && 4533147Sxc151355 (in->in_esslen != ic->ic_des_esslen || 4543147Sxc151355 memcmp(in->in_essid, ic->ic_des_essid, ic->ic_des_esslen) != 0)) { 4553147Sxc151355 fail |= IEEE80211_BADESSID; 4563147Sxc151355 } 4573147Sxc151355 if ((ic->ic_flags & IEEE80211_F_DESBSSID) && 4583147Sxc151355 !IEEE80211_ADDR_EQ(ic->ic_des_bssid, in->in_bssid)) { 4593147Sxc151355 fail |= IEEE80211_BADBSSID; 4603147Sxc151355 } 4613147Sxc151355 if (in->in_fails >= IEEE80211_STA_FAILS_MAX) 4623147Sxc151355 fail |= IEEE80211_NODEFAIL; 4633147Sxc151355 4643147Sxc151355 return (fail); 4653147Sxc151355 } 4663147Sxc151355 4673147Sxc151355 #define IEEE80211_MAXRATE(_rs) \ 4683147Sxc151355 ((_rs).ir_rates[(_rs).ir_nrates - 1] & IEEE80211_RATE_VAL) 4693147Sxc151355 4703147Sxc151355 /* 4713147Sxc151355 * Compare the capabilities of node a with node b and decide which is 4723147Sxc151355 * more desirable (return b if b is considered better than a). Note 4733147Sxc151355 * that we assume compatibility/usability has already been checked 4743147Sxc151355 * so we don't need to (e.g. validate whether privacy is supported). 4753147Sxc151355 * Used to select the best scan candidate for association in a BSS. 4763147Sxc151355 * 4773147Sxc151355 * Return desired node 4783147Sxc151355 */ 4793147Sxc151355 static ieee80211_node_t * 4803147Sxc151355 ieee80211_node_compare(ieee80211com_t *ic, ieee80211_node_t *a, 4813147Sxc151355 ieee80211_node_t *b) 4823147Sxc151355 { 4833147Sxc151355 uint8_t maxa; 4843147Sxc151355 uint8_t maxb; 4853147Sxc151355 uint8_t rssia; 4863147Sxc151355 uint8_t rssib; 4873147Sxc151355 4883147Sxc151355 /* privacy support preferred */ 4893147Sxc151355 if ((a->in_capinfo & IEEE80211_CAPINFO_PRIVACY) && 4903147Sxc151355 !(b->in_capinfo & IEEE80211_CAPINFO_PRIVACY)) { 4913147Sxc151355 return (a); 4923147Sxc151355 } 4933147Sxc151355 if (!(a->in_capinfo & IEEE80211_CAPINFO_PRIVACY) && 4943147Sxc151355 (b->in_capinfo & IEEE80211_CAPINFO_PRIVACY)) { 4953147Sxc151355 return (b); 4963147Sxc151355 } 4973147Sxc151355 4983147Sxc151355 /* compare count of previous failures */ 4993147Sxc151355 if (b->in_fails != a->in_fails) 5003147Sxc151355 return ((a->in_fails > b->in_fails) ? b : a); 5013147Sxc151355 5023147Sxc151355 rssia = ic->ic_node_getrssi(a); 5033147Sxc151355 rssib = ic->ic_node_getrssi(b); 5043147Sxc151355 if (ABS(rssib - rssia) < IEEE80211_RSSI_CMP_THRESHOLD) { 5053147Sxc151355 /* best/max rate preferred if signal level close enough */ 5063147Sxc151355 maxa = IEEE80211_MAXRATE(a->in_rates); 5073147Sxc151355 maxb = IEEE80211_MAXRATE(b->in_rates); 5083147Sxc151355 if (maxa != maxb) 5093147Sxc151355 return ((maxb > maxa) ? b : a); 5103147Sxc151355 /* for now just prefer 5Ghz band to all other bands */ 5113147Sxc151355 if (IEEE80211_IS_CHAN_5GHZ(a->in_chan) && 5123147Sxc151355 !IEEE80211_IS_CHAN_5GHZ(b->in_chan)) { 5133147Sxc151355 return (a); 5143147Sxc151355 } 5153147Sxc151355 if (!IEEE80211_IS_CHAN_5GHZ(a->in_chan) && 5163147Sxc151355 IEEE80211_IS_CHAN_5GHZ(b->in_chan)) { 5173147Sxc151355 return (b); 5183147Sxc151355 } 5193147Sxc151355 } 5203147Sxc151355 /* all things being equal, compare signal level */ 5213147Sxc151355 return ((rssib > rssia) ? b : a); 5223147Sxc151355 } 5233147Sxc151355 5243147Sxc151355 /* 5253147Sxc151355 * Mark an ongoing scan stopped. 5263147Sxc151355 */ 5273147Sxc151355 void 5283147Sxc151355 ieee80211_cancel_scan(ieee80211com_t *ic) 5293147Sxc151355 { 5303147Sxc151355 IEEE80211_LOCK(ic); 5313147Sxc151355 ieee80211_dbg(IEEE80211_MSG_SCAN, "ieee80211_cancel_scan()" 5324499Shx147065 "end %s scan\n", 5334499Shx147065 (ic->ic_flags & IEEE80211_F_ASCAN) ? "active" : "passive"); 5343147Sxc151355 ic->ic_flags &= ~(IEEE80211_F_SCAN | IEEE80211_F_ASCAN); 5353147Sxc151355 cv_broadcast(&((ieee80211_impl_t *)ic->ic_private)->im_scan_cv); 5363147Sxc151355 IEEE80211_UNLOCK(ic); 5373147Sxc151355 } 5383147Sxc151355 5393147Sxc151355 /* 5403147Sxc151355 * Complete a scan of potential channels. It is called by 5413147Sxc151355 * ieee80211_next_scan() when the state machine has performed 5423147Sxc151355 * a full cycle of scaning on all available radio channels. 5433147Sxc151355 * ieee80211_end_scan() will inspect the node cache for suitable 5443147Sxc151355 * APs found during scaning, and associate with one, should 5453147Sxc151355 * the parameters of the node match those of the configuration 5463147Sxc151355 * requested from userland. 5473147Sxc151355 */ 5483147Sxc151355 void 5493147Sxc151355 ieee80211_end_scan(ieee80211com_t *ic) 5503147Sxc151355 { 5513147Sxc151355 ieee80211_node_table_t *nt = &ic->ic_scan; 5523147Sxc151355 ieee80211_node_t *in; 5533147Sxc151355 ieee80211_node_t *selbs; 5543147Sxc151355 5553147Sxc151355 ieee80211_cancel_scan(ic); 5564126Szf162725 /* notify SCAN done */ 5574126Szf162725 ieee80211_notify(ic, EVENT_SCAN_RESULTS); 5583147Sxc151355 IEEE80211_LOCK(ic); 5593147Sxc151355 5603147Sxc151355 /* 5613147Sxc151355 * Automatic sequencing; look for a candidate and 5623147Sxc151355 * if found join the network. 5633147Sxc151355 */ 5643147Sxc151355 /* NB: unlocked read should be ok */ 5653147Sxc151355 in = list_head(&nt->nt_node); 5664126Szf162725 if (in == NULL && (ic->ic_flags & IEEE80211_F_WPA) == 0) { 5673147Sxc151355 ieee80211_dbg(IEEE80211_MSG_SCAN, "ieee80211_end_scan: " 5684499Shx147065 "no scan candidate\n"); 5693147Sxc151355 notfound: 5703147Sxc151355 if (ic->ic_opmode == IEEE80211_M_IBSS && 5713147Sxc151355 (ic->ic_flags & IEEE80211_F_IBSSON) && 5723147Sxc151355 ic->ic_des_esslen != 0) { 5733147Sxc151355 ieee80211_create_ibss(ic, ic->ic_ibss_chan); 5743147Sxc151355 IEEE80211_UNLOCK(ic); 5753147Sxc151355 return; 5763147Sxc151355 } 5773147Sxc151355 5783147Sxc151355 /* 5793147Sxc151355 * Reset the list of channels to scan and start again. 5803147Sxc151355 */ 5813147Sxc151355 ieee80211_reset_scan(ic); 5823147Sxc151355 ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN; 5833147Sxc151355 IEEE80211_UNLOCK(ic); 5843147Sxc151355 5853147Sxc151355 ieee80211_next_scan(ic); 5863147Sxc151355 return; 5873147Sxc151355 } 5883147Sxc151355 5894126Szf162725 if (ic->ic_flags & IEEE80211_F_SCANONLY || 5904126Szf162725 ic->ic_flags & IEEE80211_F_WPA) { /* scan only */ 5913147Sxc151355 ic->ic_flags &= ~IEEE80211_F_SCANONLY; 5923147Sxc151355 IEEE80211_UNLOCK(ic); 5933147Sxc151355 ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 5943147Sxc151355 return; 5953147Sxc151355 } 5963147Sxc151355 5973147Sxc151355 selbs = NULL; 5983147Sxc151355 IEEE80211_NODE_LOCK(nt); 5993147Sxc151355 while (in != NULL) { 6003147Sxc151355 if (in->in_fails >= IEEE80211_STA_FAILS_MAX) { 6013147Sxc151355 ieee80211_node_t *tmpin = in; 6023147Sxc151355 6033147Sxc151355 /* 6043147Sxc151355 * The configuration of the access points may change 6053147Sxc151355 * during my scan. So delete the entry for the AP 6063147Sxc151355 * and retry to associate if there is another beacon. 6073147Sxc151355 */ 6083147Sxc151355 in = list_next(&nt->nt_node, tmpin); 6093147Sxc151355 ieee80211_node_reclaim(nt, tmpin); 6103147Sxc151355 continue; 6113147Sxc151355 } 6123993Seh146360 /* 6133993Seh146360 * It's possible at some special moments, the in_chan will 6143993Seh146360 * be none. Need to skip the null node. 6153993Seh146360 */ 6163993Seh146360 if (in->in_chan == IEEE80211_CHAN_ANYC) { 6173993Seh146360 in = list_next(&nt->nt_node, in); 6183993Seh146360 continue; 6193993Seh146360 } 6203147Sxc151355 if (ieee80211_match_bss(ic, in) == 0) { 6213147Sxc151355 if (selbs == NULL) 6223147Sxc151355 selbs = in; 6233147Sxc151355 else 6243147Sxc151355 selbs = ieee80211_node_compare(ic, selbs, in); 6253147Sxc151355 } 6263147Sxc151355 in = list_next(&nt->nt_node, in); 6273147Sxc151355 } 6284126Szf162725 if (selbs != NULL) /* grab ref while dropping lock */ 6294126Szf162725 (void) ieee80211_ref_node(selbs); 6303147Sxc151355 IEEE80211_NODE_UNLOCK(nt); 6313147Sxc151355 if (selbs == NULL) 6323147Sxc151355 goto notfound; 6333147Sxc151355 IEEE80211_UNLOCK(ic); 6343147Sxc151355 ieee80211_sta_join(ic, selbs); 6353147Sxc151355 } 6363147Sxc151355 6373147Sxc151355 6383147Sxc151355 /* 6393147Sxc151355 * Handle 802.11 ad hoc network merge. The convention, set by the 6403147Sxc151355 * Wireless Ethernet Compatibility Alliance (WECA), is that an 802.11 6413147Sxc151355 * station will change its BSSID to match the "oldest" 802.11 ad hoc 6423147Sxc151355 * network, on the same channel, that has the station's desired SSID. 6433147Sxc151355 * The "oldest" 802.11 network sends beacons with the greatest TSF 6443147Sxc151355 * timestamp. 6453147Sxc151355 * The caller is assumed to validate TSF's before attempting a merge. 6463147Sxc151355 * 6473147Sxc151355 * Return B_TRUE if the BSSID changed, B_FALSE otherwise. 6483147Sxc151355 */ 6493147Sxc151355 boolean_t 6503147Sxc151355 ieee80211_ibss_merge(ieee80211_node_t *in) 6513147Sxc151355 { 6523147Sxc151355 ieee80211com_t *ic = in->in_ic; 6533147Sxc151355 6543147Sxc151355 if (in == ic->ic_bss || 6553147Sxc151355 IEEE80211_ADDR_EQ(in->in_bssid, ic->ic_bss->in_bssid)) { 6563147Sxc151355 /* unchanged, nothing to do */ 6573147Sxc151355 return (B_FALSE); 6583147Sxc151355 } 6593147Sxc151355 if (ieee80211_match_bss(ic, in) != 0) { /* capabilities mismatch */ 6603147Sxc151355 ieee80211_dbg(IEEE80211_MSG_ASSOC, "ieee80211_ibss_merge: " 6614499Shx147065 " merge failed, capabilities mismatch\n"); 6623147Sxc151355 return (B_FALSE); 6633147Sxc151355 } 6643147Sxc151355 ieee80211_dbg(IEEE80211_MSG_ASSOC, "ieee80211_ibss_merge: " 6654499Shx147065 "new bssid %s: %s preamble, %s slot time%s\n", 6664499Shx147065 ieee80211_macaddr_sprintf(in->in_bssid), 6674499Shx147065 (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ? "short" : "long", 6684499Shx147065 (ic->ic_flags & IEEE80211_F_SHSLOT) ? "short" : "long", 6694499Shx147065 (ic->ic_flags&IEEE80211_F_USEPROT) ? ", protection" : ""); 6704126Szf162725 ieee80211_sta_join(ic, ieee80211_ref_node(in)); 6713147Sxc151355 return (B_TRUE); 6723147Sxc151355 } 6733147Sxc151355 6743147Sxc151355 /* 675*10266SQuaker.Fang@Sun.COM * Change the bss channel. 676*10266SQuaker.Fang@Sun.COM */ 677*10266SQuaker.Fang@Sun.COM void 678*10266SQuaker.Fang@Sun.COM ieee80211_setcurchan(ieee80211com_t *ic, struct ieee80211_channel *c) 679*10266SQuaker.Fang@Sun.COM { 680*10266SQuaker.Fang@Sun.COM ic->ic_curchan = c; 681*10266SQuaker.Fang@Sun.COM ic->ic_curmode = ieee80211_chan2mode(ic, ic->ic_curchan); 682*10266SQuaker.Fang@Sun.COM if (ic->ic_set_channel != NULL) 683*10266SQuaker.Fang@Sun.COM ic->ic_set_channel(ic); 684*10266SQuaker.Fang@Sun.COM } 685*10266SQuaker.Fang@Sun.COM 686*10266SQuaker.Fang@Sun.COM /* 6873147Sxc151355 * Join the specified IBSS/BSS network. The node is assumed to 6883147Sxc151355 * be passed in with a held reference. 6893147Sxc151355 */ 6903147Sxc151355 void 6913147Sxc151355 ieee80211_sta_join(ieee80211com_t *ic, ieee80211_node_t *selbs) 6923147Sxc151355 { 6933147Sxc151355 ieee80211_impl_t *im = ic->ic_private; 6943147Sxc151355 ieee80211_node_t *obss; 6953147Sxc151355 6963147Sxc151355 IEEE80211_LOCK(ic); 6973147Sxc151355 if (ic->ic_opmode == IEEE80211_M_IBSS) { 6983147Sxc151355 ieee80211_node_table_t *nt; 6993147Sxc151355 7003147Sxc151355 /* 7013147Sxc151355 * Delete unusable rates; we've already checked 7023147Sxc151355 * that the negotiated rate set is acceptable. 7033147Sxc151355 */ 704*10266SQuaker.Fang@Sun.COM (void) ieee80211_fix_rate(selbs, &selbs->in_rates, 705*10266SQuaker.Fang@Sun.COM IEEE80211_F_DODEL); 7063147Sxc151355 /* 7073147Sxc151355 * Fillin the neighbor table 7083147Sxc151355 */ 7093147Sxc151355 nt = &ic->ic_sta; 7103147Sxc151355 IEEE80211_NODE_LOCK(nt); 7113147Sxc151355 nt->nt_name = "neighbor"; 7123147Sxc151355 nt->nt_inact_init = im->im_inact_run; 7133147Sxc151355 IEEE80211_NODE_UNLOCK(nt); 7143147Sxc151355 } 7153147Sxc151355 7163147Sxc151355 /* 7173147Sxc151355 * Committed to selbs, setup state. 7183147Sxc151355 */ 7193147Sxc151355 obss = ic->ic_bss; 7204126Szf162725 ic->ic_bss = selbs; /* caller assumed to bump refcnt */ 7213147Sxc151355 if (obss != NULL) { 7223147Sxc151355 ieee80211_copy_bss(selbs, obss); 7233147Sxc151355 ieee80211_free_node(obss); 7243147Sxc151355 } 7253147Sxc151355 ic->ic_curmode = ieee80211_chan2mode(ic, selbs->in_chan); 7263147Sxc151355 ic->ic_curchan = selbs->in_chan; 7274499Shx147065 ic->ic_phytype = selbs->in_phytype; 7283147Sxc151355 /* 7293147Sxc151355 * Set the erp state (mostly the slot time) to deal with 7303147Sxc151355 * the auto-select case; this should be redundant if the 7313147Sxc151355 * mode is locked. 7323147Sxc151355 */ 7333147Sxc151355 ieee80211_reset_erp(ic); 734*10266SQuaker.Fang@Sun.COM ieee80211_wme_initparams(ic); 7353147Sxc151355 7363147Sxc151355 IEEE80211_UNLOCK(ic); 7373147Sxc151355 if (ic->ic_opmode == IEEE80211_M_STA) 7383147Sxc151355 ieee80211_new_state(ic, IEEE80211_S_AUTH, -1); 7393147Sxc151355 else 7403147Sxc151355 ieee80211_new_state(ic, IEEE80211_S_RUN, -1); 7413147Sxc151355 } 7423147Sxc151355 7433147Sxc151355 /* 7443147Sxc151355 * Leave the specified IBSS/BSS network. The node is assumed to 7453147Sxc151355 * be passed in with a held reference. 7463147Sxc151355 */ 7473147Sxc151355 void 7483147Sxc151355 ieee80211_sta_leave(ieee80211com_t *ic, ieee80211_node_t *in) 7493147Sxc151355 { 7503147Sxc151355 IEEE80211_LOCK(ic); 7513147Sxc151355 ic->ic_node_cleanup(in); 7523147Sxc151355 ieee80211_notify_node_leave(ic, in); 7533147Sxc151355 IEEE80211_UNLOCK(ic); 7543147Sxc151355 } 7553147Sxc151355 7563147Sxc151355 /* 7573147Sxc151355 * Allocate a node. This is the default callback function for 7583147Sxc151355 * ic_node_alloc. This function may be overridden by the driver 7593147Sxc151355 * to allocate device specific node structure. 7603147Sxc151355 */ 7613147Sxc151355 /* ARGSUSED */ 7623147Sxc151355 static ieee80211_node_t * 7633147Sxc151355 ieee80211_node_alloc(ieee80211com_t *ic) 7643147Sxc151355 { 7653147Sxc151355 return (kmem_zalloc(sizeof (ieee80211_node_t), KM_SLEEP)); 7663147Sxc151355 } 7673147Sxc151355 7683147Sxc151355 /* 7693147Sxc151355 * Cleanup a node, free any memory associated with the node. 7703147Sxc151355 * This is the default callback function for ic_node_cleanup 7713147Sxc151355 * and may be overridden by the driver. 7723147Sxc151355 */ 7733147Sxc151355 static void 7743147Sxc151355 ieee80211_node_cleanup(ieee80211_node_t *in) 7753147Sxc151355 { 7763147Sxc151355 in->in_associd = 0; 7773147Sxc151355 in->in_rssi = 0; 7783147Sxc151355 in->in_rstamp = 0; 7793147Sxc151355 if (in->in_challenge != NULL) { 7803147Sxc151355 kmem_free(in->in_challenge, IEEE80211_CHALLENGE_LEN); 7813147Sxc151355 in->in_challenge = NULL; 7823147Sxc151355 } 7833147Sxc151355 if (in->in_rxfrag != NULL) { 7843147Sxc151355 freemsg(in->in_rxfrag); 7853147Sxc151355 in->in_rxfrag = NULL; 7863147Sxc151355 } 7873147Sxc151355 } 7883147Sxc151355 7893147Sxc151355 /* 7903147Sxc151355 * Free a node. This is the default callback function for ic_node_free 7913147Sxc151355 * and may be overridden by the driver to free memory used by device 7923147Sxc151355 * specific node structure 7933147Sxc151355 */ 7943147Sxc151355 static void 7953147Sxc151355 ieee80211_node_free(ieee80211_node_t *in) 7963147Sxc151355 { 7973147Sxc151355 ieee80211com_t *ic = in->in_ic; 7983147Sxc151355 7993147Sxc151355 ic->ic_node_cleanup(in); 8004126Szf162725 if (in->in_wpa_ie != NULL) 8014126Szf162725 ieee80211_free(in->in_wpa_ie); 802*10266SQuaker.Fang@Sun.COM if (in->in_wme_ie != NULL) 803*10266SQuaker.Fang@Sun.COM ieee80211_free(in->in_wme_ie); 804*10266SQuaker.Fang@Sun.COM if (in->in_htcap_ie != NULL) 805*10266SQuaker.Fang@Sun.COM ieee80211_free(in->in_htcap_ie); 8063147Sxc151355 kmem_free(in, sizeof (ieee80211_node_t)); 8073147Sxc151355 } 8083147Sxc151355 8093147Sxc151355 /* 8103147Sxc151355 * Get a node current RSSI value. This is the default callback function 8113147Sxc151355 * for ic_node_getrssi and may be overridden by the driver to provide 8123147Sxc151355 * device specific RSSI calculation algorithm. 8133147Sxc151355 */ 8143147Sxc151355 static uint8_t 8153147Sxc151355 ieee80211_node_getrssi(const ieee80211_node_t *in) 8163147Sxc151355 { 8173147Sxc151355 return (in->in_rssi); 8183147Sxc151355 } 8193147Sxc151355 8203147Sxc151355 /* Free fragment if not needed anymore */ 8213147Sxc151355 static void 8223147Sxc151355 node_cleanfrag(ieee80211_node_t *in) 8233147Sxc151355 { 8243147Sxc151355 clock_t ticks; 8253147Sxc151355 8263147Sxc151355 ticks = ddi_get_lbolt(); 8273147Sxc151355 if (in->in_rxfrag != NULL && ticks > (in->in_rxfragstamp + hz)) { 8283147Sxc151355 freemsg(in->in_rxfrag); 8293147Sxc151355 in->in_rxfrag = NULL; 8303147Sxc151355 } 8313147Sxc151355 } 8323147Sxc151355 8333147Sxc151355 /* 8343147Sxc151355 * Setup a node. Initialize the node with specified macaddr. Associate 8353147Sxc151355 * with the interface softc, ic, and add it to the specified node 8363147Sxc151355 * database. 8373147Sxc151355 */ 8383147Sxc151355 static void 8393147Sxc151355 ieee80211_setup_node(ieee80211com_t *ic, ieee80211_node_table_t *nt, 8403147Sxc151355 ieee80211_node_t *in, const uint8_t *macaddr) 8413147Sxc151355 { 8423147Sxc151355 int32_t hash; 8433147Sxc151355 8443147Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_setup_node(): " 8454499Shx147065 "%p<%s> in %s table\n", in, 8464499Shx147065 ieee80211_macaddr_sprintf(macaddr), 8474499Shx147065 (nt != NULL) ? nt->nt_name : "NULL"); 8483147Sxc151355 8493147Sxc151355 in->in_ic = ic; 8503147Sxc151355 IEEE80211_ADDR_COPY(in->in_macaddr, macaddr); 8513147Sxc151355 hash = ieee80211_node_hash(macaddr); 8523147Sxc151355 ieee80211_node_initref(in); /* mark referenced */ 8533147Sxc151355 in->in_authmode = IEEE80211_AUTH_OPEN; 8543147Sxc151355 in->in_txpower = ic->ic_txpowlimit; /* max power */ 8553147Sxc151355 in->in_chan = IEEE80211_CHAN_ANYC; 8563147Sxc151355 in->in_inact_reload = IEEE80211_INACT_INIT; 8573147Sxc151355 in->in_inact = in->in_inact_reload; 8583147Sxc151355 ieee80211_crypto_resetkey(ic, &in->in_ucastkey, IEEE80211_KEYIX_NONE); 8593147Sxc151355 8603147Sxc151355 if (nt != NULL) { 8613147Sxc151355 IEEE80211_NODE_LOCK(nt); 8623147Sxc151355 list_insert_tail(&nt->nt_node, in); 8633147Sxc151355 list_insert_tail(&nt->nt_hash[hash], in); 8643147Sxc151355 in->in_table = nt; 8653147Sxc151355 in->in_inact_reload = nt->nt_inact_init; 8663147Sxc151355 IEEE80211_NODE_UNLOCK(nt); 8673147Sxc151355 } 8683147Sxc151355 } 8693147Sxc151355 8703147Sxc151355 /* 8713147Sxc151355 * Allocates and initialize a node with specified MAC address. 8723147Sxc151355 * Associate the node with the interface ic. If the allocation 8733147Sxc151355 * is successful, the node structure is initialized by 8743147Sxc151355 * ieee80211_setup_node(); otherwise, NULL is returned 8753147Sxc151355 */ 8763147Sxc151355 ieee80211_node_t * 8773147Sxc151355 ieee80211_alloc_node(ieee80211com_t *ic, ieee80211_node_table_t *nt, 8783147Sxc151355 const uint8_t *macaddr) 8793147Sxc151355 { 8803147Sxc151355 ieee80211_node_t *in; 8813147Sxc151355 8823147Sxc151355 in = ic->ic_node_alloc(ic); 8833147Sxc151355 if (in != NULL) 8843147Sxc151355 ieee80211_setup_node(ic, nt, in, macaddr); 8853147Sxc151355 return (in); 8863147Sxc151355 } 8873147Sxc151355 8883147Sxc151355 /* 8893147Sxc151355 * Craft a temporary node suitable for sending a management frame 8903147Sxc151355 * to the specified station. We craft only as much state as we 8913147Sxc151355 * need to do the work since the node will be immediately reclaimed 8923147Sxc151355 * once the send completes. 8933147Sxc151355 */ 8943147Sxc151355 ieee80211_node_t * 8953147Sxc151355 ieee80211_tmp_node(ieee80211com_t *ic, const uint8_t *macaddr) 8963147Sxc151355 { 8973147Sxc151355 ieee80211_node_t *in; 8983147Sxc151355 8993147Sxc151355 in = ic->ic_node_alloc(ic); 9003147Sxc151355 if (in != NULL) { 9013147Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_tmp_node: " 9024499Shx147065 "%p<%s>\n", in, ieee80211_macaddr_sprintf(macaddr)); 9033147Sxc151355 9043147Sxc151355 IEEE80211_ADDR_COPY(in->in_macaddr, macaddr); 9053147Sxc151355 IEEE80211_ADDR_COPY(in->in_bssid, ic->ic_bss->in_bssid); 9063147Sxc151355 ieee80211_node_initref(in); /* mark referenced */ 9073147Sxc151355 in->in_txpower = ic->ic_bss->in_txpower; 9083147Sxc151355 /* NB: required by ieee80211_fix_rate */ 9093147Sxc151355 ieee80211_node_setchan(ic, in, ic->ic_bss->in_chan); 9103147Sxc151355 ieee80211_crypto_resetkey(ic, &in->in_ucastkey, 9114499Shx147065 IEEE80211_KEYIX_NONE); 9123147Sxc151355 9133147Sxc151355 in->in_table = NULL; /* NB: pedantic */ 9143147Sxc151355 in->in_ic = ic; 9153147Sxc151355 } 9163147Sxc151355 9173147Sxc151355 return (in); 9183147Sxc151355 } 9193147Sxc151355 9203147Sxc151355 /* 9213147Sxc151355 * ieee80211_dup_bss() is similar to ieee80211_alloc_node(), 9223147Sxc151355 * but is instead used to create a node database entry for 9233147Sxc151355 * the specified BSSID. If the allocation is successful, the 9243147Sxc151355 * node is initialized, otherwise, NULL is returned. 9253147Sxc151355 */ 9263147Sxc151355 ieee80211_node_t * 9273147Sxc151355 ieee80211_dup_bss(ieee80211_node_table_t *nt, const uint8_t *macaddr) 9283147Sxc151355 { 9293147Sxc151355 ieee80211com_t *ic = nt->nt_ic; 9303147Sxc151355 ieee80211_node_t *in; 9313147Sxc151355 9323147Sxc151355 in = ieee80211_alloc_node(ic, nt, macaddr); 9333147Sxc151355 if (in != NULL) { 9343147Sxc151355 /* 9353147Sxc151355 * Inherit from ic_bss. 9363147Sxc151355 */ 9373147Sxc151355 ieee80211_copy_bss(in, ic->ic_bss); 9383147Sxc151355 IEEE80211_ADDR_COPY(in->in_bssid, ic->ic_bss->in_bssid); 9393147Sxc151355 ieee80211_node_setchan(ic, in, ic->ic_bss->in_chan); 9403147Sxc151355 } 9413147Sxc151355 9423147Sxc151355 return (in); 9433147Sxc151355 } 9443147Sxc151355 9453147Sxc151355 /* 9463147Sxc151355 * Iterate through the node table, searching for a node entry which 9473147Sxc151355 * matches macaddr. If the entry is found, its reference count is 9483147Sxc151355 * incremented, and a pointer to the node is returned; otherwise, 9493147Sxc151355 * NULL will be returned. 9503147Sxc151355 * The node table lock is acquired by the caller. 9513147Sxc151355 */ 9523147Sxc151355 static ieee80211_node_t * 9533147Sxc151355 ieee80211_find_node_locked(ieee80211_node_table_t *nt, const uint8_t *macaddr) 9543147Sxc151355 { 9553147Sxc151355 ieee80211_node_t *in; 9563147Sxc151355 int hash; 9573147Sxc151355 9583147Sxc151355 ASSERT(IEEE80211_NODE_IS_LOCKED(nt)); 9593147Sxc151355 9603147Sxc151355 hash = ieee80211_node_hash(macaddr); 9613147Sxc151355 in = list_head(&nt->nt_hash[hash]); 9623147Sxc151355 while (in != NULL) { 9633147Sxc151355 if (IEEE80211_ADDR_EQ(in->in_macaddr, macaddr)) 9643147Sxc151355 return (ieee80211_ref_node(in)); /* mark referenced */ 9653147Sxc151355 in = list_next(&nt->nt_hash[hash], in); 9663147Sxc151355 } 9673147Sxc151355 return (NULL); 9683147Sxc151355 } 9693147Sxc151355 9703147Sxc151355 /* 9713147Sxc151355 * Iterate through the node table, searching for a node entry 9723147Sxc151355 * which match specified mac address. 9733147Sxc151355 * Return NULL if no matching node found. 9743147Sxc151355 */ 9753147Sxc151355 ieee80211_node_t * 9763147Sxc151355 ieee80211_find_node(ieee80211_node_table_t *nt, const uint8_t *macaddr) 9773147Sxc151355 { 9783147Sxc151355 ieee80211_node_t *in; 9793147Sxc151355 9803147Sxc151355 IEEE80211_NODE_LOCK(nt); 9813147Sxc151355 in = ieee80211_find_node_locked(nt, macaddr); 9823147Sxc151355 IEEE80211_NODE_UNLOCK(nt); 9833147Sxc151355 return (in); 9843147Sxc151355 } 9853147Sxc151355 9863147Sxc151355 /* 9874126Szf162725 * Like find but search based on the ssid too. 9884126Szf162725 */ 9894126Szf162725 ieee80211_node_t * 9904126Szf162725 ieee80211_find_node_with_ssid(ieee80211_node_table_t *nt, 9914126Szf162725 const uint8_t *macaddr, uint32_t ssidlen, const uint8_t *ssid) 9924126Szf162725 { 9934126Szf162725 ieee80211_node_t *in; 9944126Szf162725 int hash; 9954126Szf162725 9964126Szf162725 IEEE80211_NODE_LOCK(nt); 9974126Szf162725 9984126Szf162725 hash = ieee80211_node_hash(macaddr); 9994126Szf162725 in = list_head(&nt->nt_hash[hash]); 10004126Szf162725 while (in != NULL) { 10014126Szf162725 if (IEEE80211_ADDR_EQ(in->in_macaddr, macaddr) && 10024126Szf162725 in->in_esslen == ssidlen && 10034126Szf162725 memcmp(in->in_essid, ssid, ssidlen) == 0) 10044126Szf162725 break; 10054126Szf162725 in = list_next(&nt->nt_hash[hash], in); 10064126Szf162725 } 10074126Szf162725 if (in != NULL) { 10084126Szf162725 (void) ieee80211_ref_node(in); /* mark referenced */ 10094126Szf162725 } 10104126Szf162725 IEEE80211_NODE_UNLOCK(nt); 10114126Szf162725 10124126Szf162725 return (in); 10134126Szf162725 } 10144126Szf162725 10154126Szf162725 /* 10163147Sxc151355 * Fake up a node; this handles node discovery in adhoc mode. 10173147Sxc151355 * Note that for the driver's benefit we treat this like an 10183147Sxc151355 * association so the driver has an opportunity to setup it's 10193147Sxc151355 * private state. 10203147Sxc151355 */ 10213147Sxc151355 ieee80211_node_t * 10223147Sxc151355 ieee80211_fakeup_adhoc_node(ieee80211_node_table_t *nt, const uint8_t *macaddr) 10233147Sxc151355 { 10243147Sxc151355 ieee80211com_t *ic = nt->nt_ic; 10253147Sxc151355 ieee80211_node_t *in; 10263147Sxc151355 10273147Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_fakeup_adhoc_node: " 10284499Shx147065 "mac<%s>\n", ieee80211_macaddr_sprintf(macaddr)); 10293147Sxc151355 in = ieee80211_dup_bss(nt, macaddr); 10303147Sxc151355 if (in != NULL) { 10313147Sxc151355 /* no rate negotiation; just dup */ 10323147Sxc151355 in->in_rates = ic->ic_bss->in_rates; 10333147Sxc151355 if (ic->ic_node_newassoc != NULL) 10343147Sxc151355 ic->ic_node_newassoc(in, 1); 10353147Sxc151355 ieee80211_node_authorize(in); 10363147Sxc151355 } 10373147Sxc151355 return (in); 10383147Sxc151355 } 10393147Sxc151355 10404126Szf162725 static void 10414126Szf162725 ieee80211_saveie(uint8_t **iep, const uint8_t *ie) 10424126Szf162725 { 10434126Szf162725 uint_t ielen = ie[1]+2; 10444126Szf162725 /* 10454126Szf162725 * Record information element for later use. 10464126Szf162725 */ 10474126Szf162725 if (*iep == NULL || (*iep)[1] != ie[1]) { 10484126Szf162725 if (*iep != NULL) 10494126Szf162725 ieee80211_free(*iep); 10504126Szf162725 *iep = ieee80211_malloc(ielen); 10514126Szf162725 } 10524126Szf162725 if (*iep != NULL) 10534126Szf162725 (void) memcpy(*iep, ie, ielen); 10544126Szf162725 } 10554126Szf162725 10564126Szf162725 static void 10574126Szf162725 saveie(uint8_t **iep, const uint8_t *ie) 10584126Szf162725 { 10595296Szf162725 if (ie == NULL) { 10605296Szf162725 if (*iep != NULL) 10615296Szf162725 ieee80211_free(*iep); 10624126Szf162725 *iep = NULL; 10635296Szf162725 } 10644126Szf162725 else 10654126Szf162725 ieee80211_saveie(iep, ie); 10664126Szf162725 } 10674126Szf162725 10683147Sxc151355 /* 10693147Sxc151355 * Process a beacon or probe response frame. 10703147Sxc151355 */ 10713147Sxc151355 void 10723147Sxc151355 ieee80211_add_scan(ieee80211com_t *ic, const struct ieee80211_scanparams *sp, 10733147Sxc151355 const struct ieee80211_frame *wh, int subtype, int rssi, int rstamp) 10743147Sxc151355 { 10753147Sxc151355 ieee80211_node_table_t *nt = &ic->ic_scan; 10763147Sxc151355 ieee80211_node_t *in; 10773147Sxc151355 boolean_t newnode = B_FALSE; 10783147Sxc151355 10794499Shx147065 in = ieee80211_find_node(nt, wh->i_addr3); 10803147Sxc151355 if (in == NULL) { 10813147Sxc151355 /* 10823147Sxc151355 * Create a new entry. 10833147Sxc151355 */ 10844499Shx147065 in = ieee80211_alloc_node(ic, nt, wh->i_addr3); 10853147Sxc151355 if (in == NULL) { 10863147Sxc151355 ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_add_scan: " 10874499Shx147065 "alloc node failed\n"); 10883147Sxc151355 return; 10893147Sxc151355 } 10903147Sxc151355 /* 10913147Sxc151355 * inherit from ic_bss. 10923147Sxc151355 */ 10933147Sxc151355 ieee80211_copy_bss(in, ic->ic_bss); 10943147Sxc151355 ieee80211_node_setchan(ic, in, ic->ic_curchan); 10953147Sxc151355 newnode = B_TRUE; 10963147Sxc151355 } 10973147Sxc151355 10983147Sxc151355 /* ap beaconing multiple ssid w/ same bssid */ 10993147Sxc151355 11003147Sxc151355 /* 11013147Sxc151355 * sp->ssid[0] - element ID 11023147Sxc151355 * sp->ssid[1] - length 11033147Sxc151355 * sp->ssid[2]... - ssid 11043147Sxc151355 */ 11053147Sxc151355 if (sp->ssid[1] != 0 && 11063147Sxc151355 subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP || 11073147Sxc151355 in->in_esslen == 0) { 11083147Sxc151355 in->in_esslen = sp->ssid[1]; 11093147Sxc151355 bzero(in->in_essid, sizeof (in->in_essid)); 11103147Sxc151355 bcopy(sp->ssid + 2, in->in_essid, sp->ssid[1]); 11113147Sxc151355 } 11123147Sxc151355 IEEE80211_ADDR_COPY(in->in_bssid, wh->i_addr3); 11133147Sxc151355 in->in_rssi = (uint8_t)rssi; 11143147Sxc151355 in->in_rstamp = rstamp; 11153147Sxc151355 bcopy(sp->tstamp, in->in_tstamp.data, sizeof (in->in_tstamp)); 11163147Sxc151355 in->in_intval = sp->bintval; 11173147Sxc151355 in->in_capinfo = sp->capinfo; 11183147Sxc151355 in->in_chan = &ic->ic_sup_channels[sp->chan]; 11193147Sxc151355 in->in_phytype = sp->phytype; 11203147Sxc151355 in->in_fhdwell = sp->fhdwell; 11213147Sxc151355 in->in_fhindex = sp->fhindex; 11223147Sxc151355 in->in_erp = sp->erp; 11233147Sxc151355 if (sp->tim != NULL) { 11243147Sxc151355 struct ieee80211_tim_ie *ie; 11253147Sxc151355 11263147Sxc151355 ie = (struct ieee80211_tim_ie *)sp->tim; 11273147Sxc151355 in->in_dtim_count = ie->tim_count; 11283147Sxc151355 in->in_dtim_period = ie->tim_period; 11293147Sxc151355 } 11303147Sxc151355 /* 11313147Sxc151355 * Record the byte offset from the mac header to 11323147Sxc151355 * the start of the TIM information element for 11333147Sxc151355 * use by hardware and/or to speedup software 11343147Sxc151355 * processing of beacon frames. 11353147Sxc151355 */ 11363147Sxc151355 in->in_tim_off = sp->timoff; 11374126Szf162725 /* 11384126Szf162725 * Record optional information elements that might be 11394126Szf162725 * used by applications or drivers. 11404126Szf162725 */ 1141*10266SQuaker.Fang@Sun.COM saveie(&in->in_wme_ie, sp->wme); 11424126Szf162725 saveie(&in->in_wpa_ie, sp->wpa); 1143*10266SQuaker.Fang@Sun.COM saveie(&in->in_htcap_ie, sp->htcap); 1144*10266SQuaker.Fang@Sun.COM /* parsed in ieee80211_sta_join() */ 1145*10266SQuaker.Fang@Sun.COM if (sp->htcap != NULL) 1146*10266SQuaker.Fang@Sun.COM ieee80211_parse_htcap(in, in->in_htcap_ie); 11473147Sxc151355 11483147Sxc151355 /* NB: must be after in_chan is setup */ 11493147Sxc151355 (void) ieee80211_setup_rates(in, sp->rates, sp->xrates, 11504499Shx147065 IEEE80211_F_DOSORT); 11513147Sxc151355 11523147Sxc151355 if (!newnode) 11533147Sxc151355 ieee80211_free_node(in); 11543147Sxc151355 } 11553147Sxc151355 11563147Sxc151355 /* 11573147Sxc151355 * Initialize/update an ad-hoc node with contents from a received 11583147Sxc151355 * beacon frame. 11593147Sxc151355 */ 11603147Sxc151355 void 11613147Sxc151355 ieee80211_init_neighbor(ieee80211_node_t *in, const struct ieee80211_frame *wh, 11623147Sxc151355 const struct ieee80211_scanparams *sp) 11633147Sxc151355 { 11643147Sxc151355 in->in_esslen = sp->ssid[1]; 11653147Sxc151355 (void) memcpy(in->in_essid, sp->ssid + 2, sp->ssid[1]); 11663147Sxc151355 IEEE80211_ADDR_COPY(in->in_bssid, wh->i_addr3); 11673147Sxc151355 (void) memcpy(in->in_tstamp.data, sp->tstamp, sizeof (in->in_tstamp)); 11683147Sxc151355 in->in_intval = sp->bintval; 11693147Sxc151355 in->in_capinfo = sp->capinfo; 11703147Sxc151355 in->in_chan = in->in_ic->ic_curchan; 11713147Sxc151355 in->in_fhdwell = sp->fhdwell; 11723147Sxc151355 in->in_fhindex = sp->fhindex; 11733147Sxc151355 in->in_erp = sp->erp; 11743147Sxc151355 in->in_tim_off = sp->timoff; 1175*10266SQuaker.Fang@Sun.COM if (sp->wme != NULL) 1176*10266SQuaker.Fang@Sun.COM ieee80211_saveie(&in->in_wme_ie, sp->wme); 11773147Sxc151355 11783147Sxc151355 /* NB: must be after in_chan is setup */ 11793147Sxc151355 (void) ieee80211_setup_rates(in, sp->rates, sp->xrates, 11804499Shx147065 IEEE80211_F_DOSORT); 11813147Sxc151355 } 11823147Sxc151355 11833147Sxc151355 /* 11843147Sxc151355 * Do node discovery in adhoc mode on receipt of a beacon 11853147Sxc151355 * or probe response frame. Note that for the driver's 11863147Sxc151355 * benefit we we treat this like an association so the 11873147Sxc151355 * driver has an opportuinty to setup it's private state. 11883147Sxc151355 */ 11893147Sxc151355 ieee80211_node_t * 11903147Sxc151355 ieee80211_add_neighbor(ieee80211com_t *ic, const struct ieee80211_frame *wh, 11913147Sxc151355 const struct ieee80211_scanparams *sp) 11923147Sxc151355 { 11933147Sxc151355 ieee80211_node_t *in; 11943147Sxc151355 11953147Sxc151355 in = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2); 11963147Sxc151355 if (in != NULL) { 11973147Sxc151355 ieee80211_init_neighbor(in, wh, sp); 11983147Sxc151355 if (ic->ic_node_newassoc != NULL) 11993147Sxc151355 ic->ic_node_newassoc(in, 1); 12003147Sxc151355 } 12013147Sxc151355 return (in); 12023147Sxc151355 } 12033147Sxc151355 1204*10266SQuaker.Fang@Sun.COM #define IEEE80211_IS_CTL(wh) \ 12053147Sxc151355 ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) 12063147Sxc151355 1207*10266SQuaker.Fang@Sun.COM #define IEEE80211_IS_PSPOLL(wh) \ 1208*10266SQuaker.Fang@Sun.COM ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == \ 1209*10266SQuaker.Fang@Sun.COM IEEE80211_FC0_SUBTYPE_PS_POLL) 1210*10266SQuaker.Fang@Sun.COM 1211*10266SQuaker.Fang@Sun.COM #define IEEE80211_IS_BAR(wh) \ 1212*10266SQuaker.Fang@Sun.COM ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == \ 1213*10266SQuaker.Fang@Sun.COM IEEE80211_FC0_SUBTYPE_BAR) 1214*10266SQuaker.Fang@Sun.COM 12153147Sxc151355 /* 12163147Sxc151355 * Locate the node for sender, track state, and then pass the 12173147Sxc151355 * (referenced) node up to the 802.11 layer for its use. We 12183147Sxc151355 * are required to pass some node so we fall back to ic_bss 12193147Sxc151355 * when this frame is from an unknown sender. The 802.11 layer 12203147Sxc151355 * knows this means the sender wasn't in the node table and 12213147Sxc151355 * acts accordingly. 12223147Sxc151355 */ 12233147Sxc151355 ieee80211_node_t * 12243147Sxc151355 ieee80211_find_rxnode(ieee80211com_t *ic, const struct ieee80211_frame *wh) 12253147Sxc151355 { 12263147Sxc151355 ieee80211_node_table_t *nt; 12273147Sxc151355 ieee80211_node_t *in; 12283147Sxc151355 12293147Sxc151355 /* may want scanned nodes in the neighbor table for adhoc */ 12303147Sxc151355 if (ic->ic_opmode == IEEE80211_M_STA || 12313147Sxc151355 (ic->ic_flags & IEEE80211_F_SCAN)) { 12323147Sxc151355 nt = &ic->ic_scan; 12333147Sxc151355 } else { 12343147Sxc151355 nt = &ic->ic_sta; 12353147Sxc151355 } 12363147Sxc151355 12373147Sxc151355 IEEE80211_NODE_LOCK(nt); 1238*10266SQuaker.Fang@Sun.COM if (IEEE80211_IS_CTL(wh) && 1239*10266SQuaker.Fang@Sun.COM !IEEE80211_IS_PSPOLL(wh) && !IEEE80211_IS_BAR(wh)) 12403147Sxc151355 in = ieee80211_find_node_locked(nt, wh->i_addr1); 12413147Sxc151355 else 12423147Sxc151355 in = ieee80211_find_node_locked(nt, wh->i_addr2); 12433147Sxc151355 IEEE80211_NODE_UNLOCK(nt); 12443147Sxc151355 12453147Sxc151355 if (in == NULL) 12463147Sxc151355 in = ieee80211_ref_node(ic->ic_bss); 12473147Sxc151355 12483147Sxc151355 return (in); 12493147Sxc151355 } 12503147Sxc151355 1251*10266SQuaker.Fang@Sun.COM #undef IEEE80211_IS_BAR 1252*10266SQuaker.Fang@Sun.COM #undef IEEE80211_IS_PSPOLL 1253*10266SQuaker.Fang@Sun.COM #undef IEEE80211_IS_CTL 1254*10266SQuaker.Fang@Sun.COM 12553147Sxc151355 /* 12563147Sxc151355 * Return a reference to the appropriate node for sending 12573147Sxc151355 * a data frame. This handles node discovery in adhoc networks. 12583147Sxc151355 */ 12593147Sxc151355 ieee80211_node_t * 12603147Sxc151355 ieee80211_find_txnode(ieee80211com_t *ic, const uint8_t *daddr) 12613147Sxc151355 { 12623147Sxc151355 ieee80211_node_table_t *nt = &ic->ic_sta; 12633147Sxc151355 ieee80211_node_t *in; 12643147Sxc151355 12653147Sxc151355 /* 12663147Sxc151355 * The destination address should be in the node table 12673147Sxc151355 * unless this is a multicast/broadcast frame. We can 12683147Sxc151355 * also optimize station mode operation, all frames go 12693147Sxc151355 * to the bss node. 12703147Sxc151355 */ 12713147Sxc151355 IEEE80211_NODE_LOCK(nt); 12723147Sxc151355 if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(daddr)) 12733147Sxc151355 in = ieee80211_ref_node(ic->ic_bss); 12743147Sxc151355 else 12753147Sxc151355 in = ieee80211_find_node_locked(nt, daddr); 12763147Sxc151355 IEEE80211_NODE_UNLOCK(nt); 12773147Sxc151355 12783147Sxc151355 if (in == NULL) { 12793147Sxc151355 if (ic->ic_opmode == IEEE80211_M_IBSS) { 12803147Sxc151355 /* 12813147Sxc151355 * In adhoc mode cons up a node for the destination. 12823147Sxc151355 * Note that we need an additional reference for the 12833147Sxc151355 * caller to be consistent with 12843147Sxc151355 * ieee80211_find_node_locked 12853147Sxc151355 * can't hold lock across ieee80211_dup_bss 'cuz of 12863147Sxc151355 * recursive locking 12873147Sxc151355 */ 12883147Sxc151355 in = ieee80211_fakeup_adhoc_node(nt, daddr); 12893147Sxc151355 if (in != NULL) 12903147Sxc151355 (void) ieee80211_ref_node(in); 12913147Sxc151355 } else { 12923147Sxc151355 ieee80211_dbg(IEEE80211_MSG_OUTPUT, 12934499Shx147065 "ieee80211_find_txnode: " 12944499Shx147065 "[%s] no node, discard frame\n", 12954499Shx147065 ieee80211_macaddr_sprintf(daddr)); 12963147Sxc151355 } 12973147Sxc151355 } 12983147Sxc151355 return (in); 12993147Sxc151355 } 13003147Sxc151355 13013147Sxc151355 /* 13023147Sxc151355 * Remove a node from the node database entries and free memory 13033147Sxc151355 * associated with the node. The node table lock is acquired by 13043147Sxc151355 * the caller. 13053147Sxc151355 */ 13063147Sxc151355 static void 13073147Sxc151355 ieee80211_free_node_locked(ieee80211_node_t *in) 13083147Sxc151355 { 13093147Sxc151355 ieee80211com_t *ic = in->in_ic; 13103147Sxc151355 ieee80211_node_table_t *nt = in->in_table; 13113147Sxc151355 int32_t hash; 13123147Sxc151355 13133147Sxc151355 if (nt != NULL) { 13143147Sxc151355 hash = ieee80211_node_hash(in->in_macaddr); 13153147Sxc151355 list_remove(&nt->nt_hash[hash], in); 13163147Sxc151355 list_remove(&nt->nt_node, in); 13173147Sxc151355 } 13183147Sxc151355 ic->ic_node_free(in); 13193147Sxc151355 } 13203147Sxc151355 13213147Sxc151355 /* 13223147Sxc151355 * Remove a node from the node database entries and free any 13233147Sxc151355 * memory associated with the node. 13243147Sxc151355 * This method can be overridden in ieee80211_attach() 13253147Sxc151355 */ 13263147Sxc151355 void 13273147Sxc151355 ieee80211_free_node(ieee80211_node_t *in) 13283147Sxc151355 { 13293147Sxc151355 ieee80211_node_table_t *nt = in->in_table; 13303147Sxc151355 13313147Sxc151355 if (nt != NULL) 13323147Sxc151355 IEEE80211_NODE_LOCK(nt); 13333147Sxc151355 if (ieee80211_node_decref_nv(in) == 0) 13343147Sxc151355 ieee80211_free_node_locked(in); 13353147Sxc151355 if (nt != NULL) 13363147Sxc151355 IEEE80211_NODE_UNLOCK(nt); 13373147Sxc151355 } 13383147Sxc151355 13393147Sxc151355 /* 13403147Sxc151355 * Reclaim a node. If this is the last reference count then 13413147Sxc151355 * do the normal free work. Otherwise remove it from the node 13423147Sxc151355 * table and mark it gone by clearing the back-reference. 13433147Sxc151355 */ 13443147Sxc151355 static void 13453147Sxc151355 ieee80211_node_reclaim(ieee80211_node_table_t *nt, ieee80211_node_t *in) 13463147Sxc151355 { 13473147Sxc151355 int32_t hash; 13483147Sxc151355 13493147Sxc151355 IEEE80211_NODE_LOCK_ASSERT(nt); 13503147Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "node_reclaim: " 13514499Shx147065 " remove %p<%s> from %s table, refcnt %d\n", 13524499Shx147065 in, ieee80211_macaddr_sprintf(in->in_macaddr), nt->nt_name, 13534499Shx147065 ieee80211_node_refcnt(in)); 13543147Sxc151355 13553147Sxc151355 if (ieee80211_node_decref_nv(in) != 0) { 13563147Sxc151355 /* 13573147Sxc151355 * Clear any entry in the unicast key mapping table. 13583147Sxc151355 * We need to do it here so rx lookups don't find it 13593147Sxc151355 * in the mapping table even if it's not in the hash 13603147Sxc151355 * table. We cannot depend on the mapping table entry 13613147Sxc151355 * being cleared because the node may not be free'd. 13623147Sxc151355 */ 13633147Sxc151355 hash = ieee80211_node_hash(in->in_macaddr); 13643147Sxc151355 list_remove(&nt->nt_hash[hash], in); 13653147Sxc151355 list_remove(&nt->nt_node, in); 13663147Sxc151355 in->in_table = NULL; 13673147Sxc151355 } else { 13683147Sxc151355 ieee80211_free_node_locked(in); 13693147Sxc151355 } 13703147Sxc151355 } 13713147Sxc151355 13723147Sxc151355 /* 13733147Sxc151355 * Iterate through the node list and reclaim all node in the node table. 13743147Sxc151355 * The node table lock is acquired by the caller 13753147Sxc151355 */ 13763147Sxc151355 static void 13773147Sxc151355 ieee80211_free_allnodes_locked(ieee80211_node_table_t *nt) 13783147Sxc151355 { 13793147Sxc151355 ieee80211_node_t *in; 13803147Sxc151355 13813147Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_free_allnodes_locked(): " 13824499Shx147065 "free all nodes in %s table\n", nt->nt_name); 13833147Sxc151355 13843147Sxc151355 in = list_head(&nt->nt_node); 13853147Sxc151355 while (in != NULL) { 13863147Sxc151355 ieee80211_node_reclaim(nt, in); 13873147Sxc151355 in = list_head(&nt->nt_node); 13883147Sxc151355 } 13893147Sxc151355 ieee80211_reset_erp(nt->nt_ic); 13903147Sxc151355 } 13913147Sxc151355 13923147Sxc151355 /* 13933147Sxc151355 * Iterate through the node list, calling ieee80211_node_reclaim() for 13943147Sxc151355 * all nodes associated with the interface. 13953147Sxc151355 */ 13963147Sxc151355 static void 13973147Sxc151355 ieee80211_free_allnodes(ieee80211_node_table_t *nt) 13983147Sxc151355 { 13993147Sxc151355 IEEE80211_NODE_LOCK(nt); 14003147Sxc151355 ieee80211_free_allnodes_locked(nt); 14013147Sxc151355 IEEE80211_NODE_UNLOCK(nt); 14023147Sxc151355 } 14033147Sxc151355 14043147Sxc151355 /* 14053147Sxc151355 * Timeout entries in the scan cache. This is the timeout callback 14063147Sxc151355 * function of node table ic_scan which is called when the inactivity 14073147Sxc151355 * timer expires. 14083147Sxc151355 */ 14093147Sxc151355 static void 14103147Sxc151355 ieee80211_timeout_scan_candidates(ieee80211_node_table_t *nt) 14113147Sxc151355 { 14123147Sxc151355 ieee80211com_t *ic = nt->nt_ic; 14133147Sxc151355 ieee80211_node_t *in; 14143147Sxc151355 14153147Sxc151355 IEEE80211_NODE_LOCK(nt); 14163147Sxc151355 in = ic->ic_bss; 14173147Sxc151355 node_cleanfrag(in); /* Free fragment if not needed */ 14183147Sxc151355 nt->nt_inact_timer = IEEE80211_INACT_WAIT; 14193147Sxc151355 IEEE80211_NODE_UNLOCK(nt); 14203147Sxc151355 } 14213147Sxc151355 14223147Sxc151355 /* 14233147Sxc151355 * Timeout inactive stations and do related housekeeping. 14243147Sxc151355 * Note that we cannot hold the node lock while sending a 14253147Sxc151355 * frame as this would lead to a LOR. Instead we use a 14263147Sxc151355 * generation number to mark nodes that we've scanned and 14273147Sxc151355 * drop the lock and restart a scan if we have to time out 14283147Sxc151355 * a node. Since we are single-threaded by virtue of 14293147Sxc151355 * controlling the inactivity timer we can be sure this will 14303147Sxc151355 * process each node only once. 14313147Sxc151355 */ 14323147Sxc151355 static void 14333147Sxc151355 ieee80211_timeout_stations(ieee80211_node_table_t *nt) 14343147Sxc151355 { 14353147Sxc151355 ieee80211com_t *ic = nt->nt_ic; 14363147Sxc151355 ieee80211_impl_t *im = ic->ic_private; 14373147Sxc151355 ieee80211_node_t *in = NULL; 14383147Sxc151355 uint32_t gen; 14393147Sxc151355 boolean_t isadhoc; 14403147Sxc151355 14413147Sxc151355 IEEE80211_LOCK_ASSERT(ic); 14423147Sxc151355 isadhoc = (ic->ic_opmode == IEEE80211_M_IBSS || 14434499Shx147065 ic->ic_opmode == IEEE80211_M_AHDEMO); 14443147Sxc151355 IEEE80211_SCAN_LOCK(nt); 14453147Sxc151355 gen = ++nt->nt_scangen; 14463147Sxc151355 restart: 14473147Sxc151355 IEEE80211_NODE_LOCK(nt); 14483147Sxc151355 for (in = list_head(&nt->nt_node); in != NULL; 14494499Shx147065 in = list_next(&nt->nt_node, in)) { 14503147Sxc151355 if (in->in_scangen == gen) /* previously handled */ 14513147Sxc151355 continue; 14523147Sxc151355 in->in_scangen = gen; 14533147Sxc151355 node_cleanfrag(in); /* free fragment if not needed */ 14543147Sxc151355 14553147Sxc151355 /* 14563147Sxc151355 * Special case ourself; we may be idle for extended periods 14573147Sxc151355 * of time and regardless reclaiming our state is wrong. 14583147Sxc151355 */ 14593147Sxc151355 if (in == ic->ic_bss) 14603147Sxc151355 continue; 14613147Sxc151355 in->in_inact--; 14623147Sxc151355 if (in->in_associd != 0 || isadhoc) { 14633147Sxc151355 /* 14643147Sxc151355 * Probe the station before time it out. We 14653147Sxc151355 * send a null data frame which may not be 14663147Sxc151355 * uinversally supported by drivers (need it 14673147Sxc151355 * for ps-poll support so it should be...). 14683147Sxc151355 */ 14693147Sxc151355 if (0 < in->in_inact && 14703147Sxc151355 in->in_inact <= im->im_inact_probe) { 14713147Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "net80211: " 14724499Shx147065 "probe station due to inactivity\n"); 14733147Sxc151355 IEEE80211_NODE_UNLOCK(nt); 14743147Sxc151355 IEEE80211_UNLOCK(ic); 14753147Sxc151355 (void) ieee80211_send_nulldata(in); 14763147Sxc151355 IEEE80211_LOCK(ic); 14773147Sxc151355 goto restart; 14783147Sxc151355 } 14793147Sxc151355 } 14803147Sxc151355 if (in->in_inact <= 0) { 14813147Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "net80211: " 14824499Shx147065 "station timed out due to inact (refcnt %u)\n", 14834499Shx147065 ieee80211_node_refcnt(in)); 14843147Sxc151355 /* 14853147Sxc151355 * Send a deauthenticate frame and drop the station. 14863147Sxc151355 * This is somewhat complicated due to reference counts 14873147Sxc151355 * and locking. At this point a station will typically 14883147Sxc151355 * have a reference count of 1. ieee80211_node_leave 14893147Sxc151355 * will do a "free" of the node which will drop the 14903147Sxc151355 * reference count. But in the meantime a reference 14913147Sxc151355 * wil be held by the deauth frame. The actual reclaim 14923147Sxc151355 * of the node will happen either after the tx is 14933147Sxc151355 * completed or by ieee80211_node_leave. 14943147Sxc151355 * 14953147Sxc151355 * Separately we must drop the node lock before sending 14963147Sxc151355 * in case the driver takes a lock, as this will result 14973147Sxc151355 * in LOR between the node lock and the driver lock. 14983147Sxc151355 */ 14993147Sxc151355 IEEE80211_NODE_UNLOCK(nt); 15003147Sxc151355 if (in->in_associd != 0) { 15013147Sxc151355 IEEE80211_UNLOCK(ic); 15023147Sxc151355 IEEE80211_SEND_MGMT(ic, in, 15034499Shx147065 IEEE80211_FC0_SUBTYPE_DEAUTH, 15044499Shx147065 IEEE80211_REASON_AUTH_EXPIRE); 15053147Sxc151355 IEEE80211_LOCK(ic); 15063147Sxc151355 } 15073147Sxc151355 ieee80211_node_leave(ic, in); 15083147Sxc151355 goto restart; 15093147Sxc151355 } 15103147Sxc151355 } 15113147Sxc151355 IEEE80211_NODE_UNLOCK(nt); 15123147Sxc151355 15133147Sxc151355 IEEE80211_SCAN_UNLOCK(nt); 15143147Sxc151355 15153147Sxc151355 nt->nt_inact_timer = IEEE80211_INACT_WAIT; 15163147Sxc151355 } 15173147Sxc151355 15183147Sxc151355 /* 15193147Sxc151355 * Call the user-defined call back function for all nodes in 15203147Sxc151355 * the node cache. The callback is invoked with the user-supplied 15213147Sxc151355 * value and a pointer to the current node. 15223147Sxc151355 */ 15233147Sxc151355 void 15243147Sxc151355 ieee80211_iterate_nodes(ieee80211_node_table_t *nt, ieee80211_iter_func *f, 15253147Sxc151355 void *arg) 15263147Sxc151355 { 15273147Sxc151355 ieee80211_node_t *in; 15283147Sxc151355 15293147Sxc151355 IEEE80211_NODE_LOCK(nt); 15303147Sxc151355 in = list_head(&nt->nt_node); 15313147Sxc151355 while (in != NULL) { 15324499Shx147065 if (in->in_chan == IEEE80211_CHAN_ANYC) { 15334499Shx147065 in = list_next(&nt->nt_node, in); 15344499Shx147065 continue; 15354499Shx147065 } 15363147Sxc151355 (void) ieee80211_ref_node(in); 15373147Sxc151355 IEEE80211_NODE_UNLOCK(nt); 15383147Sxc151355 (*f)(arg, in); 15393147Sxc151355 ieee80211_free_node(in); 15403147Sxc151355 IEEE80211_NODE_LOCK(nt); 15413147Sxc151355 in = list_next(&nt->nt_node, in); 15423147Sxc151355 } 15433147Sxc151355 IEEE80211_NODE_UNLOCK(nt); 15443147Sxc151355 } 15453147Sxc151355 15463147Sxc151355 /* 15473147Sxc151355 * Handle bookkeeping for station deauthentication/disassociation 15483147Sxc151355 * when operating as an ap. 15493147Sxc151355 */ 15503147Sxc151355 static void 15513147Sxc151355 ieee80211_node_leave(ieee80211com_t *ic, ieee80211_node_t *in) 15523147Sxc151355 { 15533147Sxc151355 ieee80211_node_table_t *nt = in->in_table; 15543147Sxc151355 15553147Sxc151355 ASSERT(ic->ic_opmode == IEEE80211_M_IBSS); 15563147Sxc151355 15573147Sxc151355 /* 15583147Sxc151355 * Remove the node from any table it's recorded in and 15593147Sxc151355 * drop the caller's reference. Removal from the table 15603147Sxc151355 * is important to insure the node is not reprocessed 15613147Sxc151355 * for inactivity. 15623147Sxc151355 */ 15633147Sxc151355 if (nt != NULL) { 15643147Sxc151355 IEEE80211_NODE_LOCK(nt); 15653147Sxc151355 ieee80211_node_reclaim(nt, in); 15663147Sxc151355 IEEE80211_NODE_UNLOCK(nt); 15673147Sxc151355 } else { 15683147Sxc151355 ieee80211_free_node(in); 15693147Sxc151355 } 15703147Sxc151355 } 15713147Sxc151355 15723147Sxc151355 /* 15733147Sxc151355 * Initialize a node table with specified name, inactivity timer value 15743147Sxc151355 * and callback inactivity timeout function. Associate the node table 15753147Sxc151355 * with interface softc, ic. 15763147Sxc151355 */ 15773147Sxc151355 static void 15783147Sxc151355 ieee80211_node_table_init(ieee80211com_t *ic, ieee80211_node_table_t *nt, 15793147Sxc151355 const char *name, int inact, int keyixmax, 15803147Sxc151355 void (*timeout)(ieee80211_node_table_t *)) 15813147Sxc151355 { 15823147Sxc151355 int i; 15833147Sxc151355 15843147Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_node_table_init():" 15854499Shx147065 "%s table, inact %d\n", name, inact); 15863147Sxc151355 15873147Sxc151355 nt->nt_ic = ic; 15883147Sxc151355 nt->nt_name = name; 15893147Sxc151355 nt->nt_inact_timer = 0; 15903147Sxc151355 nt->nt_inact_init = inact; 15913147Sxc151355 nt->nt_timeout = timeout; 15923147Sxc151355 nt->nt_keyixmax = keyixmax; 15933147Sxc151355 nt->nt_scangen = 1; 15943147Sxc151355 mutex_init(&nt->nt_scanlock, NULL, MUTEX_DRIVER, NULL); 15953147Sxc151355 mutex_init(&nt->nt_nodelock, NULL, MUTEX_DRIVER, NULL); 15963147Sxc151355 15973147Sxc151355 list_create(&nt->nt_node, sizeof (ieee80211_node_t), 15984499Shx147065 offsetof(ieee80211_node_t, in_node)); 15993147Sxc151355 for (i = 0; i < IEEE80211_NODE_HASHSIZE; i++) { 16003147Sxc151355 list_create(&nt->nt_hash[i], sizeof (ieee80211_node_t), 16014499Shx147065 offsetof(ieee80211_node_t, in_hash)); 16023147Sxc151355 } 16033147Sxc151355 } 16043147Sxc151355 16053147Sxc151355 /* 16063147Sxc151355 * Reset a node table. Clean its inactivity timer and call 16073147Sxc151355 * ieee80211_free_allnodes_locked() to free all nodes in the 16083147Sxc151355 * node table. 16093147Sxc151355 */ 16103147Sxc151355 void 16113147Sxc151355 ieee80211_node_table_reset(ieee80211_node_table_t *nt) 16123147Sxc151355 { 16133147Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_node_table_reset(): " 16144499Shx147065 "%s table\n", nt->nt_name); 16153147Sxc151355 16163147Sxc151355 IEEE80211_NODE_LOCK(nt); 16173147Sxc151355 nt->nt_inact_timer = 0; 16183147Sxc151355 ieee80211_free_allnodes_locked(nt); 16193147Sxc151355 IEEE80211_NODE_UNLOCK(nt); 16203147Sxc151355 } 16213147Sxc151355 16223147Sxc151355 /* 16233147Sxc151355 * Destroy a node table. Free all nodes in the node table. 16243147Sxc151355 * This function is usually called by node detach function. 16253147Sxc151355 */ 16263147Sxc151355 static void 16273147Sxc151355 ieee80211_node_table_cleanup(ieee80211_node_table_t *nt) 16283147Sxc151355 { 16293147Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_node_table_cleanup(): " 16303147Sxc151355 "%s table\n", nt->nt_name); 16313147Sxc151355 16323147Sxc151355 IEEE80211_NODE_LOCK(nt); 16333147Sxc151355 ieee80211_free_allnodes_locked(nt); 16343147Sxc151355 IEEE80211_NODE_UNLOCK(nt); 16353147Sxc151355 mutex_destroy(&nt->nt_nodelock); 16363147Sxc151355 mutex_destroy(&nt->nt_scanlock); 16373147Sxc151355 } 1638