xref: /illumos-gate/usr/src/uts/common/io/net80211/net80211_node.c (revision e2cf88ac9d753a00c17aa235f6afdc76574fe3a6)
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
ieee80211_node_attach(ieee80211com_t * ic)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
ieee80211_node_lateattach(ieee80211com_t * ic)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
ieee80211_node_detach(ieee80211com_t * ic)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 *
ieee80211_ref_node(ieee80211_node_t * in)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
ieee80211_unref_node(ieee80211_node_t ** in)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
ieee80211_node_authorize(ieee80211_node_t * in)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
ieee80211_node_unauthorize(ieee80211_node_t * in)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
ieee80211_node_setchan(ieee80211com_t * ic,ieee80211_node_t * in,struct ieee80211_channel * chan)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
ieee80211_reset_scan(ieee80211com_t * ic)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
ieee80211_begin_scan(ieee80211com_t * ic,boolean_t reset)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
ieee80211_next_scan(ieee80211com_t * ic)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
ieee80211_copy_bss(ieee80211_node_t * nbss,const ieee80211_node_t * obss)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
ieee80211_create_ibss(ieee80211com_t * ic,struct ieee80211_channel * chan)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
ieee80211_reset_bss(ieee80211com_t * ic)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
ieee80211_match_bss(ieee80211com_t * ic,ieee80211_node_t * in)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 *
ieee80211_node_compare(ieee80211com_t * ic,ieee80211_node_t * a,ieee80211_node_t * b)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
ieee80211_cancel_scan(ieee80211com_t * ic)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
ieee80211_end_scan(ieee80211com_t * ic)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
ieee80211_ibss_merge(ieee80211_node_t * in)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
ieee80211_setcurchan(ieee80211com_t * ic,struct ieee80211_channel * c)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
ieee80211_sta_join(ieee80211com_t * ic,ieee80211_node_t * selbs)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
ieee80211_sta_leave(ieee80211com_t * ic,ieee80211_node_t * in)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 *
ieee80211_node_alloc(ieee80211com_t * ic)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
ieee80211_node_cleanup(ieee80211_node_t * in)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
ieee80211_node_free(ieee80211_node_t * in)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
ieee80211_node_getrssi(const ieee80211_node_t * in)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
node_cleanfrag(ieee80211_node_t * in)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
ieee80211_setup_node(ieee80211com_t * ic,ieee80211_node_table_t * nt,ieee80211_node_t * in,const uint8_t * macaddr)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 *
ieee80211_alloc_node(ieee80211com_t * ic,ieee80211_node_table_t * nt,const uint8_t * macaddr)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 *
ieee80211_tmp_node(ieee80211com_t * ic,const uint8_t * macaddr)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 *
ieee80211_dup_bss(ieee80211_node_table_t * nt,const uint8_t * macaddr)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 *
ieee80211_find_node_locked(ieee80211_node_table_t * nt,const uint8_t * macaddr)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 *
ieee80211_find_node(ieee80211_node_table_t * nt,const uint8_t * macaddr)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 *
ieee80211_find_node_with_ssid(ieee80211_node_table_t * nt,const uint8_t * macaddr,uint32_t ssidlen,const uint8_t * ssid)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 *
ieee80211_fakeup_adhoc_node(ieee80211_node_table_t * nt,const uint8_t * macaddr)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
ieee80211_saveie(uint8_t ** iep,const uint8_t * ie)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
saveie(uint8_t ** iep,const uint8_t * ie)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
ieee80211_add_scan(ieee80211com_t * ic,const struct ieee80211_scanparams * sp,const struct ieee80211_frame * wh,int subtype,int rssi,int rstamp)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
ieee80211_init_neighbor(ieee80211_node_t * in,const struct ieee80211_frame * wh,const struct ieee80211_scanparams * sp)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 *
ieee80211_add_neighbor(ieee80211com_t * ic,const struct ieee80211_frame * wh,const struct ieee80211_scanparams * sp)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 *
ieee80211_find_rxnode(ieee80211com_t * ic,const struct ieee80211_frame * wh)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 *
ieee80211_find_txnode(ieee80211com_t * ic,const uint8_t * daddr)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
ieee80211_free_node_locked(ieee80211_node_t * in)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
ieee80211_free_node(ieee80211_node_t * in)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
ieee80211_node_reclaim(ieee80211_node_table_t * nt,ieee80211_node_t * in)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
ieee80211_free_allnodes_locked(ieee80211_node_table_t * nt)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
ieee80211_free_allnodes(ieee80211_node_table_t * nt)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
ieee80211_timeout_scan_candidates(ieee80211_node_table_t * nt)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
ieee80211_timeout_stations(ieee80211_node_table_t * nt)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
ieee80211_iterate_nodes(ieee80211_node_table_t * nt,ieee80211_iter_func * f,void * arg)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
ieee80211_node_leave(ieee80211com_t * ic,ieee80211_node_t * in)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
ieee80211_node_table_init(ieee80211com_t * ic,ieee80211_node_table_t * nt,const char * name,int inact,int keyixmax,void (* timeout)(ieee80211_node_table_t *))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
ieee80211_node_table_reset(ieee80211_node_table_t * nt)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
ieee80211_node_table_cleanup(ieee80211_node_table_t * nt)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