10ba2cbe9Sxc151355 /* 219d332feSfei feng - Sun Microsystems - Beijing China * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 30ba2cbe9Sxc151355 * Use is subject to license terms. 40ba2cbe9Sxc151355 */ 50ba2cbe9Sxc151355 60ba2cbe9Sxc151355 /* 70ba2cbe9Sxc151355 * Copyright (c) 2001 Atsushi Onoe 819d332feSfei feng - Sun Microsystems - Beijing China * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting 90ba2cbe9Sxc151355 * All rights reserved. 100ba2cbe9Sxc151355 * 110ba2cbe9Sxc151355 * Redistribution and use in source and binary forms, with or without 120ba2cbe9Sxc151355 * modification, are permitted provided that the following conditions 130ba2cbe9Sxc151355 * are met: 140ba2cbe9Sxc151355 * 1. Redistributions of source code must retain the above copyright 150ba2cbe9Sxc151355 * notice, this list of conditions and the following disclaimer. 160ba2cbe9Sxc151355 * 2. Redistributions in binary form must reproduce the above copyright 170ba2cbe9Sxc151355 * notice, this list of conditions and the following disclaimer in the 180ba2cbe9Sxc151355 * documentation and/or other materials provided with the distribution. 190ba2cbe9Sxc151355 * 3. The name of the author may not be used to endorse or promote products 200ba2cbe9Sxc151355 * derived from this software without specific prior written permission. 210ba2cbe9Sxc151355 * 220ba2cbe9Sxc151355 * Alternatively, this software may be distributed under the terms of the 230ba2cbe9Sxc151355 * GNU General Public License ("GPL") version 2 as published by the Free 240ba2cbe9Sxc151355 * Software Foundation. 250ba2cbe9Sxc151355 * 260ba2cbe9Sxc151355 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 270ba2cbe9Sxc151355 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 280ba2cbe9Sxc151355 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 290ba2cbe9Sxc151355 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 300ba2cbe9Sxc151355 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 310ba2cbe9Sxc151355 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 320ba2cbe9Sxc151355 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 330ba2cbe9Sxc151355 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 340ba2cbe9Sxc151355 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 350ba2cbe9Sxc151355 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 360ba2cbe9Sxc151355 */ 370ba2cbe9Sxc151355 380ba2cbe9Sxc151355 /* 390ba2cbe9Sxc151355 * Node management routines 400ba2cbe9Sxc151355 */ 410ba2cbe9Sxc151355 420ba2cbe9Sxc151355 #include "net80211_impl.h" 430ba2cbe9Sxc151355 440ba2cbe9Sxc151355 static ieee80211_node_t *ieee80211_node_alloc(ieee80211com_t *); 450ba2cbe9Sxc151355 static void ieee80211_node_cleanup(ieee80211_node_t *); 460ba2cbe9Sxc151355 static void ieee80211_node_free(ieee80211_node_t *); 470ba2cbe9Sxc151355 static uint8_t ieee80211_node_getrssi(const ieee80211_node_t *); 480ba2cbe9Sxc151355 static void ieee80211_setup_node(ieee80211com_t *, ieee80211_node_table_t *, 490ba2cbe9Sxc151355 ieee80211_node_t *, const uint8_t *); 500ba2cbe9Sxc151355 static void ieee80211_node_reclaim(ieee80211_node_table_t *, 510ba2cbe9Sxc151355 ieee80211_node_t *); 520ba2cbe9Sxc151355 static void ieee80211_free_node_locked(ieee80211_node_t *); 530ba2cbe9Sxc151355 static void ieee80211_free_allnodes(ieee80211_node_table_t *); 540ba2cbe9Sxc151355 static void ieee80211_node_leave(ieee80211com_t *, ieee80211_node_t *); 550ba2cbe9Sxc151355 static void ieee80211_timeout_scan_candidates(ieee80211_node_table_t *); 560ba2cbe9Sxc151355 static void ieee80211_timeout_stations(ieee80211_node_table_t *); 570ba2cbe9Sxc151355 static void ieee80211_node_table_init(ieee80211com_t *, 580ba2cbe9Sxc151355 ieee80211_node_table_t *, const char *, int, int, 590ba2cbe9Sxc151355 void (*timeout)(ieee80211_node_table_t *)); 600ba2cbe9Sxc151355 static void ieee80211_node_table_cleanup(ieee80211_node_table_t *); 610ba2cbe9Sxc151355 620ba2cbe9Sxc151355 /* 630ba2cbe9Sxc151355 * association failures before ignored 640ba2cbe9Sxc151355 * The failure may be caused by the response frame is lost for 650ba2cbe9Sxc151355 * environmental reason. So Try associate more than once before 660ba2cbe9Sxc151355 * ignore the node 670ba2cbe9Sxc151355 */ 680ba2cbe9Sxc151355 #define IEEE80211_STA_FAILS_MAX 2 690ba2cbe9Sxc151355 700ba2cbe9Sxc151355 /* 710ba2cbe9Sxc151355 * Initialize node database management callbacks for the interface. 720ba2cbe9Sxc151355 * This function is called by ieee80211_attach(). These callback 730ba2cbe9Sxc151355 * functions may be overridden in special circumstances, as long as 740ba2cbe9Sxc151355 * as this is done after calling ieee80211_attach() and prior to any 750ba2cbe9Sxc151355 * other call which may allocate a node 760ba2cbe9Sxc151355 */ 770ba2cbe9Sxc151355 void 780ba2cbe9Sxc151355 ieee80211_node_attach(ieee80211com_t *ic) 790ba2cbe9Sxc151355 { 800ba2cbe9Sxc151355 struct ieee80211_impl *im = ic->ic_private; 810ba2cbe9Sxc151355 820ba2cbe9Sxc151355 ic->ic_node_alloc = ieee80211_node_alloc; 830ba2cbe9Sxc151355 ic->ic_node_free = ieee80211_node_free; 840ba2cbe9Sxc151355 ic->ic_node_cleanup = ieee80211_node_cleanup; 850ba2cbe9Sxc151355 ic->ic_node_getrssi = ieee80211_node_getrssi; 860ba2cbe9Sxc151355 870ba2cbe9Sxc151355 /* default station inactivity timer setings */ 880ba2cbe9Sxc151355 im->im_inact_init = IEEE80211_INACT_INIT; 890ba2cbe9Sxc151355 im->im_inact_assoc = IEEE80211_INACT_ASSOC; 900ba2cbe9Sxc151355 im->im_inact_run = IEEE80211_INACT_RUN; 910ba2cbe9Sxc151355 im->im_inact_probe = IEEE80211_INACT_PROBE; 920ba2cbe9Sxc151355 } 930ba2cbe9Sxc151355 940ba2cbe9Sxc151355 /* 950ba2cbe9Sxc151355 * Initialize node databases and the ic_bss node element. 960ba2cbe9Sxc151355 */ 970ba2cbe9Sxc151355 void 980ba2cbe9Sxc151355 ieee80211_node_lateattach(ieee80211com_t *ic) 990ba2cbe9Sxc151355 { 1000ba2cbe9Sxc151355 /* 1010ba2cbe9Sxc151355 * Calculate ic_tim_bitmap size in bytes 1020ba2cbe9Sxc151355 * IEEE80211_AID_MAX defines maximum bits in ic_tim_bitmap 1030ba2cbe9Sxc151355 */ 1040ba2cbe9Sxc151355 ic->ic_tim_len = howmany(IEEE80211_AID_MAX, 8) * sizeof (uint8_t); 1050ba2cbe9Sxc151355 1060ba2cbe9Sxc151355 ieee80211_node_table_init(ic, &ic->ic_sta, "station", 1070ba2cbe9Sxc151355 IEEE80211_INACT_INIT, IEEE80211_WEP_NKID, 1080ba2cbe9Sxc151355 ieee80211_timeout_stations); 1090ba2cbe9Sxc151355 ieee80211_node_table_init(ic, &ic->ic_scan, "scan", 1100ba2cbe9Sxc151355 IEEE80211_INACT_SCAN, 0, ieee80211_timeout_scan_candidates); 1110ba2cbe9Sxc151355 1120ba2cbe9Sxc151355 ieee80211_reset_bss(ic); 1130ba2cbe9Sxc151355 } 1140ba2cbe9Sxc151355 1150ba2cbe9Sxc151355 /* 1160ba2cbe9Sxc151355 * Destroy all node databases and is usually called during device detach 1170ba2cbe9Sxc151355 */ 1180ba2cbe9Sxc151355 void 1190ba2cbe9Sxc151355 ieee80211_node_detach(ieee80211com_t *ic) 1200ba2cbe9Sxc151355 { 1210ba2cbe9Sxc151355 /* Node Detach */ 1220ba2cbe9Sxc151355 if (ic->ic_bss != NULL) { 1230ba2cbe9Sxc151355 ieee80211_free_node(ic->ic_bss); 1240ba2cbe9Sxc151355 ic->ic_bss = NULL; 1250ba2cbe9Sxc151355 } 1260ba2cbe9Sxc151355 ieee80211_node_table_cleanup(&ic->ic_scan); 1270ba2cbe9Sxc151355 ieee80211_node_table_cleanup(&ic->ic_sta); 1280ba2cbe9Sxc151355 } 1290ba2cbe9Sxc151355 1300ba2cbe9Sxc151355 /* 1310ba2cbe9Sxc151355 * Increase a node's reference count 1320ba2cbe9Sxc151355 * 1330ba2cbe9Sxc151355 * Return pointer to the node 1340ba2cbe9Sxc151355 */ 1350ba2cbe9Sxc151355 ieee80211_node_t * 1360ba2cbe9Sxc151355 ieee80211_ref_node(ieee80211_node_t *in) 1370ba2cbe9Sxc151355 { 1380ba2cbe9Sxc151355 ieee80211_node_incref(in); 1390ba2cbe9Sxc151355 return (in); 1400ba2cbe9Sxc151355 } 1410ba2cbe9Sxc151355 1420ba2cbe9Sxc151355 /* 1430ba2cbe9Sxc151355 * Dexrease a node's reference count 1440ba2cbe9Sxc151355 */ 1450ba2cbe9Sxc151355 void 1460ba2cbe9Sxc151355 ieee80211_unref_node(ieee80211_node_t **in) 1470ba2cbe9Sxc151355 { 1480ba2cbe9Sxc151355 ieee80211_node_decref(*in); 1490ba2cbe9Sxc151355 *in = NULL; /* guard against use */ 1500ba2cbe9Sxc151355 } 1510ba2cbe9Sxc151355 1520ba2cbe9Sxc151355 /* 1530ba2cbe9Sxc151355 * Mark ports authorized for data traffic. This function is usually 1540ba2cbe9Sxc151355 * used by 802.1x authenticator. 1550ba2cbe9Sxc151355 */ 1560ba2cbe9Sxc151355 void 1570ba2cbe9Sxc151355 ieee80211_node_authorize(ieee80211_node_t *in) 1580ba2cbe9Sxc151355 { 1590ba2cbe9Sxc151355 ieee80211_impl_t *im = in->in_ic->ic_private; 1600ba2cbe9Sxc151355 1610ba2cbe9Sxc151355 in->in_flags |= IEEE80211_NODE_AUTH; 1620ba2cbe9Sxc151355 in->in_inact_reload = im->im_inact_run; 16319d332feSfei feng - Sun Microsystems - Beijing China in->in_inact = in->in_inact_reload; 1640ba2cbe9Sxc151355 } 1650ba2cbe9Sxc151355 1660ba2cbe9Sxc151355 /* 1670ba2cbe9Sxc151355 * Mark ports unauthorized for data traffic. This function is usually 1680ba2cbe9Sxc151355 * used by 802.1x authenticator. 1690ba2cbe9Sxc151355 */ 1700ba2cbe9Sxc151355 void 1710ba2cbe9Sxc151355 ieee80211_node_unauthorize(ieee80211_node_t *in) 1720ba2cbe9Sxc151355 { 1730ba2cbe9Sxc151355 in->in_flags &= ~IEEE80211_NODE_AUTH; 1740ba2cbe9Sxc151355 } 1750ba2cbe9Sxc151355 1760ba2cbe9Sxc151355 /* 1770ba2cbe9Sxc151355 * Set/change the channel. The rate set is also updated as 1780ba2cbe9Sxc151355 * to insure a consistent view by drivers. 1790ba2cbe9Sxc151355 */ 1800ba2cbe9Sxc151355 static void 1810ba2cbe9Sxc151355 ieee80211_node_setchan(ieee80211com_t *ic, ieee80211_node_t *in, 1820ba2cbe9Sxc151355 struct ieee80211_channel *chan) 1830ba2cbe9Sxc151355 { 1840ba2cbe9Sxc151355 if (chan == IEEE80211_CHAN_ANYC) 1850ba2cbe9Sxc151355 chan = ic->ic_curchan; 1860ba2cbe9Sxc151355 in->in_chan = chan; 187*e2cf88acSQuaker Fang if (IEEE80211_IS_CHAN_HT(chan)) { 188*e2cf88acSQuaker Fang /* 189*e2cf88acSQuaker Fang * Gotta be careful here; the rate set returned by 190*e2cf88acSQuaker Fang * ieee80211_get_suprates is actually any HT rate 191*e2cf88acSQuaker Fang * set so blindly copying it will be bad. We must 192*e2cf88acSQuaker Fang * install the legacy rate est in ni_rates and the 193*e2cf88acSQuaker Fang * HT rate set in ni_htrates. 194*e2cf88acSQuaker Fang */ 195*e2cf88acSQuaker Fang in->in_htrates = *ieee80211_get_suphtrates(ic, chan); 196*e2cf88acSQuaker Fang } 197*e2cf88acSQuaker Fang in->in_rates = *ieee80211_get_suprates(ic, chan); 198*e2cf88acSQuaker Fang /* in->in_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)]; */ 1990ba2cbe9Sxc151355 } 2000ba2cbe9Sxc151355 2010ba2cbe9Sxc151355 /* 2020ba2cbe9Sxc151355 * Initialize the channel set to scan based on the available channels 2030ba2cbe9Sxc151355 * and the current PHY mode. 2040ba2cbe9Sxc151355 */ 2050ba2cbe9Sxc151355 static void 2060ba2cbe9Sxc151355 ieee80211_reset_scan(ieee80211com_t *ic) 2070ba2cbe9Sxc151355 { 2080ba2cbe9Sxc151355 ieee80211_impl_t *im = ic->ic_private; 2090ba2cbe9Sxc151355 2100ba2cbe9Sxc151355 if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) { 2110ba2cbe9Sxc151355 (void) memset(im->im_chan_scan, 0, sizeof (im->im_chan_scan)); 2120ba2cbe9Sxc151355 ieee80211_setbit(im->im_chan_scan, 2130ba2cbe9Sxc151355 ieee80211_chan2ieee(ic, ic->ic_des_chan)); 2140ba2cbe9Sxc151355 } else { 2150ba2cbe9Sxc151355 bcopy(ic->ic_chan_active, im->im_chan_scan, 2160ba2cbe9Sxc151355 sizeof (ic->ic_chan_active)); 2170ba2cbe9Sxc151355 } 2180ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_SCAN, "ieee80211_reset_scan(): " 2190ba2cbe9Sxc151355 "start chan %u\n", ieee80211_chan2ieee(ic, ic->ic_curchan)); 2200ba2cbe9Sxc151355 } 2210ba2cbe9Sxc151355 2220ba2cbe9Sxc151355 /* 2230ba2cbe9Sxc151355 * Begin an active scan. Initialize the node cache. The scan 2240ba2cbe9Sxc151355 * begins on the next radio channel by calling ieee80211_next_scan(). 2250ba2cbe9Sxc151355 * The actual scanning is not automated. The driver itself 2260ba2cbe9Sxc151355 * only handles setting the radio frequency and stepping through 2270ba2cbe9Sxc151355 * the channels. 2280ba2cbe9Sxc151355 */ 2290ba2cbe9Sxc151355 void 2300ba2cbe9Sxc151355 ieee80211_begin_scan(ieee80211com_t *ic, boolean_t reset) 2310ba2cbe9Sxc151355 { 2320ba2cbe9Sxc151355 IEEE80211_LOCK(ic); 2330ba2cbe9Sxc151355 2340ba2cbe9Sxc151355 if (ic->ic_opmode != IEEE80211_M_HOSTAP) 2350ba2cbe9Sxc151355 ic->ic_flags |= IEEE80211_F_ASCAN; 2360ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_SCAN, 2370ba2cbe9Sxc151355 "begin %s scan in %s mode on channel %u\n", 2380ba2cbe9Sxc151355 (ic->ic_flags & IEEE80211_F_ASCAN) ? "active" : "passive", 2390ba2cbe9Sxc151355 ieee80211_phymode_name[ic->ic_curmode], 2400ba2cbe9Sxc151355 ieee80211_chan2ieee(ic, ic->ic_curchan)); 2410ba2cbe9Sxc151355 2420ba2cbe9Sxc151355 /* 2430ba2cbe9Sxc151355 * Clear scan state and flush any previously seen AP's. 2440ba2cbe9Sxc151355 */ 2450ba2cbe9Sxc151355 ieee80211_reset_scan(ic); 2460ba2cbe9Sxc151355 if (reset) 2470ba2cbe9Sxc151355 ieee80211_free_allnodes(&ic->ic_scan); 2480ba2cbe9Sxc151355 2490ba2cbe9Sxc151355 ic->ic_flags |= IEEE80211_F_SCAN; 2500ba2cbe9Sxc151355 IEEE80211_UNLOCK(ic); 2510ba2cbe9Sxc151355 2520ba2cbe9Sxc151355 /* Scan the next channel. */ 2530ba2cbe9Sxc151355 ieee80211_next_scan(ic); 2540ba2cbe9Sxc151355 } 2550ba2cbe9Sxc151355 2560ba2cbe9Sxc151355 /* 2570ba2cbe9Sxc151355 * Switch to the next channel marked for scanning. 2580ba2cbe9Sxc151355 * A driver is expected to first call ieee80211_begin_scan(), 2590ba2cbe9Sxc151355 * to initialize the node cache, then set the radio channel 2600ba2cbe9Sxc151355 * on the device. And then after a certain time has elapsed, 2610ba2cbe9Sxc151355 * call ieee80211_next_scan() to move to the next channel. 2620ba2cbe9Sxc151355 * Typically, a timeout routine is used to automate this process. 2630ba2cbe9Sxc151355 */ 2640ba2cbe9Sxc151355 void 2650ba2cbe9Sxc151355 ieee80211_next_scan(ieee80211com_t *ic) 2660ba2cbe9Sxc151355 { 2670ba2cbe9Sxc151355 ieee80211_impl_t *im = ic->ic_private; 2680ba2cbe9Sxc151355 struct ieee80211_channel *chan; 2690ba2cbe9Sxc151355 2700ba2cbe9Sxc151355 IEEE80211_LOCK(ic); 2710ba2cbe9Sxc151355 /* 2720ba2cbe9Sxc151355 * Insure any previous mgt frame timeouts don't fire. 2730ba2cbe9Sxc151355 * This assumes the driver does the right thing in 2740ba2cbe9Sxc151355 * flushing anything queued in the driver and below. 2750ba2cbe9Sxc151355 */ 2760ba2cbe9Sxc151355 im->im_mgt_timer = 0; 2770ba2cbe9Sxc151355 2780ba2cbe9Sxc151355 chan = ic->ic_curchan; 2790ba2cbe9Sxc151355 do { 2800ba2cbe9Sxc151355 if (++chan > &ic->ic_sup_channels[IEEE80211_CHAN_MAX]) 2810ba2cbe9Sxc151355 chan = &ic->ic_sup_channels[0]; 2820ba2cbe9Sxc151355 if (ieee80211_isset(im->im_chan_scan, 2830ba2cbe9Sxc151355 ieee80211_chan2ieee(ic, chan))) { 2840ba2cbe9Sxc151355 ieee80211_clrbit(im->im_chan_scan, 2850ba2cbe9Sxc151355 ieee80211_chan2ieee(ic, chan)); 2860ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_SCAN, 2870ba2cbe9Sxc151355 "ieee80211_next_scan: chan %d->%d\n", 2880ba2cbe9Sxc151355 ieee80211_chan2ieee(ic, ic->ic_curchan), 2890ba2cbe9Sxc151355 ieee80211_chan2ieee(ic, chan)); 2900ba2cbe9Sxc151355 ic->ic_curchan = chan; 2910ba2cbe9Sxc151355 /* 2920ba2cbe9Sxc151355 * drivers should do this as needed, 2930ba2cbe9Sxc151355 * for now maintain compatibility 2940ba2cbe9Sxc151355 */ 2950ba2cbe9Sxc151355 ic->ic_bss->in_rates = 2960ba2cbe9Sxc151355 ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)]; 2970ba2cbe9Sxc151355 IEEE80211_UNLOCK(ic); 2980ba2cbe9Sxc151355 ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); 2990ba2cbe9Sxc151355 return; 3000ba2cbe9Sxc151355 } 3010ba2cbe9Sxc151355 } while (chan != ic->ic_curchan); 3020ba2cbe9Sxc151355 IEEE80211_UNLOCK(ic); 3030ba2cbe9Sxc151355 ieee80211_end_scan(ic); 3040ba2cbe9Sxc151355 } 3050ba2cbe9Sxc151355 3060ba2cbe9Sxc151355 /* 3070ba2cbe9Sxc151355 * Copy useful state from node obss into nbss. 3080ba2cbe9Sxc151355 */ 3090ba2cbe9Sxc151355 static void 3100ba2cbe9Sxc151355 ieee80211_copy_bss(ieee80211_node_t *nbss, const ieee80211_node_t *obss) 3110ba2cbe9Sxc151355 { 3120ba2cbe9Sxc151355 /* propagate useful state */ 3130ba2cbe9Sxc151355 nbss->in_authmode = obss->in_authmode; 3140ba2cbe9Sxc151355 nbss->in_txpower = obss->in_txpower; 3150ba2cbe9Sxc151355 nbss->in_vlan = obss->in_vlan; 3160ba2cbe9Sxc151355 } 3170ba2cbe9Sxc151355 3180ba2cbe9Sxc151355 /* 3190ba2cbe9Sxc151355 * Setup the net80211 specific portion of an interface's softc, ic, 3200ba2cbe9Sxc151355 * for use in IBSS mode 3210ba2cbe9Sxc151355 */ 3220ba2cbe9Sxc151355 void 3230ba2cbe9Sxc151355 ieee80211_create_ibss(ieee80211com_t *ic, struct ieee80211_channel *chan) 3240ba2cbe9Sxc151355 { 3250ba2cbe9Sxc151355 ieee80211_impl_t *im = ic->ic_private; 3260ba2cbe9Sxc151355 ieee80211_node_table_t *nt; 3270ba2cbe9Sxc151355 ieee80211_node_t *in; 3280ba2cbe9Sxc151355 3290ba2cbe9Sxc151355 IEEE80211_LOCK_ASSERT(ic); 3300ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_SCAN, "ieee80211_create_ibss: " 3310ba2cbe9Sxc151355 "creating ibss\n"); 3320ba2cbe9Sxc151355 3330ba2cbe9Sxc151355 /* 3340ba2cbe9Sxc151355 * Create the station/neighbor table. Note that for adhoc 3350ba2cbe9Sxc151355 * mode we make the initial inactivity timer longer since 3360ba2cbe9Sxc151355 * we create nodes only through discovery and they typically 3370ba2cbe9Sxc151355 * are long-lived associations. 3380ba2cbe9Sxc151355 */ 3390ba2cbe9Sxc151355 nt = &ic->ic_sta; 3400ba2cbe9Sxc151355 IEEE80211_NODE_LOCK(nt); 3410ba2cbe9Sxc151355 nt->nt_name = "neighbor"; 3420ba2cbe9Sxc151355 nt->nt_inact_init = im->im_inact_run; 3430ba2cbe9Sxc151355 IEEE80211_NODE_UNLOCK(nt); 3440ba2cbe9Sxc151355 3450ba2cbe9Sxc151355 in = ieee80211_alloc_node(ic, &ic->ic_sta, ic->ic_macaddr); 3460ba2cbe9Sxc151355 if (in == NULL) { 3470ba2cbe9Sxc151355 ieee80211_err("ieee80211_create_ibss(): alloc node failed\n"); 3480ba2cbe9Sxc151355 return; 3490ba2cbe9Sxc151355 } 3500ba2cbe9Sxc151355 IEEE80211_ADDR_COPY(in->in_bssid, ic->ic_macaddr); 3510ba2cbe9Sxc151355 in->in_esslen = ic->ic_des_esslen; 3520ba2cbe9Sxc151355 (void) memcpy(in->in_essid, ic->ic_des_essid, in->in_esslen); 3530ba2cbe9Sxc151355 ieee80211_copy_bss(in, ic->ic_bss); 3540ba2cbe9Sxc151355 in->in_intval = ic->ic_bintval; 3550ba2cbe9Sxc151355 if (ic->ic_flags & IEEE80211_F_PRIVACY) 3560ba2cbe9Sxc151355 in->in_capinfo |= IEEE80211_CAPINFO_PRIVACY; 3570ba2cbe9Sxc151355 if (ic->ic_phytype == IEEE80211_T_FH) { 3580ba2cbe9Sxc151355 in->in_fhdwell = 200; 3590ba2cbe9Sxc151355 in->in_fhindex = 1; 3600ba2cbe9Sxc151355 } 3610ba2cbe9Sxc151355 switch (ic->ic_opmode) { 3620ba2cbe9Sxc151355 case IEEE80211_M_IBSS: 3630ba2cbe9Sxc151355 ic->ic_flags |= IEEE80211_F_SIBSS; 3640ba2cbe9Sxc151355 in->in_capinfo |= IEEE80211_CAPINFO_IBSS; 3650ba2cbe9Sxc151355 if (ic->ic_flags & IEEE80211_F_DESBSSID) 3660ba2cbe9Sxc151355 IEEE80211_ADDR_COPY(in->in_bssid, ic->ic_des_bssid); 3670ba2cbe9Sxc151355 else 3680ba2cbe9Sxc151355 in->in_bssid[0] |= 0x02; /* local bit for IBSS */ 3690ba2cbe9Sxc151355 break; 3700ba2cbe9Sxc151355 case IEEE80211_M_AHDEMO: 3710ba2cbe9Sxc151355 if (ic->ic_flags & IEEE80211_F_DESBSSID) 3720ba2cbe9Sxc151355 IEEE80211_ADDR_COPY(in->in_bssid, ic->ic_des_bssid); 3730ba2cbe9Sxc151355 else 3740ba2cbe9Sxc151355 (void) memset(in->in_bssid, 0, IEEE80211_ADDR_LEN); 3750ba2cbe9Sxc151355 break; 3760ba2cbe9Sxc151355 default: 3770ba2cbe9Sxc151355 ieee80211_err("ieee80211_create_ibss(): " 3780ba2cbe9Sxc151355 "wrong opmode %u to creat IBSS, abort\n", 3790ba2cbe9Sxc151355 ic->ic_opmode); 3800ba2cbe9Sxc151355 ieee80211_free_node(in); 3810ba2cbe9Sxc151355 return; 3820ba2cbe9Sxc151355 } 3830ba2cbe9Sxc151355 3840ba2cbe9Sxc151355 /* 3850ba2cbe9Sxc151355 * Fix the channel and related attributes. 3860ba2cbe9Sxc151355 */ 3870ba2cbe9Sxc151355 ieee80211_node_setchan(ic, in, chan); 3880ba2cbe9Sxc151355 ic->ic_curchan = chan; 3890ba2cbe9Sxc151355 ic->ic_curmode = ieee80211_chan2mode(ic, chan); 3900ba2cbe9Sxc151355 /* 3910ba2cbe9Sxc151355 * Do mode-specific rate setup. 3920ba2cbe9Sxc151355 */ 3930ba2cbe9Sxc151355 ieee80211_setbasicrates(&in->in_rates, ic->ic_curmode); 3940ba2cbe9Sxc151355 IEEE80211_UNLOCK(ic); 395a399b765Szf162725 ieee80211_sta_join(ic, ieee80211_ref_node(in)); 3960ba2cbe9Sxc151355 IEEE80211_LOCK(ic); 3970ba2cbe9Sxc151355 } 3980ba2cbe9Sxc151355 3990ba2cbe9Sxc151355 void 4000ba2cbe9Sxc151355 ieee80211_reset_bss(ieee80211com_t *ic) 4010ba2cbe9Sxc151355 { 4020ba2cbe9Sxc151355 ieee80211_node_t *in; 4030ba2cbe9Sxc151355 ieee80211_node_t *obss; 4040ba2cbe9Sxc151355 40519d332feSfei feng - Sun Microsystems - Beijing China ieee80211_node_table_reset(&ic->ic_sta); 40619d332feSfei feng - Sun Microsystems - Beijing China ieee80211_reset_erp(ic); 40719d332feSfei feng - Sun Microsystems - Beijing China 4080ba2cbe9Sxc151355 in = ieee80211_alloc_node(ic, &ic->ic_scan, ic->ic_macaddr); 4090ba2cbe9Sxc151355 ASSERT(in != NULL); 4100ba2cbe9Sxc151355 obss = ic->ic_bss; 4110ba2cbe9Sxc151355 ic->ic_bss = ieee80211_ref_node(in); 4120ba2cbe9Sxc151355 if (obss != NULL) { 4130ba2cbe9Sxc151355 ieee80211_copy_bss(in, obss); 4140ba2cbe9Sxc151355 in->in_intval = ic->ic_bintval; 4150ba2cbe9Sxc151355 ieee80211_free_node(obss); 4160ba2cbe9Sxc151355 } 4170ba2cbe9Sxc151355 } 4180ba2cbe9Sxc151355 4190ba2cbe9Sxc151355 static int 4200ba2cbe9Sxc151355 ieee80211_match_bss(ieee80211com_t *ic, ieee80211_node_t *in) 4210ba2cbe9Sxc151355 { 4220ba2cbe9Sxc151355 uint8_t rate; 4230ba2cbe9Sxc151355 int fail; 4240ba2cbe9Sxc151355 4250ba2cbe9Sxc151355 fail = 0; 4260ba2cbe9Sxc151355 if (ieee80211_isclr(ic->ic_chan_active, 4270ba2cbe9Sxc151355 ieee80211_chan2ieee(ic, in->in_chan))) { 4280ba2cbe9Sxc151355 fail |= IEEE80211_BADCHAN; 4290ba2cbe9Sxc151355 } 4300ba2cbe9Sxc151355 if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && 4310ba2cbe9Sxc151355 in->in_chan != ic->ic_des_chan) { 4320ba2cbe9Sxc151355 fail |= IEEE80211_BADCHAN; 4330ba2cbe9Sxc151355 } 4340ba2cbe9Sxc151355 if (ic->ic_opmode == IEEE80211_M_IBSS) { 4350ba2cbe9Sxc151355 if (!(in->in_capinfo & IEEE80211_CAPINFO_IBSS)) 4360ba2cbe9Sxc151355 fail |= IEEE80211_BADOPMODE; 4370ba2cbe9Sxc151355 } else { 4380ba2cbe9Sxc151355 if (!(in->in_capinfo & IEEE80211_CAPINFO_ESS)) 4390ba2cbe9Sxc151355 fail |= IEEE80211_BADOPMODE; 4400ba2cbe9Sxc151355 } 4410ba2cbe9Sxc151355 if (ic->ic_flags & IEEE80211_F_PRIVACY) { 4420ba2cbe9Sxc151355 if (!(in->in_capinfo & IEEE80211_CAPINFO_PRIVACY)) 4430ba2cbe9Sxc151355 fail |= IEEE80211_BADPRIVACY; 4440ba2cbe9Sxc151355 } else { 4450ba2cbe9Sxc151355 if (in->in_capinfo & IEEE80211_CAPINFO_PRIVACY) 4460ba2cbe9Sxc151355 fail |= IEEE80211_BADPRIVACY; 4470ba2cbe9Sxc151355 } 448*e2cf88acSQuaker Fang rate = ieee80211_fix_rate(in, &in->in_rates, 449*e2cf88acSQuaker Fang IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE); 4500ba2cbe9Sxc151355 if (rate & IEEE80211_RATE_BASIC) 4510ba2cbe9Sxc151355 fail |= IEEE80211_BADRATE; 4520ba2cbe9Sxc151355 if (ic->ic_des_esslen != 0 && 4530ba2cbe9Sxc151355 (in->in_esslen != ic->ic_des_esslen || 4540ba2cbe9Sxc151355 memcmp(in->in_essid, ic->ic_des_essid, ic->ic_des_esslen) != 0)) { 4550ba2cbe9Sxc151355 fail |= IEEE80211_BADESSID; 4560ba2cbe9Sxc151355 } 4570ba2cbe9Sxc151355 if ((ic->ic_flags & IEEE80211_F_DESBSSID) && 4580ba2cbe9Sxc151355 !IEEE80211_ADDR_EQ(ic->ic_des_bssid, in->in_bssid)) { 4590ba2cbe9Sxc151355 fail |= IEEE80211_BADBSSID; 4600ba2cbe9Sxc151355 } 4610ba2cbe9Sxc151355 if (in->in_fails >= IEEE80211_STA_FAILS_MAX) 4620ba2cbe9Sxc151355 fail |= IEEE80211_NODEFAIL; 4630ba2cbe9Sxc151355 4640ba2cbe9Sxc151355 return (fail); 4650ba2cbe9Sxc151355 } 4660ba2cbe9Sxc151355 4670ba2cbe9Sxc151355 #define IEEE80211_MAXRATE(_rs) \ 4680ba2cbe9Sxc151355 ((_rs).ir_rates[(_rs).ir_nrates - 1] & IEEE80211_RATE_VAL) 4690ba2cbe9Sxc151355 4700ba2cbe9Sxc151355 /* 4710ba2cbe9Sxc151355 * Compare the capabilities of node a with node b and decide which is 4720ba2cbe9Sxc151355 * more desirable (return b if b is considered better than a). Note 4730ba2cbe9Sxc151355 * that we assume compatibility/usability has already been checked 4740ba2cbe9Sxc151355 * so we don't need to (e.g. validate whether privacy is supported). 4750ba2cbe9Sxc151355 * Used to select the best scan candidate for association in a BSS. 4760ba2cbe9Sxc151355 * 4770ba2cbe9Sxc151355 * Return desired node 4780ba2cbe9Sxc151355 */ 4790ba2cbe9Sxc151355 static ieee80211_node_t * 4800ba2cbe9Sxc151355 ieee80211_node_compare(ieee80211com_t *ic, ieee80211_node_t *a, 4810ba2cbe9Sxc151355 ieee80211_node_t *b) 4820ba2cbe9Sxc151355 { 4830ba2cbe9Sxc151355 uint8_t maxa; 4840ba2cbe9Sxc151355 uint8_t maxb; 4850ba2cbe9Sxc151355 uint8_t rssia; 4860ba2cbe9Sxc151355 uint8_t rssib; 4870ba2cbe9Sxc151355 4880ba2cbe9Sxc151355 /* privacy support preferred */ 4890ba2cbe9Sxc151355 if ((a->in_capinfo & IEEE80211_CAPINFO_PRIVACY) && 4900ba2cbe9Sxc151355 !(b->in_capinfo & IEEE80211_CAPINFO_PRIVACY)) { 4910ba2cbe9Sxc151355 return (a); 4920ba2cbe9Sxc151355 } 4930ba2cbe9Sxc151355 if (!(a->in_capinfo & IEEE80211_CAPINFO_PRIVACY) && 4940ba2cbe9Sxc151355 (b->in_capinfo & IEEE80211_CAPINFO_PRIVACY)) { 4950ba2cbe9Sxc151355 return (b); 4960ba2cbe9Sxc151355 } 4970ba2cbe9Sxc151355 4980ba2cbe9Sxc151355 /* compare count of previous failures */ 4990ba2cbe9Sxc151355 if (b->in_fails != a->in_fails) 5000ba2cbe9Sxc151355 return ((a->in_fails > b->in_fails) ? b : a); 5010ba2cbe9Sxc151355 5020ba2cbe9Sxc151355 rssia = ic->ic_node_getrssi(a); 5030ba2cbe9Sxc151355 rssib = ic->ic_node_getrssi(b); 5040ba2cbe9Sxc151355 if (ABS(rssib - rssia) < IEEE80211_RSSI_CMP_THRESHOLD) { 5050ba2cbe9Sxc151355 /* best/max rate preferred if signal level close enough */ 5060ba2cbe9Sxc151355 maxa = IEEE80211_MAXRATE(a->in_rates); 5070ba2cbe9Sxc151355 maxb = IEEE80211_MAXRATE(b->in_rates); 5080ba2cbe9Sxc151355 if (maxa != maxb) 5090ba2cbe9Sxc151355 return ((maxb > maxa) ? b : a); 5100ba2cbe9Sxc151355 /* for now just prefer 5Ghz band to all other bands */ 5110ba2cbe9Sxc151355 if (IEEE80211_IS_CHAN_5GHZ(a->in_chan) && 5120ba2cbe9Sxc151355 !IEEE80211_IS_CHAN_5GHZ(b->in_chan)) { 5130ba2cbe9Sxc151355 return (a); 5140ba2cbe9Sxc151355 } 5150ba2cbe9Sxc151355 if (!IEEE80211_IS_CHAN_5GHZ(a->in_chan) && 5160ba2cbe9Sxc151355 IEEE80211_IS_CHAN_5GHZ(b->in_chan)) { 5170ba2cbe9Sxc151355 return (b); 5180ba2cbe9Sxc151355 } 5190ba2cbe9Sxc151355 } 5200ba2cbe9Sxc151355 /* all things being equal, compare signal level */ 5210ba2cbe9Sxc151355 return ((rssib > rssia) ? b : a); 5220ba2cbe9Sxc151355 } 5230ba2cbe9Sxc151355 5240ba2cbe9Sxc151355 /* 5250ba2cbe9Sxc151355 * Mark an ongoing scan stopped. 5260ba2cbe9Sxc151355 */ 5270ba2cbe9Sxc151355 void 5280ba2cbe9Sxc151355 ieee80211_cancel_scan(ieee80211com_t *ic) 5290ba2cbe9Sxc151355 { 5300ba2cbe9Sxc151355 IEEE80211_LOCK(ic); 5310ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_SCAN, "ieee80211_cancel_scan()" 5320ba2cbe9Sxc151355 "end %s scan\n", 5330ba2cbe9Sxc151355 (ic->ic_flags & IEEE80211_F_ASCAN) ? "active" : "passive"); 5340ba2cbe9Sxc151355 ic->ic_flags &= ~(IEEE80211_F_SCAN | IEEE80211_F_ASCAN); 5350ba2cbe9Sxc151355 cv_broadcast(&((ieee80211_impl_t *)ic->ic_private)->im_scan_cv); 5360ba2cbe9Sxc151355 IEEE80211_UNLOCK(ic); 5370ba2cbe9Sxc151355 } 5380ba2cbe9Sxc151355 5390ba2cbe9Sxc151355 /* 5400ba2cbe9Sxc151355 * Complete a scan of potential channels. It is called by 5410ba2cbe9Sxc151355 * ieee80211_next_scan() when the state machine has performed 5420ba2cbe9Sxc151355 * a full cycle of scaning on all available radio channels. 5430ba2cbe9Sxc151355 * ieee80211_end_scan() will inspect the node cache for suitable 5440ba2cbe9Sxc151355 * APs found during scaning, and associate with one, should 5450ba2cbe9Sxc151355 * the parameters of the node match those of the configuration 5460ba2cbe9Sxc151355 * requested from userland. 5470ba2cbe9Sxc151355 */ 5480ba2cbe9Sxc151355 void 5490ba2cbe9Sxc151355 ieee80211_end_scan(ieee80211com_t *ic) 5500ba2cbe9Sxc151355 { 5510ba2cbe9Sxc151355 ieee80211_node_table_t *nt = &ic->ic_scan; 5520ba2cbe9Sxc151355 ieee80211_node_t *in; 5530ba2cbe9Sxc151355 ieee80211_node_t *selbs; 5540ba2cbe9Sxc151355 5550ba2cbe9Sxc151355 ieee80211_cancel_scan(ic); 556a399b765Szf162725 /* notify SCAN done */ 557a399b765Szf162725 ieee80211_notify(ic, EVENT_SCAN_RESULTS); 5580ba2cbe9Sxc151355 IEEE80211_LOCK(ic); 5590ba2cbe9Sxc151355 5600ba2cbe9Sxc151355 /* 5610ba2cbe9Sxc151355 * Automatic sequencing; look for a candidate and 5620ba2cbe9Sxc151355 * if found join the network. 5630ba2cbe9Sxc151355 */ 5640ba2cbe9Sxc151355 /* NB: unlocked read should be ok */ 5650ba2cbe9Sxc151355 in = list_head(&nt->nt_node); 566a399b765Szf162725 if (in == NULL && (ic->ic_flags & IEEE80211_F_WPA) == 0) { 5670ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_SCAN, "ieee80211_end_scan: " 5680ba2cbe9Sxc151355 "no scan candidate\n"); 5690ba2cbe9Sxc151355 notfound: 5700ba2cbe9Sxc151355 if (ic->ic_opmode == IEEE80211_M_IBSS && 5710ba2cbe9Sxc151355 (ic->ic_flags & IEEE80211_F_IBSSON) && 5720ba2cbe9Sxc151355 ic->ic_des_esslen != 0) { 5730ba2cbe9Sxc151355 ieee80211_create_ibss(ic, ic->ic_ibss_chan); 5740ba2cbe9Sxc151355 IEEE80211_UNLOCK(ic); 5750ba2cbe9Sxc151355 return; 5760ba2cbe9Sxc151355 } 5770ba2cbe9Sxc151355 5780ba2cbe9Sxc151355 /* 5790ba2cbe9Sxc151355 * Reset the list of channels to scan and start again. 5800ba2cbe9Sxc151355 */ 5810ba2cbe9Sxc151355 ieee80211_reset_scan(ic); 5820ba2cbe9Sxc151355 ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN; 5830ba2cbe9Sxc151355 IEEE80211_UNLOCK(ic); 5840ba2cbe9Sxc151355 5850ba2cbe9Sxc151355 ieee80211_next_scan(ic); 5860ba2cbe9Sxc151355 return; 5870ba2cbe9Sxc151355 } 5880ba2cbe9Sxc151355 589a399b765Szf162725 if (ic->ic_flags & IEEE80211_F_SCANONLY || 590a399b765Szf162725 ic->ic_flags & IEEE80211_F_WPA) { /* scan only */ 5910ba2cbe9Sxc151355 ic->ic_flags &= ~IEEE80211_F_SCANONLY; 5920ba2cbe9Sxc151355 IEEE80211_UNLOCK(ic); 5930ba2cbe9Sxc151355 ieee80211_new_state(ic, IEEE80211_S_INIT, -1); 5940ba2cbe9Sxc151355 return; 5950ba2cbe9Sxc151355 } 5960ba2cbe9Sxc151355 5970ba2cbe9Sxc151355 selbs = NULL; 5980ba2cbe9Sxc151355 IEEE80211_NODE_LOCK(nt); 5990ba2cbe9Sxc151355 while (in != NULL) { 6000ba2cbe9Sxc151355 if (in->in_fails >= IEEE80211_STA_FAILS_MAX) { 6010ba2cbe9Sxc151355 ieee80211_node_t *tmpin = in; 6020ba2cbe9Sxc151355 6030ba2cbe9Sxc151355 /* 6040ba2cbe9Sxc151355 * The configuration of the access points may change 6050ba2cbe9Sxc151355 * during my scan. So delete the entry for the AP 6060ba2cbe9Sxc151355 * and retry to associate if there is another beacon. 6070ba2cbe9Sxc151355 */ 6080ba2cbe9Sxc151355 in = list_next(&nt->nt_node, tmpin); 6090ba2cbe9Sxc151355 ieee80211_node_reclaim(nt, tmpin); 6100ba2cbe9Sxc151355 continue; 6110ba2cbe9Sxc151355 } 612e919bf25Seh146360 /* 613e919bf25Seh146360 * It's possible at some special moments, the in_chan will 614e919bf25Seh146360 * be none. Need to skip the null node. 615e919bf25Seh146360 */ 616e919bf25Seh146360 if (in->in_chan == IEEE80211_CHAN_ANYC) { 617e919bf25Seh146360 in = list_next(&nt->nt_node, in); 618e919bf25Seh146360 continue; 619e919bf25Seh146360 } 6200ba2cbe9Sxc151355 if (ieee80211_match_bss(ic, in) == 0) { 6210ba2cbe9Sxc151355 if (selbs == NULL) 6220ba2cbe9Sxc151355 selbs = in; 6230ba2cbe9Sxc151355 else 6240ba2cbe9Sxc151355 selbs = ieee80211_node_compare(ic, selbs, in); 6250ba2cbe9Sxc151355 } 6260ba2cbe9Sxc151355 in = list_next(&nt->nt_node, in); 6270ba2cbe9Sxc151355 } 628a399b765Szf162725 if (selbs != NULL) /* grab ref while dropping lock */ 629a399b765Szf162725 (void) ieee80211_ref_node(selbs); 6300ba2cbe9Sxc151355 IEEE80211_NODE_UNLOCK(nt); 6310ba2cbe9Sxc151355 if (selbs == NULL) 6320ba2cbe9Sxc151355 goto notfound; 6330ba2cbe9Sxc151355 IEEE80211_UNLOCK(ic); 6340ba2cbe9Sxc151355 ieee80211_sta_join(ic, selbs); 6350ba2cbe9Sxc151355 } 6360ba2cbe9Sxc151355 6370ba2cbe9Sxc151355 6380ba2cbe9Sxc151355 /* 6390ba2cbe9Sxc151355 * Handle 802.11 ad hoc network merge. The convention, set by the 6400ba2cbe9Sxc151355 * Wireless Ethernet Compatibility Alliance (WECA), is that an 802.11 6410ba2cbe9Sxc151355 * station will change its BSSID to match the "oldest" 802.11 ad hoc 6420ba2cbe9Sxc151355 * network, on the same channel, that has the station's desired SSID. 6430ba2cbe9Sxc151355 * The "oldest" 802.11 network sends beacons with the greatest TSF 6440ba2cbe9Sxc151355 * timestamp. 6450ba2cbe9Sxc151355 * The caller is assumed to validate TSF's before attempting a merge. 6460ba2cbe9Sxc151355 * 6470ba2cbe9Sxc151355 * Return B_TRUE if the BSSID changed, B_FALSE otherwise. 6480ba2cbe9Sxc151355 */ 6490ba2cbe9Sxc151355 boolean_t 6500ba2cbe9Sxc151355 ieee80211_ibss_merge(ieee80211_node_t *in) 6510ba2cbe9Sxc151355 { 6520ba2cbe9Sxc151355 ieee80211com_t *ic = in->in_ic; 6530ba2cbe9Sxc151355 6540ba2cbe9Sxc151355 if (in == ic->ic_bss || 6550ba2cbe9Sxc151355 IEEE80211_ADDR_EQ(in->in_bssid, ic->ic_bss->in_bssid)) { 6560ba2cbe9Sxc151355 /* unchanged, nothing to do */ 6570ba2cbe9Sxc151355 return (B_FALSE); 6580ba2cbe9Sxc151355 } 6590ba2cbe9Sxc151355 if (ieee80211_match_bss(ic, in) != 0) { /* capabilities mismatch */ 6600ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_ASSOC, "ieee80211_ibss_merge: " 6610ba2cbe9Sxc151355 " merge failed, capabilities mismatch\n"); 6620ba2cbe9Sxc151355 return (B_FALSE); 6630ba2cbe9Sxc151355 } 6640ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_ASSOC, "ieee80211_ibss_merge: " 6650ba2cbe9Sxc151355 "new bssid %s: %s preamble, %s slot time%s\n", 6660ba2cbe9Sxc151355 ieee80211_macaddr_sprintf(in->in_bssid), 6670ba2cbe9Sxc151355 (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ? "short" : "long", 6680ba2cbe9Sxc151355 (ic->ic_flags & IEEE80211_F_SHSLOT) ? "short" : "long", 6690ba2cbe9Sxc151355 (ic->ic_flags&IEEE80211_F_USEPROT) ? ", protection" : ""); 670a399b765Szf162725 ieee80211_sta_join(ic, ieee80211_ref_node(in)); 6710ba2cbe9Sxc151355 return (B_TRUE); 6720ba2cbe9Sxc151355 } 6730ba2cbe9Sxc151355 6740ba2cbe9Sxc151355 /* 675*e2cf88acSQuaker Fang * Change the bss channel. 676*e2cf88acSQuaker Fang */ 677*e2cf88acSQuaker Fang void 678*e2cf88acSQuaker Fang ieee80211_setcurchan(ieee80211com_t *ic, struct ieee80211_channel *c) 679*e2cf88acSQuaker Fang { 680*e2cf88acSQuaker Fang ic->ic_curchan = c; 681*e2cf88acSQuaker Fang ic->ic_curmode = ieee80211_chan2mode(ic, ic->ic_curchan); 682*e2cf88acSQuaker Fang if (ic->ic_set_channel != NULL) 683*e2cf88acSQuaker Fang ic->ic_set_channel(ic); 684*e2cf88acSQuaker Fang } 685*e2cf88acSQuaker Fang 686*e2cf88acSQuaker Fang /* 6870ba2cbe9Sxc151355 * Join the specified IBSS/BSS network. The node is assumed to 6880ba2cbe9Sxc151355 * be passed in with a held reference. 6890ba2cbe9Sxc151355 */ 6900ba2cbe9Sxc151355 void 6910ba2cbe9Sxc151355 ieee80211_sta_join(ieee80211com_t *ic, ieee80211_node_t *selbs) 6920ba2cbe9Sxc151355 { 6930ba2cbe9Sxc151355 ieee80211_impl_t *im = ic->ic_private; 6940ba2cbe9Sxc151355 ieee80211_node_t *obss; 6950ba2cbe9Sxc151355 6960ba2cbe9Sxc151355 IEEE80211_LOCK(ic); 6970ba2cbe9Sxc151355 if (ic->ic_opmode == IEEE80211_M_IBSS) { 6980ba2cbe9Sxc151355 ieee80211_node_table_t *nt; 6990ba2cbe9Sxc151355 7000ba2cbe9Sxc151355 /* 7010ba2cbe9Sxc151355 * Delete unusable rates; we've already checked 7020ba2cbe9Sxc151355 * that the negotiated rate set is acceptable. 7030ba2cbe9Sxc151355 */ 704*e2cf88acSQuaker Fang (void) ieee80211_fix_rate(selbs, &selbs->in_rates, 705*e2cf88acSQuaker Fang IEEE80211_F_DODEL); 7060ba2cbe9Sxc151355 /* 7070ba2cbe9Sxc151355 * Fillin the neighbor table 7080ba2cbe9Sxc151355 */ 7090ba2cbe9Sxc151355 nt = &ic->ic_sta; 7100ba2cbe9Sxc151355 IEEE80211_NODE_LOCK(nt); 7110ba2cbe9Sxc151355 nt->nt_name = "neighbor"; 7120ba2cbe9Sxc151355 nt->nt_inact_init = im->im_inact_run; 7130ba2cbe9Sxc151355 IEEE80211_NODE_UNLOCK(nt); 7140ba2cbe9Sxc151355 } 7150ba2cbe9Sxc151355 7160ba2cbe9Sxc151355 /* 7170ba2cbe9Sxc151355 * Committed to selbs, setup state. 7180ba2cbe9Sxc151355 */ 7190ba2cbe9Sxc151355 obss = ic->ic_bss; 720a399b765Szf162725 ic->ic_bss = selbs; /* caller assumed to bump refcnt */ 7210ba2cbe9Sxc151355 if (obss != NULL) { 7220ba2cbe9Sxc151355 ieee80211_copy_bss(selbs, obss); 7230ba2cbe9Sxc151355 ieee80211_free_node(obss); 7240ba2cbe9Sxc151355 } 7250ba2cbe9Sxc151355 ic->ic_curmode = ieee80211_chan2mode(ic, selbs->in_chan); 7260ba2cbe9Sxc151355 ic->ic_curchan = selbs->in_chan; 727239e91abShx147065 ic->ic_phytype = selbs->in_phytype; 7280ba2cbe9Sxc151355 /* 7290ba2cbe9Sxc151355 * Set the erp state (mostly the slot time) to deal with 7300ba2cbe9Sxc151355 * the auto-select case; this should be redundant if the 7310ba2cbe9Sxc151355 * mode is locked. 7320ba2cbe9Sxc151355 */ 7330ba2cbe9Sxc151355 ieee80211_reset_erp(ic); 734*e2cf88acSQuaker Fang ieee80211_wme_initparams(ic); 7350ba2cbe9Sxc151355 7360ba2cbe9Sxc151355 IEEE80211_UNLOCK(ic); 7370ba2cbe9Sxc151355 if (ic->ic_opmode == IEEE80211_M_STA) 7380ba2cbe9Sxc151355 ieee80211_new_state(ic, IEEE80211_S_AUTH, -1); 7390ba2cbe9Sxc151355 else 7400ba2cbe9Sxc151355 ieee80211_new_state(ic, IEEE80211_S_RUN, -1); 7410ba2cbe9Sxc151355 } 7420ba2cbe9Sxc151355 7430ba2cbe9Sxc151355 /* 7440ba2cbe9Sxc151355 * Leave the specified IBSS/BSS network. The node is assumed to 7450ba2cbe9Sxc151355 * be passed in with a held reference. 7460ba2cbe9Sxc151355 */ 7470ba2cbe9Sxc151355 void 7480ba2cbe9Sxc151355 ieee80211_sta_leave(ieee80211com_t *ic, ieee80211_node_t *in) 7490ba2cbe9Sxc151355 { 7500ba2cbe9Sxc151355 IEEE80211_LOCK(ic); 7510ba2cbe9Sxc151355 ic->ic_node_cleanup(in); 7520ba2cbe9Sxc151355 ieee80211_notify_node_leave(ic, in); 7530ba2cbe9Sxc151355 IEEE80211_UNLOCK(ic); 7540ba2cbe9Sxc151355 } 7550ba2cbe9Sxc151355 7560ba2cbe9Sxc151355 /* 7570ba2cbe9Sxc151355 * Allocate a node. This is the default callback function for 7580ba2cbe9Sxc151355 * ic_node_alloc. This function may be overridden by the driver 7590ba2cbe9Sxc151355 * to allocate device specific node structure. 7600ba2cbe9Sxc151355 */ 7610ba2cbe9Sxc151355 /* ARGSUSED */ 7620ba2cbe9Sxc151355 static ieee80211_node_t * 7630ba2cbe9Sxc151355 ieee80211_node_alloc(ieee80211com_t *ic) 7640ba2cbe9Sxc151355 { 7650ba2cbe9Sxc151355 return (kmem_zalloc(sizeof (ieee80211_node_t), KM_SLEEP)); 7660ba2cbe9Sxc151355 } 7670ba2cbe9Sxc151355 7680ba2cbe9Sxc151355 /* 7690ba2cbe9Sxc151355 * Cleanup a node, free any memory associated with the node. 7700ba2cbe9Sxc151355 * This is the default callback function for ic_node_cleanup 7710ba2cbe9Sxc151355 * and may be overridden by the driver. 7720ba2cbe9Sxc151355 */ 7730ba2cbe9Sxc151355 static void 7740ba2cbe9Sxc151355 ieee80211_node_cleanup(ieee80211_node_t *in) 7750ba2cbe9Sxc151355 { 7760ba2cbe9Sxc151355 in->in_associd = 0; 7770ba2cbe9Sxc151355 in->in_rssi = 0; 7780ba2cbe9Sxc151355 in->in_rstamp = 0; 7790ba2cbe9Sxc151355 if (in->in_challenge != NULL) { 7800ba2cbe9Sxc151355 kmem_free(in->in_challenge, IEEE80211_CHALLENGE_LEN); 7810ba2cbe9Sxc151355 in->in_challenge = NULL; 7820ba2cbe9Sxc151355 } 7830ba2cbe9Sxc151355 if (in->in_rxfrag != NULL) { 7840ba2cbe9Sxc151355 freemsg(in->in_rxfrag); 7850ba2cbe9Sxc151355 in->in_rxfrag = NULL; 7860ba2cbe9Sxc151355 } 7870ba2cbe9Sxc151355 } 7880ba2cbe9Sxc151355 7890ba2cbe9Sxc151355 /* 7900ba2cbe9Sxc151355 * Free a node. This is the default callback function for ic_node_free 7910ba2cbe9Sxc151355 * and may be overridden by the driver to free memory used by device 7920ba2cbe9Sxc151355 * specific node structure 7930ba2cbe9Sxc151355 */ 7940ba2cbe9Sxc151355 static void 7950ba2cbe9Sxc151355 ieee80211_node_free(ieee80211_node_t *in) 7960ba2cbe9Sxc151355 { 7970ba2cbe9Sxc151355 ieee80211com_t *ic = in->in_ic; 7980ba2cbe9Sxc151355 7990ba2cbe9Sxc151355 ic->ic_node_cleanup(in); 800a399b765Szf162725 if (in->in_wpa_ie != NULL) 801a399b765Szf162725 ieee80211_free(in->in_wpa_ie); 802*e2cf88acSQuaker Fang if (in->in_wme_ie != NULL) 803*e2cf88acSQuaker Fang ieee80211_free(in->in_wme_ie); 804*e2cf88acSQuaker Fang if (in->in_htcap_ie != NULL) 805*e2cf88acSQuaker Fang ieee80211_free(in->in_htcap_ie); 8060ba2cbe9Sxc151355 kmem_free(in, sizeof (ieee80211_node_t)); 8070ba2cbe9Sxc151355 } 8080ba2cbe9Sxc151355 8090ba2cbe9Sxc151355 /* 8100ba2cbe9Sxc151355 * Get a node current RSSI value. This is the default callback function 8110ba2cbe9Sxc151355 * for ic_node_getrssi and may be overridden by the driver to provide 8120ba2cbe9Sxc151355 * device specific RSSI calculation algorithm. 8130ba2cbe9Sxc151355 */ 8140ba2cbe9Sxc151355 static uint8_t 8150ba2cbe9Sxc151355 ieee80211_node_getrssi(const ieee80211_node_t *in) 8160ba2cbe9Sxc151355 { 8170ba2cbe9Sxc151355 return (in->in_rssi); 8180ba2cbe9Sxc151355 } 8190ba2cbe9Sxc151355 8200ba2cbe9Sxc151355 /* Free fragment if not needed anymore */ 8210ba2cbe9Sxc151355 static void 8220ba2cbe9Sxc151355 node_cleanfrag(ieee80211_node_t *in) 8230ba2cbe9Sxc151355 { 8240ba2cbe9Sxc151355 clock_t ticks; 8250ba2cbe9Sxc151355 8260ba2cbe9Sxc151355 ticks = ddi_get_lbolt(); 8270ba2cbe9Sxc151355 if (in->in_rxfrag != NULL && ticks > (in->in_rxfragstamp + hz)) { 8280ba2cbe9Sxc151355 freemsg(in->in_rxfrag); 8290ba2cbe9Sxc151355 in->in_rxfrag = NULL; 8300ba2cbe9Sxc151355 } 8310ba2cbe9Sxc151355 } 8320ba2cbe9Sxc151355 8330ba2cbe9Sxc151355 /* 8340ba2cbe9Sxc151355 * Setup a node. Initialize the node with specified macaddr. Associate 8350ba2cbe9Sxc151355 * with the interface softc, ic, and add it to the specified node 8360ba2cbe9Sxc151355 * database. 8370ba2cbe9Sxc151355 */ 8380ba2cbe9Sxc151355 static void 8390ba2cbe9Sxc151355 ieee80211_setup_node(ieee80211com_t *ic, ieee80211_node_table_t *nt, 8400ba2cbe9Sxc151355 ieee80211_node_t *in, const uint8_t *macaddr) 8410ba2cbe9Sxc151355 { 8420ba2cbe9Sxc151355 int32_t hash; 8430ba2cbe9Sxc151355 8440ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_setup_node(): " 8450ba2cbe9Sxc151355 "%p<%s> in %s table\n", in, 8460ba2cbe9Sxc151355 ieee80211_macaddr_sprintf(macaddr), 8470ba2cbe9Sxc151355 (nt != NULL) ? nt->nt_name : "NULL"); 8480ba2cbe9Sxc151355 8490ba2cbe9Sxc151355 in->in_ic = ic; 8500ba2cbe9Sxc151355 IEEE80211_ADDR_COPY(in->in_macaddr, macaddr); 8510ba2cbe9Sxc151355 hash = ieee80211_node_hash(macaddr); 8520ba2cbe9Sxc151355 ieee80211_node_initref(in); /* mark referenced */ 8530ba2cbe9Sxc151355 in->in_authmode = IEEE80211_AUTH_OPEN; 8540ba2cbe9Sxc151355 in->in_txpower = ic->ic_txpowlimit; /* max power */ 8550ba2cbe9Sxc151355 in->in_chan = IEEE80211_CHAN_ANYC; 8560ba2cbe9Sxc151355 in->in_inact_reload = IEEE80211_INACT_INIT; 8570ba2cbe9Sxc151355 in->in_inact = in->in_inact_reload; 8580ba2cbe9Sxc151355 ieee80211_crypto_resetkey(ic, &in->in_ucastkey, IEEE80211_KEYIX_NONE); 8590ba2cbe9Sxc151355 8600ba2cbe9Sxc151355 if (nt != NULL) { 8610ba2cbe9Sxc151355 IEEE80211_NODE_LOCK(nt); 8620ba2cbe9Sxc151355 list_insert_tail(&nt->nt_node, in); 8630ba2cbe9Sxc151355 list_insert_tail(&nt->nt_hash[hash], in); 8640ba2cbe9Sxc151355 in->in_table = nt; 8650ba2cbe9Sxc151355 in->in_inact_reload = nt->nt_inact_init; 8660ba2cbe9Sxc151355 IEEE80211_NODE_UNLOCK(nt); 8670ba2cbe9Sxc151355 } 8680ba2cbe9Sxc151355 } 8690ba2cbe9Sxc151355 8700ba2cbe9Sxc151355 /* 8710ba2cbe9Sxc151355 * Allocates and initialize a node with specified MAC address. 8720ba2cbe9Sxc151355 * Associate the node with the interface ic. If the allocation 8730ba2cbe9Sxc151355 * is successful, the node structure is initialized by 8740ba2cbe9Sxc151355 * ieee80211_setup_node(); otherwise, NULL is returned 8750ba2cbe9Sxc151355 */ 8760ba2cbe9Sxc151355 ieee80211_node_t * 8770ba2cbe9Sxc151355 ieee80211_alloc_node(ieee80211com_t *ic, ieee80211_node_table_t *nt, 8780ba2cbe9Sxc151355 const uint8_t *macaddr) 8790ba2cbe9Sxc151355 { 8800ba2cbe9Sxc151355 ieee80211_node_t *in; 8810ba2cbe9Sxc151355 8820ba2cbe9Sxc151355 in = ic->ic_node_alloc(ic); 8830ba2cbe9Sxc151355 if (in != NULL) 8840ba2cbe9Sxc151355 ieee80211_setup_node(ic, nt, in, macaddr); 8850ba2cbe9Sxc151355 return (in); 8860ba2cbe9Sxc151355 } 8870ba2cbe9Sxc151355 8880ba2cbe9Sxc151355 /* 8890ba2cbe9Sxc151355 * Craft a temporary node suitable for sending a management frame 8900ba2cbe9Sxc151355 * to the specified station. We craft only as much state as we 8910ba2cbe9Sxc151355 * need to do the work since the node will be immediately reclaimed 8920ba2cbe9Sxc151355 * once the send completes. 8930ba2cbe9Sxc151355 */ 8940ba2cbe9Sxc151355 ieee80211_node_t * 8950ba2cbe9Sxc151355 ieee80211_tmp_node(ieee80211com_t *ic, const uint8_t *macaddr) 8960ba2cbe9Sxc151355 { 8970ba2cbe9Sxc151355 ieee80211_node_t *in; 8980ba2cbe9Sxc151355 8990ba2cbe9Sxc151355 in = ic->ic_node_alloc(ic); 9000ba2cbe9Sxc151355 if (in != NULL) { 9010ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_tmp_node: " 9020ba2cbe9Sxc151355 "%p<%s>\n", in, ieee80211_macaddr_sprintf(macaddr)); 9030ba2cbe9Sxc151355 9040ba2cbe9Sxc151355 IEEE80211_ADDR_COPY(in->in_macaddr, macaddr); 9050ba2cbe9Sxc151355 IEEE80211_ADDR_COPY(in->in_bssid, ic->ic_bss->in_bssid); 9060ba2cbe9Sxc151355 ieee80211_node_initref(in); /* mark referenced */ 9070ba2cbe9Sxc151355 in->in_txpower = ic->ic_bss->in_txpower; 9080ba2cbe9Sxc151355 /* NB: required by ieee80211_fix_rate */ 9090ba2cbe9Sxc151355 ieee80211_node_setchan(ic, in, ic->ic_bss->in_chan); 9100ba2cbe9Sxc151355 ieee80211_crypto_resetkey(ic, &in->in_ucastkey, 9110ba2cbe9Sxc151355 IEEE80211_KEYIX_NONE); 9120ba2cbe9Sxc151355 9130ba2cbe9Sxc151355 in->in_table = NULL; /* NB: pedantic */ 9140ba2cbe9Sxc151355 in->in_ic = ic; 9150ba2cbe9Sxc151355 } 9160ba2cbe9Sxc151355 9170ba2cbe9Sxc151355 return (in); 9180ba2cbe9Sxc151355 } 9190ba2cbe9Sxc151355 9200ba2cbe9Sxc151355 /* 9210ba2cbe9Sxc151355 * ieee80211_dup_bss() is similar to ieee80211_alloc_node(), 9220ba2cbe9Sxc151355 * but is instead used to create a node database entry for 9230ba2cbe9Sxc151355 * the specified BSSID. If the allocation is successful, the 9240ba2cbe9Sxc151355 * node is initialized, otherwise, NULL is returned. 9250ba2cbe9Sxc151355 */ 9260ba2cbe9Sxc151355 ieee80211_node_t * 9270ba2cbe9Sxc151355 ieee80211_dup_bss(ieee80211_node_table_t *nt, const uint8_t *macaddr) 9280ba2cbe9Sxc151355 { 9290ba2cbe9Sxc151355 ieee80211com_t *ic = nt->nt_ic; 9300ba2cbe9Sxc151355 ieee80211_node_t *in; 9310ba2cbe9Sxc151355 9320ba2cbe9Sxc151355 in = ieee80211_alloc_node(ic, nt, macaddr); 9330ba2cbe9Sxc151355 if (in != NULL) { 9340ba2cbe9Sxc151355 /* 9350ba2cbe9Sxc151355 * Inherit from ic_bss. 9360ba2cbe9Sxc151355 */ 9370ba2cbe9Sxc151355 ieee80211_copy_bss(in, ic->ic_bss); 9380ba2cbe9Sxc151355 IEEE80211_ADDR_COPY(in->in_bssid, ic->ic_bss->in_bssid); 9390ba2cbe9Sxc151355 ieee80211_node_setchan(ic, in, ic->ic_bss->in_chan); 9400ba2cbe9Sxc151355 } 9410ba2cbe9Sxc151355 9420ba2cbe9Sxc151355 return (in); 9430ba2cbe9Sxc151355 } 9440ba2cbe9Sxc151355 9450ba2cbe9Sxc151355 /* 9460ba2cbe9Sxc151355 * Iterate through the node table, searching for a node entry which 9470ba2cbe9Sxc151355 * matches macaddr. If the entry is found, its reference count is 9480ba2cbe9Sxc151355 * incremented, and a pointer to the node is returned; otherwise, 9490ba2cbe9Sxc151355 * NULL will be returned. 9500ba2cbe9Sxc151355 * The node table lock is acquired by the caller. 9510ba2cbe9Sxc151355 */ 9520ba2cbe9Sxc151355 static ieee80211_node_t * 9530ba2cbe9Sxc151355 ieee80211_find_node_locked(ieee80211_node_table_t *nt, const uint8_t *macaddr) 9540ba2cbe9Sxc151355 { 9550ba2cbe9Sxc151355 ieee80211_node_t *in; 9560ba2cbe9Sxc151355 int hash; 9570ba2cbe9Sxc151355 9580ba2cbe9Sxc151355 ASSERT(IEEE80211_NODE_IS_LOCKED(nt)); 9590ba2cbe9Sxc151355 9600ba2cbe9Sxc151355 hash = ieee80211_node_hash(macaddr); 9610ba2cbe9Sxc151355 in = list_head(&nt->nt_hash[hash]); 9620ba2cbe9Sxc151355 while (in != NULL) { 9630ba2cbe9Sxc151355 if (IEEE80211_ADDR_EQ(in->in_macaddr, macaddr)) 9640ba2cbe9Sxc151355 return (ieee80211_ref_node(in)); /* mark referenced */ 9650ba2cbe9Sxc151355 in = list_next(&nt->nt_hash[hash], in); 9660ba2cbe9Sxc151355 } 9670ba2cbe9Sxc151355 return (NULL); 9680ba2cbe9Sxc151355 } 9690ba2cbe9Sxc151355 9700ba2cbe9Sxc151355 /* 9710ba2cbe9Sxc151355 * Iterate through the node table, searching for a node entry 9720ba2cbe9Sxc151355 * which match specified mac address. 9730ba2cbe9Sxc151355 * Return NULL if no matching node found. 9740ba2cbe9Sxc151355 */ 9750ba2cbe9Sxc151355 ieee80211_node_t * 9760ba2cbe9Sxc151355 ieee80211_find_node(ieee80211_node_table_t *nt, const uint8_t *macaddr) 9770ba2cbe9Sxc151355 { 9780ba2cbe9Sxc151355 ieee80211_node_t *in; 9790ba2cbe9Sxc151355 9800ba2cbe9Sxc151355 IEEE80211_NODE_LOCK(nt); 9810ba2cbe9Sxc151355 in = ieee80211_find_node_locked(nt, macaddr); 9820ba2cbe9Sxc151355 IEEE80211_NODE_UNLOCK(nt); 9830ba2cbe9Sxc151355 return (in); 9840ba2cbe9Sxc151355 } 9850ba2cbe9Sxc151355 9860ba2cbe9Sxc151355 /* 987a399b765Szf162725 * Like find but search based on the ssid too. 988a399b765Szf162725 */ 989a399b765Szf162725 ieee80211_node_t * 990a399b765Szf162725 ieee80211_find_node_with_ssid(ieee80211_node_table_t *nt, 991a399b765Szf162725 const uint8_t *macaddr, uint32_t ssidlen, const uint8_t *ssid) 992a399b765Szf162725 { 993a399b765Szf162725 ieee80211_node_t *in; 994a399b765Szf162725 int hash; 995a399b765Szf162725 996a399b765Szf162725 IEEE80211_NODE_LOCK(nt); 997a399b765Szf162725 998a399b765Szf162725 hash = ieee80211_node_hash(macaddr); 999a399b765Szf162725 in = list_head(&nt->nt_hash[hash]); 1000a399b765Szf162725 while (in != NULL) { 1001a399b765Szf162725 if (IEEE80211_ADDR_EQ(in->in_macaddr, macaddr) && 1002a399b765Szf162725 in->in_esslen == ssidlen && 1003a399b765Szf162725 memcmp(in->in_essid, ssid, ssidlen) == 0) 1004a399b765Szf162725 break; 1005a399b765Szf162725 in = list_next(&nt->nt_hash[hash], in); 1006a399b765Szf162725 } 1007a399b765Szf162725 if (in != NULL) { 1008a399b765Szf162725 (void) ieee80211_ref_node(in); /* mark referenced */ 1009a399b765Szf162725 } 1010a399b765Szf162725 IEEE80211_NODE_UNLOCK(nt); 1011a399b765Szf162725 1012a399b765Szf162725 return (in); 1013a399b765Szf162725 } 1014a399b765Szf162725 1015a399b765Szf162725 /* 10160ba2cbe9Sxc151355 * Fake up a node; this handles node discovery in adhoc mode. 10170ba2cbe9Sxc151355 * Note that for the driver's benefit we treat this like an 10180ba2cbe9Sxc151355 * association so the driver has an opportunity to setup it's 10190ba2cbe9Sxc151355 * private state. 10200ba2cbe9Sxc151355 */ 10210ba2cbe9Sxc151355 ieee80211_node_t * 10220ba2cbe9Sxc151355 ieee80211_fakeup_adhoc_node(ieee80211_node_table_t *nt, const uint8_t *macaddr) 10230ba2cbe9Sxc151355 { 10240ba2cbe9Sxc151355 ieee80211com_t *ic = nt->nt_ic; 10250ba2cbe9Sxc151355 ieee80211_node_t *in; 10260ba2cbe9Sxc151355 10270ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_fakeup_adhoc_node: " 10280ba2cbe9Sxc151355 "mac<%s>\n", ieee80211_macaddr_sprintf(macaddr)); 10290ba2cbe9Sxc151355 in = ieee80211_dup_bss(nt, macaddr); 10300ba2cbe9Sxc151355 if (in != NULL) { 10310ba2cbe9Sxc151355 /* no rate negotiation; just dup */ 10320ba2cbe9Sxc151355 in->in_rates = ic->ic_bss->in_rates; 10330ba2cbe9Sxc151355 if (ic->ic_node_newassoc != NULL) 10340ba2cbe9Sxc151355 ic->ic_node_newassoc(in, 1); 10350ba2cbe9Sxc151355 ieee80211_node_authorize(in); 10360ba2cbe9Sxc151355 } 10370ba2cbe9Sxc151355 return (in); 10380ba2cbe9Sxc151355 } 10390ba2cbe9Sxc151355 1040a399b765Szf162725 static void 1041a399b765Szf162725 ieee80211_saveie(uint8_t **iep, const uint8_t *ie) 1042a399b765Szf162725 { 1043a399b765Szf162725 uint_t ielen = ie[1]+2; 1044a399b765Szf162725 /* 1045a399b765Szf162725 * Record information element for later use. 1046a399b765Szf162725 */ 1047a399b765Szf162725 if (*iep == NULL || (*iep)[1] != ie[1]) { 1048a399b765Szf162725 if (*iep != NULL) 1049a399b765Szf162725 ieee80211_free(*iep); 1050a399b765Szf162725 *iep = ieee80211_malloc(ielen); 1051a399b765Szf162725 } 1052a399b765Szf162725 if (*iep != NULL) 1053a399b765Szf162725 (void) memcpy(*iep, ie, ielen); 1054a399b765Szf162725 } 1055a399b765Szf162725 1056a399b765Szf162725 static void 1057a399b765Szf162725 saveie(uint8_t **iep, const uint8_t *ie) 1058a399b765Szf162725 { 10593a1a8936Szf162725 if (ie == NULL) { 10603a1a8936Szf162725 if (*iep != NULL) 10613a1a8936Szf162725 ieee80211_free(*iep); 1062a399b765Szf162725 *iep = NULL; 10633a1a8936Szf162725 } 1064a399b765Szf162725 else 1065a399b765Szf162725 ieee80211_saveie(iep, ie); 1066a399b765Szf162725 } 1067a399b765Szf162725 10680ba2cbe9Sxc151355 /* 10690ba2cbe9Sxc151355 * Process a beacon or probe response frame. 10700ba2cbe9Sxc151355 */ 10710ba2cbe9Sxc151355 void 10720ba2cbe9Sxc151355 ieee80211_add_scan(ieee80211com_t *ic, const struct ieee80211_scanparams *sp, 10730ba2cbe9Sxc151355 const struct ieee80211_frame *wh, int subtype, int rssi, int rstamp) 10740ba2cbe9Sxc151355 { 10750ba2cbe9Sxc151355 ieee80211_node_table_t *nt = &ic->ic_scan; 10760ba2cbe9Sxc151355 ieee80211_node_t *in; 10770ba2cbe9Sxc151355 boolean_t newnode = B_FALSE; 10780ba2cbe9Sxc151355 1079239e91abShx147065 in = ieee80211_find_node(nt, wh->i_addr3); 10800ba2cbe9Sxc151355 if (in == NULL) { 10810ba2cbe9Sxc151355 /* 10820ba2cbe9Sxc151355 * Create a new entry. 10830ba2cbe9Sxc151355 */ 1084239e91abShx147065 in = ieee80211_alloc_node(ic, nt, wh->i_addr3); 10850ba2cbe9Sxc151355 if (in == NULL) { 10860ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_add_scan: " 10870ba2cbe9Sxc151355 "alloc node failed\n"); 10880ba2cbe9Sxc151355 return; 10890ba2cbe9Sxc151355 } 10900ba2cbe9Sxc151355 /* 10910ba2cbe9Sxc151355 * inherit from ic_bss. 10920ba2cbe9Sxc151355 */ 10930ba2cbe9Sxc151355 ieee80211_copy_bss(in, ic->ic_bss); 10940ba2cbe9Sxc151355 ieee80211_node_setchan(ic, in, ic->ic_curchan); 10950ba2cbe9Sxc151355 newnode = B_TRUE; 10960ba2cbe9Sxc151355 } 10970ba2cbe9Sxc151355 10980ba2cbe9Sxc151355 /* ap beaconing multiple ssid w/ same bssid */ 10990ba2cbe9Sxc151355 11000ba2cbe9Sxc151355 /* 11010ba2cbe9Sxc151355 * sp->ssid[0] - element ID 11020ba2cbe9Sxc151355 * sp->ssid[1] - length 11030ba2cbe9Sxc151355 * sp->ssid[2]... - ssid 11040ba2cbe9Sxc151355 */ 11050ba2cbe9Sxc151355 if (sp->ssid[1] != 0 && 11060ba2cbe9Sxc151355 subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP || 11070ba2cbe9Sxc151355 in->in_esslen == 0) { 11080ba2cbe9Sxc151355 in->in_esslen = sp->ssid[1]; 11090ba2cbe9Sxc151355 bzero(in->in_essid, sizeof (in->in_essid)); 11100ba2cbe9Sxc151355 bcopy(sp->ssid + 2, in->in_essid, sp->ssid[1]); 11110ba2cbe9Sxc151355 } 11120ba2cbe9Sxc151355 IEEE80211_ADDR_COPY(in->in_bssid, wh->i_addr3); 11130ba2cbe9Sxc151355 in->in_rssi = (uint8_t)rssi; 11140ba2cbe9Sxc151355 in->in_rstamp = rstamp; 11150ba2cbe9Sxc151355 bcopy(sp->tstamp, in->in_tstamp.data, sizeof (in->in_tstamp)); 11160ba2cbe9Sxc151355 in->in_intval = sp->bintval; 11170ba2cbe9Sxc151355 in->in_capinfo = sp->capinfo; 11180ba2cbe9Sxc151355 in->in_chan = &ic->ic_sup_channels[sp->chan]; 11190ba2cbe9Sxc151355 in->in_phytype = sp->phytype; 11200ba2cbe9Sxc151355 in->in_fhdwell = sp->fhdwell; 11210ba2cbe9Sxc151355 in->in_fhindex = sp->fhindex; 11220ba2cbe9Sxc151355 in->in_erp = sp->erp; 11230ba2cbe9Sxc151355 if (sp->tim != NULL) { 11240ba2cbe9Sxc151355 struct ieee80211_tim_ie *ie; 11250ba2cbe9Sxc151355 11260ba2cbe9Sxc151355 ie = (struct ieee80211_tim_ie *)sp->tim; 11270ba2cbe9Sxc151355 in->in_dtim_count = ie->tim_count; 11280ba2cbe9Sxc151355 in->in_dtim_period = ie->tim_period; 11290ba2cbe9Sxc151355 } 11300ba2cbe9Sxc151355 /* 11310ba2cbe9Sxc151355 * Record the byte offset from the mac header to 11320ba2cbe9Sxc151355 * the start of the TIM information element for 11330ba2cbe9Sxc151355 * use by hardware and/or to speedup software 11340ba2cbe9Sxc151355 * processing of beacon frames. 11350ba2cbe9Sxc151355 */ 11360ba2cbe9Sxc151355 in->in_tim_off = sp->timoff; 1137a399b765Szf162725 /* 1138a399b765Szf162725 * Record optional information elements that might be 1139a399b765Szf162725 * used by applications or drivers. 1140a399b765Szf162725 */ 1141*e2cf88acSQuaker Fang saveie(&in->in_wme_ie, sp->wme); 1142a399b765Szf162725 saveie(&in->in_wpa_ie, sp->wpa); 1143*e2cf88acSQuaker Fang saveie(&in->in_htcap_ie, sp->htcap); 1144*e2cf88acSQuaker Fang /* parsed in ieee80211_sta_join() */ 1145*e2cf88acSQuaker Fang if (sp->htcap != NULL) 1146*e2cf88acSQuaker Fang ieee80211_parse_htcap(in, in->in_htcap_ie); 11470ba2cbe9Sxc151355 11480ba2cbe9Sxc151355 /* NB: must be after in_chan is setup */ 11490ba2cbe9Sxc151355 (void) ieee80211_setup_rates(in, sp->rates, sp->xrates, 11500ba2cbe9Sxc151355 IEEE80211_F_DOSORT); 11510ba2cbe9Sxc151355 11520ba2cbe9Sxc151355 if (!newnode) 11530ba2cbe9Sxc151355 ieee80211_free_node(in); 11540ba2cbe9Sxc151355 } 11550ba2cbe9Sxc151355 11560ba2cbe9Sxc151355 /* 11570ba2cbe9Sxc151355 * Initialize/update an ad-hoc node with contents from a received 11580ba2cbe9Sxc151355 * beacon frame. 11590ba2cbe9Sxc151355 */ 11600ba2cbe9Sxc151355 void 11610ba2cbe9Sxc151355 ieee80211_init_neighbor(ieee80211_node_t *in, const struct ieee80211_frame *wh, 11620ba2cbe9Sxc151355 const struct ieee80211_scanparams *sp) 11630ba2cbe9Sxc151355 { 11640ba2cbe9Sxc151355 in->in_esslen = sp->ssid[1]; 11650ba2cbe9Sxc151355 (void) memcpy(in->in_essid, sp->ssid + 2, sp->ssid[1]); 11660ba2cbe9Sxc151355 IEEE80211_ADDR_COPY(in->in_bssid, wh->i_addr3); 11670ba2cbe9Sxc151355 (void) memcpy(in->in_tstamp.data, sp->tstamp, sizeof (in->in_tstamp)); 11680ba2cbe9Sxc151355 in->in_intval = sp->bintval; 11690ba2cbe9Sxc151355 in->in_capinfo = sp->capinfo; 11700ba2cbe9Sxc151355 in->in_chan = in->in_ic->ic_curchan; 11710ba2cbe9Sxc151355 in->in_fhdwell = sp->fhdwell; 11720ba2cbe9Sxc151355 in->in_fhindex = sp->fhindex; 11730ba2cbe9Sxc151355 in->in_erp = sp->erp; 11740ba2cbe9Sxc151355 in->in_tim_off = sp->timoff; 1175*e2cf88acSQuaker Fang if (sp->wme != NULL) 1176*e2cf88acSQuaker Fang ieee80211_saveie(&in->in_wme_ie, sp->wme); 11770ba2cbe9Sxc151355 11780ba2cbe9Sxc151355 /* NB: must be after in_chan is setup */ 11790ba2cbe9Sxc151355 (void) ieee80211_setup_rates(in, sp->rates, sp->xrates, 11800ba2cbe9Sxc151355 IEEE80211_F_DOSORT); 11810ba2cbe9Sxc151355 } 11820ba2cbe9Sxc151355 11830ba2cbe9Sxc151355 /* 11840ba2cbe9Sxc151355 * Do node discovery in adhoc mode on receipt of a beacon 11850ba2cbe9Sxc151355 * or probe response frame. Note that for the driver's 11860ba2cbe9Sxc151355 * benefit we we treat this like an association so the 11870ba2cbe9Sxc151355 * driver has an opportuinty to setup it's private state. 11880ba2cbe9Sxc151355 */ 11890ba2cbe9Sxc151355 ieee80211_node_t * 11900ba2cbe9Sxc151355 ieee80211_add_neighbor(ieee80211com_t *ic, const struct ieee80211_frame *wh, 11910ba2cbe9Sxc151355 const struct ieee80211_scanparams *sp) 11920ba2cbe9Sxc151355 { 11930ba2cbe9Sxc151355 ieee80211_node_t *in; 11940ba2cbe9Sxc151355 11950ba2cbe9Sxc151355 in = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2); 11960ba2cbe9Sxc151355 if (in != NULL) { 11970ba2cbe9Sxc151355 ieee80211_init_neighbor(in, wh, sp); 11980ba2cbe9Sxc151355 if (ic->ic_node_newassoc != NULL) 11990ba2cbe9Sxc151355 ic->ic_node_newassoc(in, 1); 12000ba2cbe9Sxc151355 } 12010ba2cbe9Sxc151355 return (in); 12020ba2cbe9Sxc151355 } 12030ba2cbe9Sxc151355 12040ba2cbe9Sxc151355 #define IEEE80211_IS_CTL(wh) \ 12050ba2cbe9Sxc151355 ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) 12060ba2cbe9Sxc151355 1207*e2cf88acSQuaker Fang #define IEEE80211_IS_PSPOLL(wh) \ 1208*e2cf88acSQuaker Fang ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == \ 1209*e2cf88acSQuaker Fang IEEE80211_FC0_SUBTYPE_PS_POLL) 1210*e2cf88acSQuaker Fang 1211*e2cf88acSQuaker Fang #define IEEE80211_IS_BAR(wh) \ 1212*e2cf88acSQuaker Fang ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == \ 1213*e2cf88acSQuaker Fang IEEE80211_FC0_SUBTYPE_BAR) 1214*e2cf88acSQuaker Fang 12150ba2cbe9Sxc151355 /* 12160ba2cbe9Sxc151355 * Locate the node for sender, track state, and then pass the 12170ba2cbe9Sxc151355 * (referenced) node up to the 802.11 layer for its use. We 12180ba2cbe9Sxc151355 * are required to pass some node so we fall back to ic_bss 12190ba2cbe9Sxc151355 * when this frame is from an unknown sender. The 802.11 layer 12200ba2cbe9Sxc151355 * knows this means the sender wasn't in the node table and 12210ba2cbe9Sxc151355 * acts accordingly. 12220ba2cbe9Sxc151355 */ 12230ba2cbe9Sxc151355 ieee80211_node_t * 12240ba2cbe9Sxc151355 ieee80211_find_rxnode(ieee80211com_t *ic, const struct ieee80211_frame *wh) 12250ba2cbe9Sxc151355 { 12260ba2cbe9Sxc151355 ieee80211_node_table_t *nt; 12270ba2cbe9Sxc151355 ieee80211_node_t *in; 12280ba2cbe9Sxc151355 12290ba2cbe9Sxc151355 /* may want scanned nodes in the neighbor table for adhoc */ 12300ba2cbe9Sxc151355 if (ic->ic_opmode == IEEE80211_M_STA || 12310ba2cbe9Sxc151355 (ic->ic_flags & IEEE80211_F_SCAN)) { 12320ba2cbe9Sxc151355 nt = &ic->ic_scan; 12330ba2cbe9Sxc151355 } else { 12340ba2cbe9Sxc151355 nt = &ic->ic_sta; 12350ba2cbe9Sxc151355 } 12360ba2cbe9Sxc151355 12370ba2cbe9Sxc151355 IEEE80211_NODE_LOCK(nt); 1238*e2cf88acSQuaker Fang if (IEEE80211_IS_CTL(wh) && 1239*e2cf88acSQuaker Fang !IEEE80211_IS_PSPOLL(wh) && !IEEE80211_IS_BAR(wh)) 12400ba2cbe9Sxc151355 in = ieee80211_find_node_locked(nt, wh->i_addr1); 12410ba2cbe9Sxc151355 else 12420ba2cbe9Sxc151355 in = ieee80211_find_node_locked(nt, wh->i_addr2); 12430ba2cbe9Sxc151355 IEEE80211_NODE_UNLOCK(nt); 12440ba2cbe9Sxc151355 12450ba2cbe9Sxc151355 if (in == NULL) 12460ba2cbe9Sxc151355 in = ieee80211_ref_node(ic->ic_bss); 12470ba2cbe9Sxc151355 12480ba2cbe9Sxc151355 return (in); 12490ba2cbe9Sxc151355 } 12500ba2cbe9Sxc151355 1251*e2cf88acSQuaker Fang #undef IEEE80211_IS_BAR 1252*e2cf88acSQuaker Fang #undef IEEE80211_IS_PSPOLL 1253*e2cf88acSQuaker Fang #undef IEEE80211_IS_CTL 1254*e2cf88acSQuaker Fang 12550ba2cbe9Sxc151355 /* 12560ba2cbe9Sxc151355 * Return a reference to the appropriate node for sending 12570ba2cbe9Sxc151355 * a data frame. This handles node discovery in adhoc networks. 12580ba2cbe9Sxc151355 */ 12590ba2cbe9Sxc151355 ieee80211_node_t * 12600ba2cbe9Sxc151355 ieee80211_find_txnode(ieee80211com_t *ic, const uint8_t *daddr) 12610ba2cbe9Sxc151355 { 12620ba2cbe9Sxc151355 ieee80211_node_table_t *nt = &ic->ic_sta; 12630ba2cbe9Sxc151355 ieee80211_node_t *in; 12640ba2cbe9Sxc151355 12650ba2cbe9Sxc151355 /* 12660ba2cbe9Sxc151355 * The destination address should be in the node table 12670ba2cbe9Sxc151355 * unless this is a multicast/broadcast frame. We can 12680ba2cbe9Sxc151355 * also optimize station mode operation, all frames go 12690ba2cbe9Sxc151355 * to the bss node. 12700ba2cbe9Sxc151355 */ 12710ba2cbe9Sxc151355 IEEE80211_NODE_LOCK(nt); 12720ba2cbe9Sxc151355 if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(daddr)) 12730ba2cbe9Sxc151355 in = ieee80211_ref_node(ic->ic_bss); 12740ba2cbe9Sxc151355 else 12750ba2cbe9Sxc151355 in = ieee80211_find_node_locked(nt, daddr); 12760ba2cbe9Sxc151355 IEEE80211_NODE_UNLOCK(nt); 12770ba2cbe9Sxc151355 12780ba2cbe9Sxc151355 if (in == NULL) { 12790ba2cbe9Sxc151355 if (ic->ic_opmode == IEEE80211_M_IBSS) { 12800ba2cbe9Sxc151355 /* 12810ba2cbe9Sxc151355 * In adhoc mode cons up a node for the destination. 12820ba2cbe9Sxc151355 * Note that we need an additional reference for the 12830ba2cbe9Sxc151355 * caller to be consistent with 12840ba2cbe9Sxc151355 * ieee80211_find_node_locked 12850ba2cbe9Sxc151355 * can't hold lock across ieee80211_dup_bss 'cuz of 12860ba2cbe9Sxc151355 * recursive locking 12870ba2cbe9Sxc151355 */ 12880ba2cbe9Sxc151355 in = ieee80211_fakeup_adhoc_node(nt, daddr); 12890ba2cbe9Sxc151355 if (in != NULL) 12900ba2cbe9Sxc151355 (void) ieee80211_ref_node(in); 12910ba2cbe9Sxc151355 } else { 12920ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_OUTPUT, 12930ba2cbe9Sxc151355 "ieee80211_find_txnode: " 12940ba2cbe9Sxc151355 "[%s] no node, discard frame\n", 12950ba2cbe9Sxc151355 ieee80211_macaddr_sprintf(daddr)); 12960ba2cbe9Sxc151355 } 12970ba2cbe9Sxc151355 } 12980ba2cbe9Sxc151355 return (in); 12990ba2cbe9Sxc151355 } 13000ba2cbe9Sxc151355 13010ba2cbe9Sxc151355 /* 13020ba2cbe9Sxc151355 * Remove a node from the node database entries and free memory 13030ba2cbe9Sxc151355 * associated with the node. The node table lock is acquired by 13040ba2cbe9Sxc151355 * the caller. 13050ba2cbe9Sxc151355 */ 13060ba2cbe9Sxc151355 static void 13070ba2cbe9Sxc151355 ieee80211_free_node_locked(ieee80211_node_t *in) 13080ba2cbe9Sxc151355 { 13090ba2cbe9Sxc151355 ieee80211com_t *ic = in->in_ic; 13100ba2cbe9Sxc151355 ieee80211_node_table_t *nt = in->in_table; 13110ba2cbe9Sxc151355 int32_t hash; 13120ba2cbe9Sxc151355 13130ba2cbe9Sxc151355 if (nt != NULL) { 13140ba2cbe9Sxc151355 hash = ieee80211_node_hash(in->in_macaddr); 13150ba2cbe9Sxc151355 list_remove(&nt->nt_hash[hash], in); 13160ba2cbe9Sxc151355 list_remove(&nt->nt_node, in); 13170ba2cbe9Sxc151355 } 13180ba2cbe9Sxc151355 ic->ic_node_free(in); 13190ba2cbe9Sxc151355 } 13200ba2cbe9Sxc151355 13210ba2cbe9Sxc151355 /* 13220ba2cbe9Sxc151355 * Remove a node from the node database entries and free any 13230ba2cbe9Sxc151355 * memory associated with the node. 13240ba2cbe9Sxc151355 * This method can be overridden in ieee80211_attach() 13250ba2cbe9Sxc151355 */ 13260ba2cbe9Sxc151355 void 13270ba2cbe9Sxc151355 ieee80211_free_node(ieee80211_node_t *in) 13280ba2cbe9Sxc151355 { 13290ba2cbe9Sxc151355 ieee80211_node_table_t *nt = in->in_table; 13300ba2cbe9Sxc151355 13310ba2cbe9Sxc151355 if (nt != NULL) 13320ba2cbe9Sxc151355 IEEE80211_NODE_LOCK(nt); 13330ba2cbe9Sxc151355 if (ieee80211_node_decref_nv(in) == 0) 13340ba2cbe9Sxc151355 ieee80211_free_node_locked(in); 13350ba2cbe9Sxc151355 if (nt != NULL) 13360ba2cbe9Sxc151355 IEEE80211_NODE_UNLOCK(nt); 13370ba2cbe9Sxc151355 } 13380ba2cbe9Sxc151355 13390ba2cbe9Sxc151355 /* 13400ba2cbe9Sxc151355 * Reclaim a node. If this is the last reference count then 13410ba2cbe9Sxc151355 * do the normal free work. Otherwise remove it from the node 13420ba2cbe9Sxc151355 * table and mark it gone by clearing the back-reference. 13430ba2cbe9Sxc151355 */ 13440ba2cbe9Sxc151355 static void 13450ba2cbe9Sxc151355 ieee80211_node_reclaim(ieee80211_node_table_t *nt, ieee80211_node_t *in) 13460ba2cbe9Sxc151355 { 13470ba2cbe9Sxc151355 int32_t hash; 13480ba2cbe9Sxc151355 13490ba2cbe9Sxc151355 IEEE80211_NODE_LOCK_ASSERT(nt); 13500ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "node_reclaim: " 13510ba2cbe9Sxc151355 " remove %p<%s> from %s table, refcnt %d\n", 13520ba2cbe9Sxc151355 in, ieee80211_macaddr_sprintf(in->in_macaddr), nt->nt_name, 13530ba2cbe9Sxc151355 ieee80211_node_refcnt(in)); 13540ba2cbe9Sxc151355 13550ba2cbe9Sxc151355 if (ieee80211_node_decref_nv(in) != 0) { 13560ba2cbe9Sxc151355 /* 13570ba2cbe9Sxc151355 * Clear any entry in the unicast key mapping table. 13580ba2cbe9Sxc151355 * We need to do it here so rx lookups don't find it 13590ba2cbe9Sxc151355 * in the mapping table even if it's not in the hash 13600ba2cbe9Sxc151355 * table. We cannot depend on the mapping table entry 13610ba2cbe9Sxc151355 * being cleared because the node may not be free'd. 13620ba2cbe9Sxc151355 */ 13630ba2cbe9Sxc151355 hash = ieee80211_node_hash(in->in_macaddr); 13640ba2cbe9Sxc151355 list_remove(&nt->nt_hash[hash], in); 13650ba2cbe9Sxc151355 list_remove(&nt->nt_node, in); 13660ba2cbe9Sxc151355 in->in_table = NULL; 13670ba2cbe9Sxc151355 } else { 13680ba2cbe9Sxc151355 ieee80211_free_node_locked(in); 13690ba2cbe9Sxc151355 } 13700ba2cbe9Sxc151355 } 13710ba2cbe9Sxc151355 13720ba2cbe9Sxc151355 /* 13730ba2cbe9Sxc151355 * Iterate through the node list and reclaim all node in the node table. 13740ba2cbe9Sxc151355 * The node table lock is acquired by the caller 13750ba2cbe9Sxc151355 */ 13760ba2cbe9Sxc151355 static void 13770ba2cbe9Sxc151355 ieee80211_free_allnodes_locked(ieee80211_node_table_t *nt) 13780ba2cbe9Sxc151355 { 13790ba2cbe9Sxc151355 ieee80211_node_t *in; 13800ba2cbe9Sxc151355 13810ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_free_allnodes_locked(): " 13820ba2cbe9Sxc151355 "free all nodes in %s table\n", nt->nt_name); 13830ba2cbe9Sxc151355 13840ba2cbe9Sxc151355 in = list_head(&nt->nt_node); 13850ba2cbe9Sxc151355 while (in != NULL) { 13860ba2cbe9Sxc151355 ieee80211_node_reclaim(nt, in); 13870ba2cbe9Sxc151355 in = list_head(&nt->nt_node); 13880ba2cbe9Sxc151355 } 13890ba2cbe9Sxc151355 ieee80211_reset_erp(nt->nt_ic); 13900ba2cbe9Sxc151355 } 13910ba2cbe9Sxc151355 13920ba2cbe9Sxc151355 /* 13930ba2cbe9Sxc151355 * Iterate through the node list, calling ieee80211_node_reclaim() for 13940ba2cbe9Sxc151355 * all nodes associated with the interface. 13950ba2cbe9Sxc151355 */ 13960ba2cbe9Sxc151355 static void 13970ba2cbe9Sxc151355 ieee80211_free_allnodes(ieee80211_node_table_t *nt) 13980ba2cbe9Sxc151355 { 13990ba2cbe9Sxc151355 IEEE80211_NODE_LOCK(nt); 14000ba2cbe9Sxc151355 ieee80211_free_allnodes_locked(nt); 14010ba2cbe9Sxc151355 IEEE80211_NODE_UNLOCK(nt); 14020ba2cbe9Sxc151355 } 14030ba2cbe9Sxc151355 14040ba2cbe9Sxc151355 /* 14050ba2cbe9Sxc151355 * Timeout entries in the scan cache. This is the timeout callback 14060ba2cbe9Sxc151355 * function of node table ic_scan which is called when the inactivity 14070ba2cbe9Sxc151355 * timer expires. 14080ba2cbe9Sxc151355 */ 14090ba2cbe9Sxc151355 static void 14100ba2cbe9Sxc151355 ieee80211_timeout_scan_candidates(ieee80211_node_table_t *nt) 14110ba2cbe9Sxc151355 { 14120ba2cbe9Sxc151355 ieee80211com_t *ic = nt->nt_ic; 14130ba2cbe9Sxc151355 ieee80211_node_t *in; 14140ba2cbe9Sxc151355 14150ba2cbe9Sxc151355 IEEE80211_NODE_LOCK(nt); 14160ba2cbe9Sxc151355 in = ic->ic_bss; 14170ba2cbe9Sxc151355 node_cleanfrag(in); /* Free fragment if not needed */ 14180ba2cbe9Sxc151355 nt->nt_inact_timer = IEEE80211_INACT_WAIT; 14190ba2cbe9Sxc151355 IEEE80211_NODE_UNLOCK(nt); 14200ba2cbe9Sxc151355 } 14210ba2cbe9Sxc151355 14220ba2cbe9Sxc151355 /* 14230ba2cbe9Sxc151355 * Timeout inactive stations and do related housekeeping. 14240ba2cbe9Sxc151355 * Note that we cannot hold the node lock while sending a 14250ba2cbe9Sxc151355 * frame as this would lead to a LOR. Instead we use a 14260ba2cbe9Sxc151355 * generation number to mark nodes that we've scanned and 14270ba2cbe9Sxc151355 * drop the lock and restart a scan if we have to time out 14280ba2cbe9Sxc151355 * a node. Since we are single-threaded by virtue of 14290ba2cbe9Sxc151355 * controlling the inactivity timer we can be sure this will 14300ba2cbe9Sxc151355 * process each node only once. 14310ba2cbe9Sxc151355 */ 14320ba2cbe9Sxc151355 static void 14330ba2cbe9Sxc151355 ieee80211_timeout_stations(ieee80211_node_table_t *nt) 14340ba2cbe9Sxc151355 { 14350ba2cbe9Sxc151355 ieee80211com_t *ic = nt->nt_ic; 14360ba2cbe9Sxc151355 ieee80211_impl_t *im = ic->ic_private; 14370ba2cbe9Sxc151355 ieee80211_node_t *in = NULL; 14380ba2cbe9Sxc151355 uint32_t gen; 14390ba2cbe9Sxc151355 boolean_t isadhoc; 14400ba2cbe9Sxc151355 14410ba2cbe9Sxc151355 IEEE80211_LOCK_ASSERT(ic); 14420ba2cbe9Sxc151355 isadhoc = (ic->ic_opmode == IEEE80211_M_IBSS || 14430ba2cbe9Sxc151355 ic->ic_opmode == IEEE80211_M_AHDEMO); 14440ba2cbe9Sxc151355 IEEE80211_SCAN_LOCK(nt); 14450ba2cbe9Sxc151355 gen = ++nt->nt_scangen; 14460ba2cbe9Sxc151355 restart: 14470ba2cbe9Sxc151355 IEEE80211_NODE_LOCK(nt); 14480ba2cbe9Sxc151355 for (in = list_head(&nt->nt_node); in != NULL; 14490ba2cbe9Sxc151355 in = list_next(&nt->nt_node, in)) { 14500ba2cbe9Sxc151355 if (in->in_scangen == gen) /* previously handled */ 14510ba2cbe9Sxc151355 continue; 14520ba2cbe9Sxc151355 in->in_scangen = gen; 14530ba2cbe9Sxc151355 node_cleanfrag(in); /* free fragment if not needed */ 14540ba2cbe9Sxc151355 14550ba2cbe9Sxc151355 /* 14560ba2cbe9Sxc151355 * Special case ourself; we may be idle for extended periods 14570ba2cbe9Sxc151355 * of time and regardless reclaiming our state is wrong. 14580ba2cbe9Sxc151355 */ 14590ba2cbe9Sxc151355 if (in == ic->ic_bss) 14600ba2cbe9Sxc151355 continue; 14610ba2cbe9Sxc151355 in->in_inact--; 14620ba2cbe9Sxc151355 if (in->in_associd != 0 || isadhoc) { 14630ba2cbe9Sxc151355 /* 14640ba2cbe9Sxc151355 * Probe the station before time it out. We 14650ba2cbe9Sxc151355 * send a null data frame which may not be 14660ba2cbe9Sxc151355 * uinversally supported by drivers (need it 14670ba2cbe9Sxc151355 * for ps-poll support so it should be...). 14680ba2cbe9Sxc151355 */ 14690ba2cbe9Sxc151355 if (0 < in->in_inact && 14700ba2cbe9Sxc151355 in->in_inact <= im->im_inact_probe) { 14710ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "net80211: " 14720ba2cbe9Sxc151355 "probe station due to inactivity\n"); 14730ba2cbe9Sxc151355 IEEE80211_NODE_UNLOCK(nt); 14740ba2cbe9Sxc151355 IEEE80211_UNLOCK(ic); 14750ba2cbe9Sxc151355 (void) ieee80211_send_nulldata(in); 14760ba2cbe9Sxc151355 IEEE80211_LOCK(ic); 14770ba2cbe9Sxc151355 goto restart; 14780ba2cbe9Sxc151355 } 14790ba2cbe9Sxc151355 } 14800ba2cbe9Sxc151355 if (in->in_inact <= 0) { 14810ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "net80211: " 14820ba2cbe9Sxc151355 "station timed out due to inact (refcnt %u)\n", 14830ba2cbe9Sxc151355 ieee80211_node_refcnt(in)); 14840ba2cbe9Sxc151355 /* 14850ba2cbe9Sxc151355 * Send a deauthenticate frame and drop the station. 14860ba2cbe9Sxc151355 * This is somewhat complicated due to reference counts 14870ba2cbe9Sxc151355 * and locking. At this point a station will typically 14880ba2cbe9Sxc151355 * have a reference count of 1. ieee80211_node_leave 14890ba2cbe9Sxc151355 * will do a "free" of the node which will drop the 14900ba2cbe9Sxc151355 * reference count. But in the meantime a reference 14910ba2cbe9Sxc151355 * wil be held by the deauth frame. The actual reclaim 14920ba2cbe9Sxc151355 * of the node will happen either after the tx is 14930ba2cbe9Sxc151355 * completed or by ieee80211_node_leave. 14940ba2cbe9Sxc151355 * 14950ba2cbe9Sxc151355 * Separately we must drop the node lock before sending 14960ba2cbe9Sxc151355 * in case the driver takes a lock, as this will result 14970ba2cbe9Sxc151355 * in LOR between the node lock and the driver lock. 14980ba2cbe9Sxc151355 */ 14990ba2cbe9Sxc151355 IEEE80211_NODE_UNLOCK(nt); 15000ba2cbe9Sxc151355 if (in->in_associd != 0) { 15010ba2cbe9Sxc151355 IEEE80211_UNLOCK(ic); 15020ba2cbe9Sxc151355 IEEE80211_SEND_MGMT(ic, in, 15030ba2cbe9Sxc151355 IEEE80211_FC0_SUBTYPE_DEAUTH, 15040ba2cbe9Sxc151355 IEEE80211_REASON_AUTH_EXPIRE); 15050ba2cbe9Sxc151355 IEEE80211_LOCK(ic); 15060ba2cbe9Sxc151355 } 15070ba2cbe9Sxc151355 ieee80211_node_leave(ic, in); 15080ba2cbe9Sxc151355 goto restart; 15090ba2cbe9Sxc151355 } 15100ba2cbe9Sxc151355 } 15110ba2cbe9Sxc151355 IEEE80211_NODE_UNLOCK(nt); 15120ba2cbe9Sxc151355 15130ba2cbe9Sxc151355 IEEE80211_SCAN_UNLOCK(nt); 15140ba2cbe9Sxc151355 15150ba2cbe9Sxc151355 nt->nt_inact_timer = IEEE80211_INACT_WAIT; 15160ba2cbe9Sxc151355 } 15170ba2cbe9Sxc151355 15180ba2cbe9Sxc151355 /* 15190ba2cbe9Sxc151355 * Call the user-defined call back function for all nodes in 15200ba2cbe9Sxc151355 * the node cache. The callback is invoked with the user-supplied 15210ba2cbe9Sxc151355 * value and a pointer to the current node. 15220ba2cbe9Sxc151355 */ 15230ba2cbe9Sxc151355 void 15240ba2cbe9Sxc151355 ieee80211_iterate_nodes(ieee80211_node_table_t *nt, ieee80211_iter_func *f, 15250ba2cbe9Sxc151355 void *arg) 15260ba2cbe9Sxc151355 { 15270ba2cbe9Sxc151355 ieee80211_node_t *in; 15280ba2cbe9Sxc151355 15290ba2cbe9Sxc151355 IEEE80211_NODE_LOCK(nt); 15300ba2cbe9Sxc151355 in = list_head(&nt->nt_node); 15310ba2cbe9Sxc151355 while (in != NULL) { 1532239e91abShx147065 if (in->in_chan == IEEE80211_CHAN_ANYC) { 1533239e91abShx147065 in = list_next(&nt->nt_node, in); 1534239e91abShx147065 continue; 1535239e91abShx147065 } 15360ba2cbe9Sxc151355 (void) ieee80211_ref_node(in); 15370ba2cbe9Sxc151355 IEEE80211_NODE_UNLOCK(nt); 15380ba2cbe9Sxc151355 (*f)(arg, in); 15390ba2cbe9Sxc151355 ieee80211_free_node(in); 15400ba2cbe9Sxc151355 IEEE80211_NODE_LOCK(nt); 15410ba2cbe9Sxc151355 in = list_next(&nt->nt_node, in); 15420ba2cbe9Sxc151355 } 15430ba2cbe9Sxc151355 IEEE80211_NODE_UNLOCK(nt); 15440ba2cbe9Sxc151355 } 15450ba2cbe9Sxc151355 15460ba2cbe9Sxc151355 /* 15470ba2cbe9Sxc151355 * Handle bookkeeping for station deauthentication/disassociation 15480ba2cbe9Sxc151355 * when operating as an ap. 15490ba2cbe9Sxc151355 */ 15500ba2cbe9Sxc151355 static void 15510ba2cbe9Sxc151355 ieee80211_node_leave(ieee80211com_t *ic, ieee80211_node_t *in) 15520ba2cbe9Sxc151355 { 15530ba2cbe9Sxc151355 ieee80211_node_table_t *nt = in->in_table; 15540ba2cbe9Sxc151355 15550ba2cbe9Sxc151355 ASSERT(ic->ic_opmode == IEEE80211_M_IBSS); 15560ba2cbe9Sxc151355 15570ba2cbe9Sxc151355 /* 15580ba2cbe9Sxc151355 * Remove the node from any table it's recorded in and 15590ba2cbe9Sxc151355 * drop the caller's reference. Removal from the table 15600ba2cbe9Sxc151355 * is important to insure the node is not reprocessed 15610ba2cbe9Sxc151355 * for inactivity. 15620ba2cbe9Sxc151355 */ 15630ba2cbe9Sxc151355 if (nt != NULL) { 15640ba2cbe9Sxc151355 IEEE80211_NODE_LOCK(nt); 15650ba2cbe9Sxc151355 ieee80211_node_reclaim(nt, in); 15660ba2cbe9Sxc151355 IEEE80211_NODE_UNLOCK(nt); 15670ba2cbe9Sxc151355 } else { 15680ba2cbe9Sxc151355 ieee80211_free_node(in); 15690ba2cbe9Sxc151355 } 15700ba2cbe9Sxc151355 } 15710ba2cbe9Sxc151355 15720ba2cbe9Sxc151355 /* 15730ba2cbe9Sxc151355 * Initialize a node table with specified name, inactivity timer value 15740ba2cbe9Sxc151355 * and callback inactivity timeout function. Associate the node table 15750ba2cbe9Sxc151355 * with interface softc, ic. 15760ba2cbe9Sxc151355 */ 15770ba2cbe9Sxc151355 static void 15780ba2cbe9Sxc151355 ieee80211_node_table_init(ieee80211com_t *ic, ieee80211_node_table_t *nt, 15790ba2cbe9Sxc151355 const char *name, int inact, int keyixmax, 15800ba2cbe9Sxc151355 void (*timeout)(ieee80211_node_table_t *)) 15810ba2cbe9Sxc151355 { 15820ba2cbe9Sxc151355 int i; 15830ba2cbe9Sxc151355 15840ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_node_table_init():" 15850ba2cbe9Sxc151355 "%s table, inact %d\n", name, inact); 15860ba2cbe9Sxc151355 15870ba2cbe9Sxc151355 nt->nt_ic = ic; 15880ba2cbe9Sxc151355 nt->nt_name = name; 15890ba2cbe9Sxc151355 nt->nt_inact_timer = 0; 15900ba2cbe9Sxc151355 nt->nt_inact_init = inact; 15910ba2cbe9Sxc151355 nt->nt_timeout = timeout; 15920ba2cbe9Sxc151355 nt->nt_keyixmax = keyixmax; 15930ba2cbe9Sxc151355 nt->nt_scangen = 1; 15940ba2cbe9Sxc151355 mutex_init(&nt->nt_scanlock, NULL, MUTEX_DRIVER, NULL); 15950ba2cbe9Sxc151355 mutex_init(&nt->nt_nodelock, NULL, MUTEX_DRIVER, NULL); 15960ba2cbe9Sxc151355 15970ba2cbe9Sxc151355 list_create(&nt->nt_node, sizeof (ieee80211_node_t), 15980ba2cbe9Sxc151355 offsetof(ieee80211_node_t, in_node)); 15990ba2cbe9Sxc151355 for (i = 0; i < IEEE80211_NODE_HASHSIZE; i++) { 16000ba2cbe9Sxc151355 list_create(&nt->nt_hash[i], sizeof (ieee80211_node_t), 16010ba2cbe9Sxc151355 offsetof(ieee80211_node_t, in_hash)); 16020ba2cbe9Sxc151355 } 16030ba2cbe9Sxc151355 } 16040ba2cbe9Sxc151355 16050ba2cbe9Sxc151355 /* 16060ba2cbe9Sxc151355 * Reset a node table. Clean its inactivity timer and call 16070ba2cbe9Sxc151355 * ieee80211_free_allnodes_locked() to free all nodes in the 16080ba2cbe9Sxc151355 * node table. 16090ba2cbe9Sxc151355 */ 16100ba2cbe9Sxc151355 void 16110ba2cbe9Sxc151355 ieee80211_node_table_reset(ieee80211_node_table_t *nt) 16120ba2cbe9Sxc151355 { 16130ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_node_table_reset(): " 16140ba2cbe9Sxc151355 "%s table\n", nt->nt_name); 16150ba2cbe9Sxc151355 16160ba2cbe9Sxc151355 IEEE80211_NODE_LOCK(nt); 16170ba2cbe9Sxc151355 nt->nt_inact_timer = 0; 16180ba2cbe9Sxc151355 ieee80211_free_allnodes_locked(nt); 16190ba2cbe9Sxc151355 IEEE80211_NODE_UNLOCK(nt); 16200ba2cbe9Sxc151355 } 16210ba2cbe9Sxc151355 16220ba2cbe9Sxc151355 /* 16230ba2cbe9Sxc151355 * Destroy a node table. Free all nodes in the node table. 16240ba2cbe9Sxc151355 * This function is usually called by node detach function. 16250ba2cbe9Sxc151355 */ 16260ba2cbe9Sxc151355 static void 16270ba2cbe9Sxc151355 ieee80211_node_table_cleanup(ieee80211_node_table_t *nt) 16280ba2cbe9Sxc151355 { 16290ba2cbe9Sxc151355 ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_node_table_cleanup(): " 16300ba2cbe9Sxc151355 "%s table\n", nt->nt_name); 16310ba2cbe9Sxc151355 16320ba2cbe9Sxc151355 IEEE80211_NODE_LOCK(nt); 16330ba2cbe9Sxc151355 ieee80211_free_allnodes_locked(nt); 16340ba2cbe9Sxc151355 IEEE80211_NODE_UNLOCK(nt); 16350ba2cbe9Sxc151355 mutex_destroy(&nt->nt_nodelock); 16360ba2cbe9Sxc151355 mutex_destroy(&nt->nt_scanlock); 16370ba2cbe9Sxc151355 } 1638