13147Sxc151355 /*
2*3993Seh146360  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
33147Sxc151355  * Use is subject to license terms.
43147Sxc151355  */
53147Sxc151355 
63147Sxc151355 /*
73147Sxc151355  * Copyright (c) 2001 Atsushi Onoe
83147Sxc151355  * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting
93147Sxc151355  * All rights reserved.
103147Sxc151355  *
113147Sxc151355  * Redistribution and use in source and binary forms, with or without
123147Sxc151355  * modification, are permitted provided that the following conditions
133147Sxc151355  * are met:
143147Sxc151355  * 1. Redistributions of source code must retain the above copyright
153147Sxc151355  *    notice, this list of conditions and the following disclaimer.
163147Sxc151355  * 2. Redistributions in binary form must reproduce the above copyright
173147Sxc151355  *    notice, this list of conditions and the following disclaimer in the
183147Sxc151355  *    documentation and/or other materials provided with the distribution.
193147Sxc151355  * 3. The name of the author may not be used to endorse or promote products
203147Sxc151355  *    derived from this software without specific prior written permission.
213147Sxc151355  *
223147Sxc151355  * Alternatively, this software may be distributed under the terms of the
233147Sxc151355  * GNU General Public License ("GPL") version 2 as published by the Free
243147Sxc151355  * Software Foundation.
253147Sxc151355  *
263147Sxc151355  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
273147Sxc151355  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
283147Sxc151355  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
293147Sxc151355  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
303147Sxc151355  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
313147Sxc151355  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
323147Sxc151355  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
333147Sxc151355  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
343147Sxc151355  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
353147Sxc151355  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
363147Sxc151355  */
373147Sxc151355 
383147Sxc151355 #pragma ident	"%Z%%M%	%I%	%E% SMI"
393147Sxc151355 
403147Sxc151355 /*
413147Sxc151355  * Node management routines
423147Sxc151355  */
433147Sxc151355 
443147Sxc151355 #include "net80211_impl.h"
453147Sxc151355 
463147Sxc151355 static ieee80211_node_t *ieee80211_node_alloc(ieee80211com_t *);
473147Sxc151355 static void ieee80211_node_cleanup(ieee80211_node_t *);
483147Sxc151355 static void ieee80211_node_free(ieee80211_node_t *);
493147Sxc151355 static uint8_t ieee80211_node_getrssi(const ieee80211_node_t *);
503147Sxc151355 static void ieee80211_setup_node(ieee80211com_t *, ieee80211_node_table_t *,
513147Sxc151355     ieee80211_node_t *, const uint8_t *);
523147Sxc151355 static void ieee80211_node_reclaim(ieee80211_node_table_t *,
533147Sxc151355     ieee80211_node_t *);
543147Sxc151355 static void ieee80211_free_node_locked(ieee80211_node_t *);
553147Sxc151355 static void ieee80211_free_allnodes(ieee80211_node_table_t *);
563147Sxc151355 static void ieee80211_node_leave(ieee80211com_t *, ieee80211_node_t *);
573147Sxc151355 static void ieee80211_timeout_scan_candidates(ieee80211_node_table_t *);
583147Sxc151355 static void ieee80211_timeout_stations(ieee80211_node_table_t *);
593147Sxc151355 static void ieee80211_node_table_init(ieee80211com_t *,
603147Sxc151355     ieee80211_node_table_t *, const char *, int, int,
613147Sxc151355     void (*timeout)(ieee80211_node_table_t *));
623147Sxc151355 static void ieee80211_node_table_cleanup(ieee80211_node_table_t *);
633147Sxc151355 
643147Sxc151355 /*
653147Sxc151355  * association failures before ignored
663147Sxc151355  * The failure may be caused by the response frame is lost for
673147Sxc151355  * environmental reason. So Try associate more than once before
683147Sxc151355  * ignore the node
693147Sxc151355  */
703147Sxc151355 #define	IEEE80211_STA_FAILS_MAX	2
713147Sxc151355 
723147Sxc151355 /*
733147Sxc151355  * Initialize node database management callbacks for the interface.
743147Sxc151355  * This function is called by ieee80211_attach(). These callback
753147Sxc151355  * functions may be overridden in special circumstances, as long as
763147Sxc151355  * as this is done after calling ieee80211_attach() and prior to any
773147Sxc151355  * other call which may allocate a node
783147Sxc151355  */
793147Sxc151355 void
803147Sxc151355 ieee80211_node_attach(ieee80211com_t *ic)
813147Sxc151355 {
823147Sxc151355 	struct ieee80211_impl *im = ic->ic_private;
833147Sxc151355 
843147Sxc151355 	ic->ic_node_alloc = ieee80211_node_alloc;
853147Sxc151355 	ic->ic_node_free = ieee80211_node_free;
863147Sxc151355 	ic->ic_node_cleanup = ieee80211_node_cleanup;
873147Sxc151355 	ic->ic_node_getrssi = ieee80211_node_getrssi;
883147Sxc151355 
893147Sxc151355 	/* default station inactivity timer setings */
903147Sxc151355 	im->im_inact_init = IEEE80211_INACT_INIT;
913147Sxc151355 	im->im_inact_assoc = IEEE80211_INACT_ASSOC;
923147Sxc151355 	im->im_inact_run = IEEE80211_INACT_RUN;
933147Sxc151355 	im->im_inact_probe = IEEE80211_INACT_PROBE;
943147Sxc151355 }
953147Sxc151355 
963147Sxc151355 /*
973147Sxc151355  * Initialize node databases and the ic_bss node element.
983147Sxc151355  */
993147Sxc151355 void
1003147Sxc151355 ieee80211_node_lateattach(ieee80211com_t *ic)
1013147Sxc151355 {
1023147Sxc151355 	/*
1033147Sxc151355 	 * Calculate ic_tim_bitmap size in bytes
1043147Sxc151355 	 * IEEE80211_AID_MAX defines maximum bits in ic_tim_bitmap
1053147Sxc151355 	 */
1063147Sxc151355 	ic->ic_tim_len = howmany(IEEE80211_AID_MAX, 8) * sizeof (uint8_t);
1073147Sxc151355 
1083147Sxc151355 	ieee80211_node_table_init(ic, &ic->ic_sta, "station",
1093147Sxc151355 		IEEE80211_INACT_INIT, IEEE80211_WEP_NKID,
1103147Sxc151355 		ieee80211_timeout_stations);
1113147Sxc151355 	ieee80211_node_table_init(ic, &ic->ic_scan, "scan",
1123147Sxc151355 		IEEE80211_INACT_SCAN, 0, ieee80211_timeout_scan_candidates);
1133147Sxc151355 
1143147Sxc151355 	ieee80211_reset_bss(ic);
1153147Sxc151355 }
1163147Sxc151355 
1173147Sxc151355 /*
1183147Sxc151355  * Destroy all node databases and is usually called during device detach
1193147Sxc151355  */
1203147Sxc151355 void
1213147Sxc151355 ieee80211_node_detach(ieee80211com_t *ic)
1223147Sxc151355 {
1233147Sxc151355 	/* Node Detach */
1243147Sxc151355 	if (ic->ic_bss != NULL) {
1253147Sxc151355 		ieee80211_free_node(ic->ic_bss);
1263147Sxc151355 		ic->ic_bss = NULL;
1273147Sxc151355 	}
1283147Sxc151355 	ieee80211_node_table_cleanup(&ic->ic_scan);
1293147Sxc151355 	ieee80211_node_table_cleanup(&ic->ic_sta);
1303147Sxc151355 }
1313147Sxc151355 
1323147Sxc151355 /*
1333147Sxc151355  * Increase a node's reference count
1343147Sxc151355  *
1353147Sxc151355  * Return pointer to the node
1363147Sxc151355  */
1373147Sxc151355 ieee80211_node_t *
1383147Sxc151355 ieee80211_ref_node(ieee80211_node_t *in)
1393147Sxc151355 {
1403147Sxc151355 	ieee80211_node_incref(in);
1413147Sxc151355 	return (in);
1423147Sxc151355 }
1433147Sxc151355 
1443147Sxc151355 /*
1453147Sxc151355  * Dexrease a node's reference count
1463147Sxc151355  */
1473147Sxc151355 void
1483147Sxc151355 ieee80211_unref_node(ieee80211_node_t **in)
1493147Sxc151355 {
1503147Sxc151355 	ieee80211_node_decref(*in);
1513147Sxc151355 	*in = NULL;			/* guard against use */
1523147Sxc151355 }
1533147Sxc151355 
1543147Sxc151355 /*
1553147Sxc151355  * Mark ports authorized for data traffic. This function is usually
1563147Sxc151355  * used by 802.1x authenticator.
1573147Sxc151355  */
1583147Sxc151355 void
1593147Sxc151355 ieee80211_node_authorize(ieee80211_node_t *in)
1603147Sxc151355 {
1613147Sxc151355 	ieee80211_impl_t *im = in->in_ic->ic_private;
1623147Sxc151355 
1633147Sxc151355 	in->in_flags |= IEEE80211_NODE_AUTH;
1643147Sxc151355 	in->in_inact_reload = im->im_inact_run;
1653147Sxc151355 }
1663147Sxc151355 
1673147Sxc151355 /*
1683147Sxc151355  * Mark ports unauthorized for data traffic. This function is usually
1693147Sxc151355  * used by 802.1x authenticator.
1703147Sxc151355  */
1713147Sxc151355 void
1723147Sxc151355 ieee80211_node_unauthorize(ieee80211_node_t *in)
1733147Sxc151355 {
1743147Sxc151355 	in->in_flags &= ~IEEE80211_NODE_AUTH;
1753147Sxc151355 }
1763147Sxc151355 
1773147Sxc151355 /*
1783147Sxc151355  * Set/change the channel.  The rate set is also updated as
1793147Sxc151355  * to insure a consistent view by drivers.
1803147Sxc151355  */
1813147Sxc151355 static void
1823147Sxc151355 ieee80211_node_setchan(ieee80211com_t *ic, ieee80211_node_t *in,
1833147Sxc151355     struct ieee80211_channel *chan)
1843147Sxc151355 {
1853147Sxc151355 	if (chan == IEEE80211_CHAN_ANYC)
1863147Sxc151355 		chan = ic->ic_curchan;
1873147Sxc151355 	in->in_chan = chan;
1883147Sxc151355 	in->in_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)];
1893147Sxc151355 }
1903147Sxc151355 
1913147Sxc151355 /*
1923147Sxc151355  * Initialize the channel set to scan based on the available channels
1933147Sxc151355  * and the current PHY mode.
1943147Sxc151355  */
1953147Sxc151355 static void
1963147Sxc151355 ieee80211_reset_scan(ieee80211com_t *ic)
1973147Sxc151355 {
1983147Sxc151355 	ieee80211_impl_t	*im = ic->ic_private;
1993147Sxc151355 
2003147Sxc151355 	if (ic->ic_des_chan != IEEE80211_CHAN_ANYC) {
2013147Sxc151355 		(void) memset(im->im_chan_scan, 0, sizeof (im->im_chan_scan));
2023147Sxc151355 		ieee80211_setbit(im->im_chan_scan,
2033147Sxc151355 			ieee80211_chan2ieee(ic, ic->ic_des_chan));
2043147Sxc151355 	} else {
2053147Sxc151355 		bcopy(ic->ic_chan_active, im->im_chan_scan,
2063147Sxc151355 			sizeof (ic->ic_chan_active));
2073147Sxc151355 	}
2083147Sxc151355 	ieee80211_dbg(IEEE80211_MSG_SCAN, "ieee80211_reset_scan(): "
2093147Sxc151355 		"start chan %u\n", ieee80211_chan2ieee(ic, ic->ic_curchan));
2103147Sxc151355 }
2113147Sxc151355 
2123147Sxc151355 /*
2133147Sxc151355  * Begin an active scan. Initialize the node cache. The scan
2143147Sxc151355  * begins on the next radio channel by calling ieee80211_next_scan().
2153147Sxc151355  * The actual scanning is not automated. The driver itself
2163147Sxc151355  * only handles setting the radio frequency and stepping through
2173147Sxc151355  * the channels.
2183147Sxc151355  */
2193147Sxc151355 void
2203147Sxc151355 ieee80211_begin_scan(ieee80211com_t *ic, boolean_t reset)
2213147Sxc151355 {
2223147Sxc151355 	IEEE80211_LOCK(ic);
2233147Sxc151355 
2243147Sxc151355 	if (ic->ic_opmode != IEEE80211_M_HOSTAP)
2253147Sxc151355 		ic->ic_flags |= IEEE80211_F_ASCAN;
2263147Sxc151355 	ieee80211_dbg(IEEE80211_MSG_SCAN,
2273147Sxc151355 		"begin %s scan in %s mode on channel %u\n",
2283147Sxc151355 		(ic->ic_flags & IEEE80211_F_ASCAN) ?  "active" : "passive",
2293147Sxc151355 		ieee80211_phymode_name[ic->ic_curmode],
2303147Sxc151355 		ieee80211_chan2ieee(ic, ic->ic_curchan));
2313147Sxc151355 
2323147Sxc151355 	/*
2333147Sxc151355 	 * Clear scan state and flush any previously seen AP's.
2343147Sxc151355 	 */
2353147Sxc151355 	ieee80211_reset_scan(ic);
2363147Sxc151355 	if (reset)
2373147Sxc151355 		ieee80211_free_allnodes(&ic->ic_scan);
2383147Sxc151355 
2393147Sxc151355 	ic->ic_flags |= IEEE80211_F_SCAN;
2403147Sxc151355 	IEEE80211_UNLOCK(ic);
2413147Sxc151355 
2423147Sxc151355 	/* Scan the next channel. */
2433147Sxc151355 	ieee80211_next_scan(ic);
2443147Sxc151355 }
2453147Sxc151355 
2463147Sxc151355 /*
2473147Sxc151355  * Switch to the next channel marked for scanning.
2483147Sxc151355  * A driver is expected to first call ieee80211_begin_scan(),
2493147Sxc151355  * to initialize the node cache, then set the radio channel
2503147Sxc151355  * on the device. And then after a certain time has elapsed,
2513147Sxc151355  * call ieee80211_next_scan() to move to the next channel.
2523147Sxc151355  * Typically, a timeout routine is used to automate this process.
2533147Sxc151355  */
2543147Sxc151355 void
2553147Sxc151355 ieee80211_next_scan(ieee80211com_t *ic)
2563147Sxc151355 {
2573147Sxc151355 	ieee80211_impl_t *im = ic->ic_private;
2583147Sxc151355 	struct ieee80211_channel *chan;
2593147Sxc151355 
2603147Sxc151355 	IEEE80211_LOCK(ic);
2613147Sxc151355 	/*
2623147Sxc151355 	 * Insure any previous mgt frame timeouts don't fire.
2633147Sxc151355 	 * This assumes the driver does the right thing in
2643147Sxc151355 	 * flushing anything queued in the driver and below.
2653147Sxc151355 	 */
2663147Sxc151355 	im->im_mgt_timer = 0;
2673147Sxc151355 
2683147Sxc151355 	chan = ic->ic_curchan;
2693147Sxc151355 	do {
2703147Sxc151355 		if (++chan > &ic->ic_sup_channels[IEEE80211_CHAN_MAX])
2713147Sxc151355 			chan = &ic->ic_sup_channels[0];
2723147Sxc151355 		if (ieee80211_isset(im->im_chan_scan,
2733147Sxc151355 		    ieee80211_chan2ieee(ic, chan))) {
2743147Sxc151355 			ieee80211_clrbit(im->im_chan_scan,
2753147Sxc151355 				ieee80211_chan2ieee(ic, chan));
2763147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_SCAN,
2773147Sxc151355 				"ieee80211_next_scan: chan %d->%d\n",
2783147Sxc151355 				ieee80211_chan2ieee(ic, ic->ic_curchan),
2793147Sxc151355 				ieee80211_chan2ieee(ic, chan));
2803147Sxc151355 			ic->ic_curchan = chan;
2813147Sxc151355 			/*
2823147Sxc151355 			 * drivers should do this as needed,
2833147Sxc151355 			 * for now maintain compatibility
2843147Sxc151355 			 */
2853147Sxc151355 			ic->ic_bss->in_rates =
2863147Sxc151355 				ic->ic_sup_rates[ieee80211_chan2mode(ic, chan)];
2873147Sxc151355 			IEEE80211_UNLOCK(ic);
2883147Sxc151355 			ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
2893147Sxc151355 			return;
2903147Sxc151355 		}
2913147Sxc151355 	} while (chan != ic->ic_curchan);
2923147Sxc151355 	IEEE80211_UNLOCK(ic);
2933147Sxc151355 	ieee80211_end_scan(ic);
2943147Sxc151355 }
2953147Sxc151355 
2963147Sxc151355 /*
2973147Sxc151355  * Copy useful state from node obss into nbss.
2983147Sxc151355  */
2993147Sxc151355 static void
3003147Sxc151355 ieee80211_copy_bss(ieee80211_node_t *nbss, const ieee80211_node_t *obss)
3013147Sxc151355 {
3023147Sxc151355 	/* propagate useful state */
3033147Sxc151355 	nbss->in_authmode = obss->in_authmode;
3043147Sxc151355 	nbss->in_txpower = obss->in_txpower;
3053147Sxc151355 	nbss->in_vlan = obss->in_vlan;
3063147Sxc151355 }
3073147Sxc151355 
3083147Sxc151355 /*
3093147Sxc151355  * Setup the net80211 specific portion of an interface's softc, ic,
3103147Sxc151355  * for use in IBSS mode
3113147Sxc151355  */
3123147Sxc151355 void
3133147Sxc151355 ieee80211_create_ibss(ieee80211com_t *ic, struct ieee80211_channel *chan)
3143147Sxc151355 {
3153147Sxc151355 	ieee80211_impl_t *im = ic->ic_private;
3163147Sxc151355 	ieee80211_node_table_t *nt;
3173147Sxc151355 	ieee80211_node_t *in;
3183147Sxc151355 
3193147Sxc151355 	IEEE80211_LOCK_ASSERT(ic);
3203147Sxc151355 	ieee80211_dbg(IEEE80211_MSG_SCAN, "ieee80211_create_ibss: "
3213147Sxc151355 		"creating ibss\n");
3223147Sxc151355 
3233147Sxc151355 	/*
3243147Sxc151355 	 * Create the station/neighbor table.  Note that for adhoc
3253147Sxc151355 	 * mode we make the initial inactivity timer longer since
3263147Sxc151355 	 * we create nodes only through discovery and they typically
3273147Sxc151355 	 * are long-lived associations.
3283147Sxc151355 	 */
3293147Sxc151355 	nt = &ic->ic_sta;
3303147Sxc151355 	IEEE80211_NODE_LOCK(nt);
3313147Sxc151355 	nt->nt_name = "neighbor";
3323147Sxc151355 	nt->nt_inact_init = im->im_inact_run;
3333147Sxc151355 	IEEE80211_NODE_UNLOCK(nt);
3343147Sxc151355 
3353147Sxc151355 	in = ieee80211_alloc_node(ic, &ic->ic_sta, ic->ic_macaddr);
3363147Sxc151355 	if (in == NULL) {
3373147Sxc151355 		ieee80211_err("ieee80211_create_ibss(): alloc node failed\n");
3383147Sxc151355 		return;
3393147Sxc151355 	}
3403147Sxc151355 	IEEE80211_ADDR_COPY(in->in_bssid, ic->ic_macaddr);
3413147Sxc151355 	in->in_esslen = ic->ic_des_esslen;
3423147Sxc151355 	(void) memcpy(in->in_essid, ic->ic_des_essid, in->in_esslen);
3433147Sxc151355 	ieee80211_copy_bss(in, ic->ic_bss);
3443147Sxc151355 	in->in_intval = ic->ic_bintval;
3453147Sxc151355 	if (ic->ic_flags & IEEE80211_F_PRIVACY)
3463147Sxc151355 		in->in_capinfo |= IEEE80211_CAPINFO_PRIVACY;
3473147Sxc151355 	if (ic->ic_phytype == IEEE80211_T_FH) {
3483147Sxc151355 		in->in_fhdwell = 200;
3493147Sxc151355 		in->in_fhindex = 1;
3503147Sxc151355 	}
3513147Sxc151355 	switch (ic->ic_opmode) {
3523147Sxc151355 	case IEEE80211_M_IBSS:
3533147Sxc151355 		ic->ic_flags |= IEEE80211_F_SIBSS;
3543147Sxc151355 		in->in_capinfo |= IEEE80211_CAPINFO_IBSS;
3553147Sxc151355 		if (ic->ic_flags & IEEE80211_F_DESBSSID)
3563147Sxc151355 			IEEE80211_ADDR_COPY(in->in_bssid, ic->ic_des_bssid);
3573147Sxc151355 		else
3583147Sxc151355 			in->in_bssid[0] |= 0x02;	/* local bit for IBSS */
3593147Sxc151355 		break;
3603147Sxc151355 	case IEEE80211_M_AHDEMO:
3613147Sxc151355 		if (ic->ic_flags & IEEE80211_F_DESBSSID)
3623147Sxc151355 			IEEE80211_ADDR_COPY(in->in_bssid, ic->ic_des_bssid);
3633147Sxc151355 		else
3643147Sxc151355 			(void) memset(in->in_bssid, 0, IEEE80211_ADDR_LEN);
3653147Sxc151355 		break;
3663147Sxc151355 	default:
3673147Sxc151355 		ieee80211_err("ieee80211_create_ibss(): "
3683147Sxc151355 			"wrong opmode %u to creat IBSS, abort\n",
3693147Sxc151355 			ic->ic_opmode);
3703147Sxc151355 		ieee80211_free_node(in);
3713147Sxc151355 		return;
3723147Sxc151355 	}
3733147Sxc151355 
3743147Sxc151355 	/*
3753147Sxc151355 	 * Fix the channel and related attributes.
3763147Sxc151355 	 */
3773147Sxc151355 	ieee80211_node_setchan(ic, in, chan);
3783147Sxc151355 	ic->ic_curchan = chan;
3793147Sxc151355 	ic->ic_curmode = ieee80211_chan2mode(ic, chan);
3803147Sxc151355 	/*
3813147Sxc151355 	 * Do mode-specific rate setup.
3823147Sxc151355 	 */
3833147Sxc151355 	ieee80211_setbasicrates(&in->in_rates, ic->ic_curmode);
3843147Sxc151355 	IEEE80211_UNLOCK(ic);
3853147Sxc151355 	ieee80211_sta_join(ic, in);
3863147Sxc151355 	IEEE80211_LOCK(ic);
3873147Sxc151355 }
3883147Sxc151355 
3893147Sxc151355 void
3903147Sxc151355 ieee80211_reset_bss(ieee80211com_t *ic)
3913147Sxc151355 {
3923147Sxc151355 	ieee80211_node_t *in;
3933147Sxc151355 	ieee80211_node_t *obss;
3943147Sxc151355 
3953147Sxc151355 	in = ieee80211_alloc_node(ic, &ic->ic_scan, ic->ic_macaddr);
3963147Sxc151355 	ASSERT(in != NULL);
3973147Sxc151355 	obss = ic->ic_bss;
3983147Sxc151355 	ic->ic_bss = ieee80211_ref_node(in);
3993147Sxc151355 	if (obss != NULL) {
4003147Sxc151355 		ieee80211_copy_bss(in, obss);
4013147Sxc151355 		in->in_intval = ic->ic_bintval;
4023147Sxc151355 		ieee80211_free_node(obss);
4033147Sxc151355 	}
4043147Sxc151355 }
4053147Sxc151355 
4063147Sxc151355 static int
4073147Sxc151355 ieee80211_match_bss(ieee80211com_t *ic, ieee80211_node_t *in)
4083147Sxc151355 {
4093147Sxc151355 	uint8_t rate;
4103147Sxc151355 	int fail;
4113147Sxc151355 
4123147Sxc151355 	fail = 0;
4133147Sxc151355 	if (ieee80211_isclr(ic->ic_chan_active,
4143147Sxc151355 	    ieee80211_chan2ieee(ic, in->in_chan))) {
4153147Sxc151355 		fail |= IEEE80211_BADCHAN;
4163147Sxc151355 	}
4173147Sxc151355 	if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
4183147Sxc151355 	    in->in_chan != ic->ic_des_chan) {
4193147Sxc151355 		fail |= IEEE80211_BADCHAN;
4203147Sxc151355 	}
4213147Sxc151355 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
4223147Sxc151355 		if (!(in->in_capinfo & IEEE80211_CAPINFO_IBSS))
4233147Sxc151355 			fail |= IEEE80211_BADOPMODE;
4243147Sxc151355 	} else {
4253147Sxc151355 		if (!(in->in_capinfo & IEEE80211_CAPINFO_ESS))
4263147Sxc151355 			fail |= IEEE80211_BADOPMODE;
4273147Sxc151355 	}
4283147Sxc151355 	if (ic->ic_flags & IEEE80211_F_PRIVACY) {
4293147Sxc151355 		if (!(in->in_capinfo & IEEE80211_CAPINFO_PRIVACY))
4303147Sxc151355 			fail |= IEEE80211_BADPRIVACY;
4313147Sxc151355 	} else {
4323147Sxc151355 		if (in->in_capinfo & IEEE80211_CAPINFO_PRIVACY)
4333147Sxc151355 			fail |= IEEE80211_BADPRIVACY;
4343147Sxc151355 	}
4353147Sxc151355 	rate = ieee80211_fix_rate(in, IEEE80211_F_DONEGO | IEEE80211_F_DOFRATE);
4363147Sxc151355 	if (rate & IEEE80211_RATE_BASIC)
4373147Sxc151355 		fail |= IEEE80211_BADRATE;
4383147Sxc151355 	if (ic->ic_des_esslen != 0 &&
4393147Sxc151355 	    (in->in_esslen != ic->ic_des_esslen ||
4403147Sxc151355 	    memcmp(in->in_essid, ic->ic_des_essid, ic->ic_des_esslen) != 0)) {
4413147Sxc151355 		fail |= IEEE80211_BADESSID;
4423147Sxc151355 	}
4433147Sxc151355 	if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
4443147Sxc151355 	    !IEEE80211_ADDR_EQ(ic->ic_des_bssid, in->in_bssid)) {
4453147Sxc151355 		fail |= IEEE80211_BADBSSID;
4463147Sxc151355 	}
4473147Sxc151355 	if (in->in_fails >= IEEE80211_STA_FAILS_MAX)
4483147Sxc151355 		fail |= IEEE80211_NODEFAIL;
4493147Sxc151355 
4503147Sxc151355 	return (fail);
4513147Sxc151355 }
4523147Sxc151355 
4533147Sxc151355 #define	IEEE80211_MAXRATE(_rs) \
4543147Sxc151355 	((_rs).ir_rates[(_rs).ir_nrates - 1] & IEEE80211_RATE_VAL)
4553147Sxc151355 
4563147Sxc151355 /*
4573147Sxc151355  * Compare the capabilities of node a with node b and decide which is
4583147Sxc151355  * more desirable (return b if b is considered better than a).  Note
4593147Sxc151355  * that we assume compatibility/usability has already been checked
4603147Sxc151355  * so we don't need to (e.g. validate whether privacy is supported).
4613147Sxc151355  * Used to select the best scan candidate for association in a BSS.
4623147Sxc151355  *
4633147Sxc151355  * Return desired node
4643147Sxc151355  */
4653147Sxc151355 static ieee80211_node_t *
4663147Sxc151355 ieee80211_node_compare(ieee80211com_t *ic, ieee80211_node_t *a,
4673147Sxc151355     ieee80211_node_t *b)
4683147Sxc151355 {
4693147Sxc151355 	uint8_t maxa;
4703147Sxc151355 	uint8_t maxb;
4713147Sxc151355 	uint8_t rssia;
4723147Sxc151355 	uint8_t rssib;
4733147Sxc151355 
4743147Sxc151355 	/* privacy support preferred */
4753147Sxc151355 	if ((a->in_capinfo & IEEE80211_CAPINFO_PRIVACY) &&
4763147Sxc151355 	    !(b->in_capinfo & IEEE80211_CAPINFO_PRIVACY)) {
4773147Sxc151355 		return (a);
4783147Sxc151355 	}
4793147Sxc151355 	if (!(a->in_capinfo & IEEE80211_CAPINFO_PRIVACY) &&
4803147Sxc151355 	    (b->in_capinfo & IEEE80211_CAPINFO_PRIVACY)) {
4813147Sxc151355 		return (b);
4823147Sxc151355 	}
4833147Sxc151355 
4843147Sxc151355 	/* compare count of previous failures */
4853147Sxc151355 	if (b->in_fails != a->in_fails)
4863147Sxc151355 		return ((a->in_fails > b->in_fails) ? b : a);
4873147Sxc151355 
4883147Sxc151355 	rssia = ic->ic_node_getrssi(a);
4893147Sxc151355 	rssib = ic->ic_node_getrssi(b);
4903147Sxc151355 	if (ABS(rssib - rssia) < IEEE80211_RSSI_CMP_THRESHOLD) {
4913147Sxc151355 		/* best/max rate preferred if signal level close enough */
4923147Sxc151355 		maxa = IEEE80211_MAXRATE(a->in_rates);
4933147Sxc151355 		maxb = IEEE80211_MAXRATE(b->in_rates);
4943147Sxc151355 		if (maxa != maxb)
4953147Sxc151355 			return ((maxb > maxa) ? b : a);
4963147Sxc151355 		/* for now just prefer 5Ghz band to all other bands */
4973147Sxc151355 		if (IEEE80211_IS_CHAN_5GHZ(a->in_chan) &&
4983147Sxc151355 		    !IEEE80211_IS_CHAN_5GHZ(b->in_chan)) {
4993147Sxc151355 			return (a);
5003147Sxc151355 		}
5013147Sxc151355 		if (!IEEE80211_IS_CHAN_5GHZ(a->in_chan) &&
5023147Sxc151355 		    IEEE80211_IS_CHAN_5GHZ(b->in_chan)) {
5033147Sxc151355 			return (b);
5043147Sxc151355 		}
5053147Sxc151355 	}
5063147Sxc151355 	/* all things being equal, compare signal level */
5073147Sxc151355 	return ((rssib > rssia) ? b : a);
5083147Sxc151355 }
5093147Sxc151355 
5103147Sxc151355 /*
5113147Sxc151355  * Mark an ongoing scan stopped.
5123147Sxc151355  */
5133147Sxc151355 void
5143147Sxc151355 ieee80211_cancel_scan(ieee80211com_t *ic)
5153147Sxc151355 {
5163147Sxc151355 	IEEE80211_LOCK(ic);
5173147Sxc151355 	ieee80211_dbg(IEEE80211_MSG_SCAN, "ieee80211_cancel_scan()"
5183147Sxc151355 		"end %s scan\n",
5193147Sxc151355 		(ic->ic_flags & IEEE80211_F_ASCAN) ?  "active" : "passive");
5203147Sxc151355 	ic->ic_flags &= ~(IEEE80211_F_SCAN | IEEE80211_F_ASCAN);
5213147Sxc151355 	cv_broadcast(&((ieee80211_impl_t *)ic->ic_private)->im_scan_cv);
5223147Sxc151355 	IEEE80211_UNLOCK(ic);
5233147Sxc151355 }
5243147Sxc151355 
5253147Sxc151355 /*
5263147Sxc151355  * Complete a scan of potential channels. It is called by
5273147Sxc151355  * ieee80211_next_scan() when the state machine has performed
5283147Sxc151355  * a full cycle of scaning on all available radio channels.
5293147Sxc151355  * ieee80211_end_scan() will inspect the node cache for suitable
5303147Sxc151355  * APs found during scaning, and associate with one, should
5313147Sxc151355  * the parameters of the node match those of the configuration
5323147Sxc151355  * requested from userland.
5333147Sxc151355  */
5343147Sxc151355 void
5353147Sxc151355 ieee80211_end_scan(ieee80211com_t *ic)
5363147Sxc151355 {
5373147Sxc151355 	ieee80211_node_table_t *nt = &ic->ic_scan;
5383147Sxc151355 	ieee80211_node_t *in;
5393147Sxc151355 	ieee80211_node_t *selbs;
5403147Sxc151355 
5413147Sxc151355 	ieee80211_cancel_scan(ic);
5423147Sxc151355 	IEEE80211_LOCK(ic);
5433147Sxc151355 
5443147Sxc151355 	/*
5453147Sxc151355 	 * Automatic sequencing; look for a candidate and
5463147Sxc151355 	 * if found join the network.
5473147Sxc151355 	 */
5483147Sxc151355 	/* NB: unlocked read should be ok */
5493147Sxc151355 	in = list_head(&nt->nt_node);
5503147Sxc151355 	if (in == NULL) {
5513147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_SCAN, "ieee80211_end_scan: "
5523147Sxc151355 			"no scan candidate\n");
5533147Sxc151355 	notfound:
5543147Sxc151355 		if (ic->ic_opmode == IEEE80211_M_IBSS &&
5553147Sxc151355 		    (ic->ic_flags & IEEE80211_F_IBSSON) &&
5563147Sxc151355 		    ic->ic_des_esslen != 0) {
5573147Sxc151355 			ieee80211_create_ibss(ic, ic->ic_ibss_chan);
5583147Sxc151355 			IEEE80211_UNLOCK(ic);
5593147Sxc151355 			return;
5603147Sxc151355 		}
5613147Sxc151355 
5623147Sxc151355 		/*
5633147Sxc151355 		 * Reset the list of channels to scan and start again.
5643147Sxc151355 		 */
5653147Sxc151355 		ieee80211_reset_scan(ic);
5663147Sxc151355 		ic->ic_flags |= IEEE80211_F_SCAN | IEEE80211_F_ASCAN;
5673147Sxc151355 		IEEE80211_UNLOCK(ic);
5683147Sxc151355 
5693147Sxc151355 		ieee80211_next_scan(ic);
5703147Sxc151355 		return;
5713147Sxc151355 	}
5723147Sxc151355 
5733147Sxc151355 	if (ic->ic_flags & IEEE80211_F_SCANONLY) {	/* scan only */
5743147Sxc151355 		ic->ic_flags &= ~IEEE80211_F_SCANONLY;
5753147Sxc151355 		IEEE80211_UNLOCK(ic);
5763147Sxc151355 		ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
5773147Sxc151355 		return;
5783147Sxc151355 	}
5793147Sxc151355 
5803147Sxc151355 	selbs = NULL;
5813147Sxc151355 	IEEE80211_NODE_LOCK(nt);
5823147Sxc151355 	while (in != NULL) {
5833147Sxc151355 		if (in->in_fails >= IEEE80211_STA_FAILS_MAX) {
5843147Sxc151355 			ieee80211_node_t *tmpin = in;
5853147Sxc151355 
5863147Sxc151355 			/*
5873147Sxc151355 			 * The configuration of the access points may change
5883147Sxc151355 			 * during my scan.  So delete the entry for the AP
5893147Sxc151355 			 * and retry to associate if there is another beacon.
5903147Sxc151355 			 */
5913147Sxc151355 			in = list_next(&nt->nt_node, tmpin);
5923147Sxc151355 			ieee80211_node_reclaim(nt, tmpin);
5933147Sxc151355 			continue;
5943147Sxc151355 		}
595*3993Seh146360 		/*
596*3993Seh146360 		 * It's possible at some special moments, the in_chan will
597*3993Seh146360 		 * be none. Need to skip the null node.
598*3993Seh146360 		 */
599*3993Seh146360 		if (in->in_chan == IEEE80211_CHAN_ANYC) {
600*3993Seh146360 			in = list_next(&nt->nt_node, in);
601*3993Seh146360 			continue;
602*3993Seh146360 		}
6033147Sxc151355 		if (ieee80211_match_bss(ic, in) == 0) {
6043147Sxc151355 			if (selbs == NULL)
6053147Sxc151355 				selbs = in;
6063147Sxc151355 			else
6073147Sxc151355 				selbs = ieee80211_node_compare(ic, selbs, in);
6083147Sxc151355 		}
6093147Sxc151355 		in = list_next(&nt->nt_node, in);
6103147Sxc151355 	}
6113147Sxc151355 	IEEE80211_NODE_UNLOCK(nt);
6123147Sxc151355 	if (selbs == NULL)
6133147Sxc151355 		goto notfound;
6143147Sxc151355 	IEEE80211_UNLOCK(ic);
6153147Sxc151355 	ieee80211_sta_join(ic, selbs);
6163147Sxc151355 }
6173147Sxc151355 
6183147Sxc151355 
6193147Sxc151355 /*
6203147Sxc151355  * Handle 802.11 ad hoc network merge.  The convention, set by the
6213147Sxc151355  * Wireless Ethernet Compatibility Alliance (WECA), is that an 802.11
6223147Sxc151355  * station will change its BSSID to match the "oldest" 802.11 ad hoc
6233147Sxc151355  * network, on the same channel, that has the station's desired SSID.
6243147Sxc151355  * The "oldest" 802.11 network sends beacons with the greatest TSF
6253147Sxc151355  * timestamp.
6263147Sxc151355  * The caller is assumed to validate TSF's before attempting a merge.
6273147Sxc151355  *
6283147Sxc151355  * Return B_TRUE if the BSSID changed, B_FALSE otherwise.
6293147Sxc151355  */
6303147Sxc151355 boolean_t
6313147Sxc151355 ieee80211_ibss_merge(ieee80211_node_t *in)
6323147Sxc151355 {
6333147Sxc151355 	ieee80211com_t *ic = in->in_ic;
6343147Sxc151355 
6353147Sxc151355 	if (in == ic->ic_bss ||
6363147Sxc151355 	    IEEE80211_ADDR_EQ(in->in_bssid, ic->ic_bss->in_bssid)) {
6373147Sxc151355 		/* unchanged, nothing to do */
6383147Sxc151355 		return (B_FALSE);
6393147Sxc151355 	}
6403147Sxc151355 	if (ieee80211_match_bss(ic, in) != 0) {	/* capabilities mismatch */
6413147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_ASSOC, "ieee80211_ibss_merge: "
6423147Sxc151355 			" merge failed, capabilities mismatch\n");
6433147Sxc151355 		return (B_FALSE);
6443147Sxc151355 	}
6453147Sxc151355 	ieee80211_dbg(IEEE80211_MSG_ASSOC, "ieee80211_ibss_merge: "
6463147Sxc151355 		"new bssid %s: %s preamble, %s slot time%s\n",
6473147Sxc151355 		ieee80211_macaddr_sprintf(in->in_bssid),
6483147Sxc151355 		(ic->ic_flags & IEEE80211_F_SHPREAMBLE) ? "short" : "long",
6493147Sxc151355 		(ic->ic_flags & IEEE80211_F_SHSLOT) ? "short" : "long",
6503147Sxc151355 		(ic->ic_flags&IEEE80211_F_USEPROT) ? ", protection" : "");
6513147Sxc151355 	ieee80211_sta_join(ic, in);
6523147Sxc151355 	return (B_TRUE);
6533147Sxc151355 }
6543147Sxc151355 
6553147Sxc151355 /*
6563147Sxc151355  * Join the specified IBSS/BSS network.  The node is assumed to
6573147Sxc151355  * be passed in with a held reference.
6583147Sxc151355  */
6593147Sxc151355 void
6603147Sxc151355 ieee80211_sta_join(ieee80211com_t *ic, ieee80211_node_t *selbs)
6613147Sxc151355 {
6623147Sxc151355 	ieee80211_impl_t *im = ic->ic_private;
6633147Sxc151355 	ieee80211_node_t *obss;
6643147Sxc151355 
6653147Sxc151355 	IEEE80211_LOCK(ic);
6663147Sxc151355 	if (ic->ic_opmode == IEEE80211_M_IBSS) {
6673147Sxc151355 		ieee80211_node_table_t *nt;
6683147Sxc151355 
6693147Sxc151355 		/*
6703147Sxc151355 		 * Delete unusable rates; we've already checked
6713147Sxc151355 		 * that the negotiated rate set is acceptable.
6723147Sxc151355 		 */
6733147Sxc151355 		(void) ieee80211_fix_rate(selbs, IEEE80211_F_DODEL);
6743147Sxc151355 		/*
6753147Sxc151355 		 * Fillin the neighbor table
6763147Sxc151355 		 */
6773147Sxc151355 		nt = &ic->ic_sta;
6783147Sxc151355 		IEEE80211_NODE_LOCK(nt);
6793147Sxc151355 		nt->nt_name = "neighbor";
6803147Sxc151355 		nt->nt_inact_init = im->im_inact_run;
6813147Sxc151355 		IEEE80211_NODE_UNLOCK(nt);
6823147Sxc151355 	}
6833147Sxc151355 
6843147Sxc151355 	/*
6853147Sxc151355 	 * Committed to selbs, setup state.
6863147Sxc151355 	 */
6873147Sxc151355 	obss = ic->ic_bss;
6883147Sxc151355 	ic->ic_bss = ieee80211_ref_node(selbs);	/* Grab reference */
6893147Sxc151355 	if (obss != NULL) {
6903147Sxc151355 		ieee80211_copy_bss(selbs, obss);
6913147Sxc151355 		ieee80211_free_node(obss);
6923147Sxc151355 	}
6933147Sxc151355 	ic->ic_curmode = ieee80211_chan2mode(ic, selbs->in_chan);
6943147Sxc151355 	ic->ic_curchan = selbs->in_chan;
6953147Sxc151355 	/*
6963147Sxc151355 	 * Set the erp state (mostly the slot time) to deal with
6973147Sxc151355 	 * the auto-select case; this should be redundant if the
6983147Sxc151355 	 * mode is locked.
6993147Sxc151355 	 */
7003147Sxc151355 	ieee80211_reset_erp(ic);
7013147Sxc151355 
7023147Sxc151355 	IEEE80211_UNLOCK(ic);
7033147Sxc151355 	if (ic->ic_opmode == IEEE80211_M_STA)
7043147Sxc151355 		ieee80211_new_state(ic, IEEE80211_S_AUTH, -1);
7053147Sxc151355 	else
7063147Sxc151355 		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
7073147Sxc151355 }
7083147Sxc151355 
7093147Sxc151355 /*
7103147Sxc151355  * Leave the specified IBSS/BSS network.  The node is assumed to
7113147Sxc151355  * be passed in with a held reference.
7123147Sxc151355  */
7133147Sxc151355 void
7143147Sxc151355 ieee80211_sta_leave(ieee80211com_t *ic, ieee80211_node_t *in)
7153147Sxc151355 {
7163147Sxc151355 	IEEE80211_LOCK(ic);
7173147Sxc151355 	ic->ic_node_cleanup(in);
7183147Sxc151355 	ieee80211_notify_node_leave(ic, in);
7193147Sxc151355 	IEEE80211_UNLOCK(ic);
7203147Sxc151355 }
7213147Sxc151355 
7223147Sxc151355 /*
7233147Sxc151355  * Allocate a node. This is the default callback function for
7243147Sxc151355  * ic_node_alloc. This function may be overridden by the driver
7253147Sxc151355  * to allocate device specific node structure.
7263147Sxc151355  */
7273147Sxc151355 /* ARGSUSED */
7283147Sxc151355 static ieee80211_node_t *
7293147Sxc151355 ieee80211_node_alloc(ieee80211com_t *ic)
7303147Sxc151355 {
7313147Sxc151355 	return (kmem_zalloc(sizeof (ieee80211_node_t), KM_SLEEP));
7323147Sxc151355 }
7333147Sxc151355 
7343147Sxc151355 /*
7353147Sxc151355  * Cleanup a node, free any memory associated with the node.
7363147Sxc151355  * This is the default callback function for ic_node_cleanup
7373147Sxc151355  * and may be overridden by the driver.
7383147Sxc151355  */
7393147Sxc151355 static void
7403147Sxc151355 ieee80211_node_cleanup(ieee80211_node_t *in)
7413147Sxc151355 {
7423147Sxc151355 	in->in_associd = 0;
7433147Sxc151355 	in->in_rssi = 0;
7443147Sxc151355 	in->in_rstamp = 0;
7453147Sxc151355 	if (in->in_challenge != NULL) {
7463147Sxc151355 		kmem_free(in->in_challenge, IEEE80211_CHALLENGE_LEN);
7473147Sxc151355 		in->in_challenge = NULL;
7483147Sxc151355 	}
7493147Sxc151355 	if (in->in_rxfrag != NULL) {
7503147Sxc151355 		freemsg(in->in_rxfrag);
7513147Sxc151355 		in->in_rxfrag = NULL;
7523147Sxc151355 	}
7533147Sxc151355 }
7543147Sxc151355 
7553147Sxc151355 /*
7563147Sxc151355  * Free a node. This is the default callback function for ic_node_free
7573147Sxc151355  * and may be overridden by the driver to free memory used by device
7583147Sxc151355  * specific node structure
7593147Sxc151355  */
7603147Sxc151355 static void
7613147Sxc151355 ieee80211_node_free(ieee80211_node_t *in)
7623147Sxc151355 {
7633147Sxc151355 	ieee80211com_t *ic = in->in_ic;
7643147Sxc151355 
7653147Sxc151355 	ic->ic_node_cleanup(in);
7663147Sxc151355 	kmem_free(in, sizeof (ieee80211_node_t));
7673147Sxc151355 }
7683147Sxc151355 
7693147Sxc151355 /*
7703147Sxc151355  * Get a node current RSSI value. This is the default callback function
7713147Sxc151355  * for ic_node_getrssi and may be overridden by the driver to provide
7723147Sxc151355  * device specific RSSI calculation algorithm.
7733147Sxc151355  */
7743147Sxc151355 static uint8_t
7753147Sxc151355 ieee80211_node_getrssi(const ieee80211_node_t *in)
7763147Sxc151355 {
7773147Sxc151355 	return (in->in_rssi);
7783147Sxc151355 }
7793147Sxc151355 
7803147Sxc151355 /* Free fragment if not needed anymore */
7813147Sxc151355 static void
7823147Sxc151355 node_cleanfrag(ieee80211_node_t *in)
7833147Sxc151355 {
7843147Sxc151355 	clock_t ticks;
7853147Sxc151355 
7863147Sxc151355 	ticks = ddi_get_lbolt();
7873147Sxc151355 	if (in->in_rxfrag != NULL && ticks > (in->in_rxfragstamp + hz)) {
7883147Sxc151355 		freemsg(in->in_rxfrag);
7893147Sxc151355 		in->in_rxfrag = NULL;
7903147Sxc151355 	}
7913147Sxc151355 }
7923147Sxc151355 
7933147Sxc151355 /*
7943147Sxc151355  * Setup a node. Initialize the node with specified macaddr. Associate
7953147Sxc151355  * with the interface softc, ic, and add it to the specified node
7963147Sxc151355  * database.
7973147Sxc151355  */
7983147Sxc151355 static void
7993147Sxc151355 ieee80211_setup_node(ieee80211com_t *ic, ieee80211_node_table_t *nt,
8003147Sxc151355     ieee80211_node_t *in, const uint8_t *macaddr)
8013147Sxc151355 {
8023147Sxc151355 	int32_t hash;
8033147Sxc151355 
8043147Sxc151355 	ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_setup_node(): "
8053147Sxc151355 		"%p<%s> in %s table\n", in,
8063147Sxc151355 		ieee80211_macaddr_sprintf(macaddr),
8073147Sxc151355 		(nt != NULL) ? nt->nt_name : "NULL");
8083147Sxc151355 
8093147Sxc151355 	in->in_ic = ic;
8103147Sxc151355 	IEEE80211_ADDR_COPY(in->in_macaddr, macaddr);
8113147Sxc151355 	hash = ieee80211_node_hash(macaddr);
8123147Sxc151355 	ieee80211_node_initref(in);		/* mark referenced */
8133147Sxc151355 	in->in_authmode = IEEE80211_AUTH_OPEN;
8143147Sxc151355 	in->in_txpower = ic->ic_txpowlimit;	/* max power */
8153147Sxc151355 	in->in_chan = IEEE80211_CHAN_ANYC;
8163147Sxc151355 	in->in_inact_reload = IEEE80211_INACT_INIT;
8173147Sxc151355 	in->in_inact = in->in_inact_reload;
8183147Sxc151355 	ieee80211_crypto_resetkey(ic, &in->in_ucastkey, IEEE80211_KEYIX_NONE);
8193147Sxc151355 
8203147Sxc151355 	if (nt != NULL) {
8213147Sxc151355 		IEEE80211_NODE_LOCK(nt);
8223147Sxc151355 		list_insert_tail(&nt->nt_node, in);
8233147Sxc151355 		list_insert_tail(&nt->nt_hash[hash], in);
8243147Sxc151355 		in->in_table = nt;
8253147Sxc151355 		in->in_inact_reload = nt->nt_inact_init;
8263147Sxc151355 		IEEE80211_NODE_UNLOCK(nt);
8273147Sxc151355 	}
8283147Sxc151355 }
8293147Sxc151355 
8303147Sxc151355 /*
8313147Sxc151355  * Allocates and initialize a node with specified MAC address.
8323147Sxc151355  * Associate the node with the interface ic. If the allocation
8333147Sxc151355  * is successful, the node structure is initialized by
8343147Sxc151355  * ieee80211_setup_node(); otherwise, NULL is returned
8353147Sxc151355  */
8363147Sxc151355 ieee80211_node_t *
8373147Sxc151355 ieee80211_alloc_node(ieee80211com_t *ic, ieee80211_node_table_t *nt,
8383147Sxc151355     const uint8_t *macaddr)
8393147Sxc151355 {
8403147Sxc151355 	ieee80211_node_t *in;
8413147Sxc151355 
8423147Sxc151355 	in = ic->ic_node_alloc(ic);
8433147Sxc151355 	if (in != NULL)
8443147Sxc151355 		ieee80211_setup_node(ic, nt, in, macaddr);
8453147Sxc151355 	return (in);
8463147Sxc151355 }
8473147Sxc151355 
8483147Sxc151355 /*
8493147Sxc151355  * Craft a temporary node suitable for sending a management frame
8503147Sxc151355  * to the specified station.  We craft only as much state as we
8513147Sxc151355  * need to do the work since the node will be immediately reclaimed
8523147Sxc151355  * once the send completes.
8533147Sxc151355  */
8543147Sxc151355 ieee80211_node_t *
8553147Sxc151355 ieee80211_tmp_node(ieee80211com_t *ic, const uint8_t *macaddr)
8563147Sxc151355 {
8573147Sxc151355 	ieee80211_node_t *in;
8583147Sxc151355 
8593147Sxc151355 	in = ic->ic_node_alloc(ic);
8603147Sxc151355 	if (in != NULL) {
8613147Sxc151355 		ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_tmp_node: "
8623147Sxc151355 			"%p<%s>\n", in, ieee80211_macaddr_sprintf(macaddr));
8633147Sxc151355 
8643147Sxc151355 		IEEE80211_ADDR_COPY(in->in_macaddr, macaddr);
8653147Sxc151355 		IEEE80211_ADDR_COPY(in->in_bssid, ic->ic_bss->in_bssid);
8663147Sxc151355 		ieee80211_node_initref(in);		/* mark referenced */
8673147Sxc151355 		in->in_txpower = ic->ic_bss->in_txpower;
8683147Sxc151355 		/* NB: required by ieee80211_fix_rate */
8693147Sxc151355 		ieee80211_node_setchan(ic, in, ic->ic_bss->in_chan);
8703147Sxc151355 		ieee80211_crypto_resetkey(ic, &in->in_ucastkey,
8713147Sxc151355 			IEEE80211_KEYIX_NONE);
8723147Sxc151355 
8733147Sxc151355 		in->in_table = NULL;		/* NB: pedantic */
8743147Sxc151355 		in->in_ic = ic;
8753147Sxc151355 	}
8763147Sxc151355 
8773147Sxc151355 	return (in);
8783147Sxc151355 }
8793147Sxc151355 
8803147Sxc151355 /*
8813147Sxc151355  * ieee80211_dup_bss() is similar to ieee80211_alloc_node(),
8823147Sxc151355  * but is instead used to create a node database entry for
8833147Sxc151355  * the specified BSSID. If the allocation is successful, the
8843147Sxc151355  * node is initialized,  otherwise, NULL is returned.
8853147Sxc151355  */
8863147Sxc151355 ieee80211_node_t *
8873147Sxc151355 ieee80211_dup_bss(ieee80211_node_table_t *nt, const uint8_t *macaddr)
8883147Sxc151355 {
8893147Sxc151355 	ieee80211com_t *ic = nt->nt_ic;
8903147Sxc151355 	ieee80211_node_t *in;
8913147Sxc151355 
8923147Sxc151355 	in = ieee80211_alloc_node(ic, nt, macaddr);
8933147Sxc151355 	if (in != NULL) {
8943147Sxc151355 		/*
8953147Sxc151355 		 * Inherit from ic_bss.
8963147Sxc151355 		 */
8973147Sxc151355 		ieee80211_copy_bss(in, ic->ic_bss);
8983147Sxc151355 		IEEE80211_ADDR_COPY(in->in_bssid, ic->ic_bss->in_bssid);
8993147Sxc151355 		ieee80211_node_setchan(ic, in, ic->ic_bss->in_chan);
9003147Sxc151355 	}
9013147Sxc151355 
9023147Sxc151355 	return (in);
9033147Sxc151355 }
9043147Sxc151355 
9053147Sxc151355 /*
9063147Sxc151355  * Iterate through the node table, searching for a node entry which
9073147Sxc151355  * matches macaddr. If the entry is found, its reference count is
9083147Sxc151355  * incremented, and a pointer to the node is returned; otherwise,
9093147Sxc151355  * NULL will be returned.
9103147Sxc151355  * The node table lock is acquired by the caller.
9113147Sxc151355  */
9123147Sxc151355 static ieee80211_node_t *
9133147Sxc151355 ieee80211_find_node_locked(ieee80211_node_table_t *nt, const uint8_t *macaddr)
9143147Sxc151355 {
9153147Sxc151355 	ieee80211_node_t *in;
9163147Sxc151355 	int hash;
9173147Sxc151355 
9183147Sxc151355 	ASSERT(IEEE80211_NODE_IS_LOCKED(nt));
9193147Sxc151355 
9203147Sxc151355 	hash = ieee80211_node_hash(macaddr);
9213147Sxc151355 	in = list_head(&nt->nt_hash[hash]);
9223147Sxc151355 	while (in != NULL) {
9233147Sxc151355 		if (IEEE80211_ADDR_EQ(in->in_macaddr, macaddr))
9243147Sxc151355 			return (ieee80211_ref_node(in)); /* mark referenced */
9253147Sxc151355 		in = list_next(&nt->nt_hash[hash], in);
9263147Sxc151355 	}
9273147Sxc151355 	return (NULL);
9283147Sxc151355 }
9293147Sxc151355 
9303147Sxc151355 /*
9313147Sxc151355  * Iterate through the node table, searching for a node entry
9323147Sxc151355  * which match specified mac address.
9333147Sxc151355  * Return NULL if no matching node found.
9343147Sxc151355  */
9353147Sxc151355 ieee80211_node_t *
9363147Sxc151355 ieee80211_find_node(ieee80211_node_table_t *nt, const uint8_t *macaddr)
9373147Sxc151355 {
9383147Sxc151355 	ieee80211_node_t *in;
9393147Sxc151355 
9403147Sxc151355 	IEEE80211_NODE_LOCK(nt);
9413147Sxc151355 	in = ieee80211_find_node_locked(nt, macaddr);
9423147Sxc151355 	IEEE80211_NODE_UNLOCK(nt);
9433147Sxc151355 	return (in);
9443147Sxc151355 }
9453147Sxc151355 
9463147Sxc151355 /*
9473147Sxc151355  * Fake up a node; this handles node discovery in adhoc mode.
9483147Sxc151355  * Note that for the driver's benefit we treat this like an
9493147Sxc151355  * association so the driver has an opportunity to setup it's
9503147Sxc151355  * private state.
9513147Sxc151355  */
9523147Sxc151355 ieee80211_node_t *
9533147Sxc151355 ieee80211_fakeup_adhoc_node(ieee80211_node_table_t *nt, const uint8_t *macaddr)
9543147Sxc151355 {
9553147Sxc151355 	ieee80211com_t *ic = nt->nt_ic;
9563147Sxc151355 	ieee80211_node_t *in;
9573147Sxc151355 
9583147Sxc151355 	ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_fakeup_adhoc_node: "
9593147Sxc151355 		"mac<%s>\n", ieee80211_macaddr_sprintf(macaddr));
9603147Sxc151355 	in = ieee80211_dup_bss(nt, macaddr);
9613147Sxc151355 	if (in != NULL) {
9623147Sxc151355 		/* no rate negotiation; just dup */
9633147Sxc151355 		in->in_rates = ic->ic_bss->in_rates;
9643147Sxc151355 		if (ic->ic_node_newassoc != NULL)
9653147Sxc151355 			ic->ic_node_newassoc(in, 1);
9663147Sxc151355 		ieee80211_node_authorize(in);
9673147Sxc151355 	}
9683147Sxc151355 	return (in);
9693147Sxc151355 }
9703147Sxc151355 
9713147Sxc151355 /*
9723147Sxc151355  * Process a beacon or probe response frame.
9733147Sxc151355  */
9743147Sxc151355 void
9753147Sxc151355 ieee80211_add_scan(ieee80211com_t *ic, const struct ieee80211_scanparams *sp,
9763147Sxc151355     const struct ieee80211_frame *wh, int subtype, int rssi, int rstamp)
9773147Sxc151355 {
9783147Sxc151355 	ieee80211_node_table_t *nt = &ic->ic_scan;
9793147Sxc151355 	ieee80211_node_t *in;
9803147Sxc151355 	boolean_t newnode = B_FALSE;
9813147Sxc151355 
9823147Sxc151355 	in = ieee80211_find_node(nt, wh->i_addr2);
9833147Sxc151355 	if (in == NULL) {
9843147Sxc151355 		/*
9853147Sxc151355 		 * Create a new entry.
9863147Sxc151355 		 */
9873147Sxc151355 		in = ieee80211_alloc_node(ic, nt, wh->i_addr2);
9883147Sxc151355 		if (in == NULL) {
9893147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_ANY, "ieee80211_add_scan: "
9903147Sxc151355 				"alloc node failed\n");
9913147Sxc151355 			return;
9923147Sxc151355 		}
9933147Sxc151355 		/*
9943147Sxc151355 		 * inherit from ic_bss.
9953147Sxc151355 		 */
9963147Sxc151355 		ieee80211_copy_bss(in, ic->ic_bss);
9973147Sxc151355 		ieee80211_node_setchan(ic, in, ic->ic_curchan);
9983147Sxc151355 		newnode = B_TRUE;
9993147Sxc151355 	}
10003147Sxc151355 
10013147Sxc151355 	/* ap beaconing multiple ssid w/ same bssid */
10023147Sxc151355 
10033147Sxc151355 	/*
10043147Sxc151355 	 * sp->ssid[0] - element ID
10053147Sxc151355 	 * sp->ssid[1] - length
10063147Sxc151355 	 * sp->ssid[2]... - ssid
10073147Sxc151355 	 */
10083147Sxc151355 	if (sp->ssid[1] != 0 &&
10093147Sxc151355 	    subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP ||
10103147Sxc151355 	    in->in_esslen == 0) {
10113147Sxc151355 		in->in_esslen = sp->ssid[1];
10123147Sxc151355 		bzero(in->in_essid, sizeof (in->in_essid));
10133147Sxc151355 		bcopy(sp->ssid + 2, in->in_essid, sp->ssid[1]);
10143147Sxc151355 	}
10153147Sxc151355 	IEEE80211_ADDR_COPY(in->in_bssid, wh->i_addr3);
10163147Sxc151355 	in->in_rssi = (uint8_t)rssi;
10173147Sxc151355 	in->in_rstamp = rstamp;
10183147Sxc151355 	bcopy(sp->tstamp, in->in_tstamp.data, sizeof (in->in_tstamp));
10193147Sxc151355 	in->in_intval = sp->bintval;
10203147Sxc151355 	in->in_capinfo = sp->capinfo;
10213147Sxc151355 	in->in_chan = &ic->ic_sup_channels[sp->chan];
10223147Sxc151355 	in->in_phytype = sp->phytype;
10233147Sxc151355 	in->in_fhdwell = sp->fhdwell;
10243147Sxc151355 	in->in_fhindex = sp->fhindex;
10253147Sxc151355 	in->in_erp = sp->erp;
10263147Sxc151355 	if (sp->tim != NULL) {
10273147Sxc151355 		struct ieee80211_tim_ie *ie;
10283147Sxc151355 
10293147Sxc151355 		ie = (struct ieee80211_tim_ie *)sp->tim;
10303147Sxc151355 		in->in_dtim_count = ie->tim_count;
10313147Sxc151355 		in->in_dtim_period = ie->tim_period;
10323147Sxc151355 	}
10333147Sxc151355 	/*
10343147Sxc151355 	 * Record the byte offset from the mac header to
10353147Sxc151355 	 * the start of the TIM information element for
10363147Sxc151355 	 * use by hardware and/or to speedup software
10373147Sxc151355 	 * processing of beacon frames.
10383147Sxc151355 	 */
10393147Sxc151355 	in->in_tim_off = sp->timoff;
10403147Sxc151355 
10413147Sxc151355 	/* NB: must be after in_chan is setup */
10423147Sxc151355 	(void) ieee80211_setup_rates(in, sp->rates, sp->xrates,
10433147Sxc151355 		IEEE80211_F_DOSORT);
10443147Sxc151355 
10453147Sxc151355 	if (!newnode)
10463147Sxc151355 		ieee80211_free_node(in);
10473147Sxc151355 }
10483147Sxc151355 
10493147Sxc151355 /*
10503147Sxc151355  * Initialize/update an ad-hoc node with contents from a received
10513147Sxc151355  * beacon frame.
10523147Sxc151355  */
10533147Sxc151355 void
10543147Sxc151355 ieee80211_init_neighbor(ieee80211_node_t *in, const struct ieee80211_frame *wh,
10553147Sxc151355     const struct ieee80211_scanparams *sp)
10563147Sxc151355 {
10573147Sxc151355 	in->in_esslen = sp->ssid[1];
10583147Sxc151355 	(void) memcpy(in->in_essid, sp->ssid + 2, sp->ssid[1]);
10593147Sxc151355 	IEEE80211_ADDR_COPY(in->in_bssid, wh->i_addr3);
10603147Sxc151355 	(void) memcpy(in->in_tstamp.data, sp->tstamp, sizeof (in->in_tstamp));
10613147Sxc151355 	in->in_intval = sp->bintval;
10623147Sxc151355 	in->in_capinfo = sp->capinfo;
10633147Sxc151355 	in->in_chan = in->in_ic->ic_curchan;
10643147Sxc151355 	in->in_fhdwell = sp->fhdwell;
10653147Sxc151355 	in->in_fhindex = sp->fhindex;
10663147Sxc151355 	in->in_erp = sp->erp;
10673147Sxc151355 	in->in_tim_off = sp->timoff;
10683147Sxc151355 
10693147Sxc151355 	/* NB: must be after in_chan is setup */
10703147Sxc151355 	(void) ieee80211_setup_rates(in, sp->rates, sp->xrates,
10713147Sxc151355 		IEEE80211_F_DOSORT);
10723147Sxc151355 }
10733147Sxc151355 
10743147Sxc151355 /*
10753147Sxc151355  * Do node discovery in adhoc mode on receipt of a beacon
10763147Sxc151355  * or probe response frame.  Note that for the driver's
10773147Sxc151355  * benefit we we treat this like an association so the
10783147Sxc151355  * driver has an opportuinty to setup it's private state.
10793147Sxc151355  */
10803147Sxc151355 ieee80211_node_t *
10813147Sxc151355 ieee80211_add_neighbor(ieee80211com_t *ic, const struct ieee80211_frame *wh,
10823147Sxc151355     const struct ieee80211_scanparams *sp)
10833147Sxc151355 {
10843147Sxc151355 	ieee80211_node_t *in;
10853147Sxc151355 
10863147Sxc151355 	in = ieee80211_dup_bss(&ic->ic_sta, wh->i_addr2);
10873147Sxc151355 	if (in != NULL) {
10883147Sxc151355 		ieee80211_init_neighbor(in, wh, sp);
10893147Sxc151355 		if (ic->ic_node_newassoc != NULL)
10903147Sxc151355 			ic->ic_node_newassoc(in, 1);
10913147Sxc151355 	}
10923147Sxc151355 	return (in);
10933147Sxc151355 }
10943147Sxc151355 
10953147Sxc151355 #define	IEEE80211_IS_CTL(wh) \
10963147Sxc151355 	((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)
10973147Sxc151355 
10983147Sxc151355 /*
10993147Sxc151355  * Locate the node for sender, track state, and then pass the
11003147Sxc151355  * (referenced) node up to the 802.11 layer for its use.  We
11013147Sxc151355  * are required to pass some node so we fall back to ic_bss
11023147Sxc151355  * when this frame is from an unknown sender.  The 802.11 layer
11033147Sxc151355  * knows this means the sender wasn't in the node table and
11043147Sxc151355  * acts accordingly.
11053147Sxc151355  */
11063147Sxc151355 ieee80211_node_t *
11073147Sxc151355 ieee80211_find_rxnode(ieee80211com_t *ic, const struct ieee80211_frame *wh)
11083147Sxc151355 {
11093147Sxc151355 	ieee80211_node_table_t *nt;
11103147Sxc151355 	ieee80211_node_t *in;
11113147Sxc151355 
11123147Sxc151355 	/* may want scanned nodes in the neighbor table for adhoc */
11133147Sxc151355 	if (ic->ic_opmode == IEEE80211_M_STA ||
11143147Sxc151355 	    (ic->ic_flags & IEEE80211_F_SCAN)) {
11153147Sxc151355 		nt = &ic->ic_scan;
11163147Sxc151355 	} else {
11173147Sxc151355 		nt = &ic->ic_sta;
11183147Sxc151355 	}
11193147Sxc151355 
11203147Sxc151355 	IEEE80211_NODE_LOCK(nt);
11213147Sxc151355 	if (IEEE80211_IS_CTL(wh))
11223147Sxc151355 		in = ieee80211_find_node_locked(nt, wh->i_addr1);
11233147Sxc151355 	else
11243147Sxc151355 		in = ieee80211_find_node_locked(nt, wh->i_addr2);
11253147Sxc151355 	IEEE80211_NODE_UNLOCK(nt);
11263147Sxc151355 
11273147Sxc151355 	if (in == NULL)
11283147Sxc151355 		in = ieee80211_ref_node(ic->ic_bss);
11293147Sxc151355 
11303147Sxc151355 	return (in);
11313147Sxc151355 }
11323147Sxc151355 
11333147Sxc151355 /*
11343147Sxc151355  * Return a reference to the appropriate node for sending
11353147Sxc151355  * a data frame.  This handles node discovery in adhoc networks.
11363147Sxc151355  */
11373147Sxc151355 ieee80211_node_t *
11383147Sxc151355 ieee80211_find_txnode(ieee80211com_t *ic, const uint8_t *daddr)
11393147Sxc151355 {
11403147Sxc151355 	ieee80211_node_table_t *nt = &ic->ic_sta;
11413147Sxc151355 	ieee80211_node_t *in;
11423147Sxc151355 
11433147Sxc151355 	/*
11443147Sxc151355 	 * The destination address should be in the node table
11453147Sxc151355 	 * unless this is a multicast/broadcast frame.  We can
11463147Sxc151355 	 * also optimize station mode operation, all frames go
11473147Sxc151355 	 * to the bss node.
11483147Sxc151355 	 */
11493147Sxc151355 	IEEE80211_NODE_LOCK(nt);
11503147Sxc151355 	if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(daddr))
11513147Sxc151355 		in = ieee80211_ref_node(ic->ic_bss);
11523147Sxc151355 	else
11533147Sxc151355 		in = ieee80211_find_node_locked(nt, daddr);
11543147Sxc151355 	IEEE80211_NODE_UNLOCK(nt);
11553147Sxc151355 
11563147Sxc151355 	if (in == NULL) {
11573147Sxc151355 		if (ic->ic_opmode == IEEE80211_M_IBSS) {
11583147Sxc151355 			/*
11593147Sxc151355 			 * In adhoc mode cons up a node for the destination.
11603147Sxc151355 			 * Note that we need an additional reference for the
11613147Sxc151355 			 * caller to be consistent with
11623147Sxc151355 			 * ieee80211_find_node_locked
11633147Sxc151355 			 * can't hold lock across ieee80211_dup_bss 'cuz of
11643147Sxc151355 			 * recursive locking
11653147Sxc151355 			 */
11663147Sxc151355 			in = ieee80211_fakeup_adhoc_node(nt, daddr);
11673147Sxc151355 			if (in != NULL)
11683147Sxc151355 				(void) ieee80211_ref_node(in);
11693147Sxc151355 		} else {
11703147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_OUTPUT,
11713147Sxc151355 				"ieee80211_find_txnode: "
11723147Sxc151355 				"[%s] no node, discard frame\n",
11733147Sxc151355 				ieee80211_macaddr_sprintf(daddr));
11743147Sxc151355 		}
11753147Sxc151355 	}
11763147Sxc151355 	return (in);
11773147Sxc151355 }
11783147Sxc151355 
11793147Sxc151355 /*
11803147Sxc151355  * Remove a node from the node database entries and free memory
11813147Sxc151355  * associated with the node. The node table lock is acquired by
11823147Sxc151355  * the caller.
11833147Sxc151355  */
11843147Sxc151355 static void
11853147Sxc151355 ieee80211_free_node_locked(ieee80211_node_t *in)
11863147Sxc151355 {
11873147Sxc151355 	ieee80211com_t *ic = in->in_ic;
11883147Sxc151355 	ieee80211_node_table_t *nt = in->in_table;
11893147Sxc151355 	int32_t hash;
11903147Sxc151355 
11913147Sxc151355 	if (nt != NULL) {
11923147Sxc151355 		hash = ieee80211_node_hash(in->in_macaddr);
11933147Sxc151355 		list_remove(&nt->nt_hash[hash], in);
11943147Sxc151355 		list_remove(&nt->nt_node, in);
11953147Sxc151355 	}
11963147Sxc151355 	ic->ic_node_free(in);
11973147Sxc151355 }
11983147Sxc151355 
11993147Sxc151355 /*
12003147Sxc151355  * Remove a node from the node database entries and free any
12013147Sxc151355  * memory associated with the node.
12023147Sxc151355  * This method can be overridden in ieee80211_attach()
12033147Sxc151355  */
12043147Sxc151355 void
12053147Sxc151355 ieee80211_free_node(ieee80211_node_t *in)
12063147Sxc151355 {
12073147Sxc151355 	ieee80211_node_table_t *nt = in->in_table;
12083147Sxc151355 
12093147Sxc151355 	if (nt != NULL)
12103147Sxc151355 		IEEE80211_NODE_LOCK(nt);
12113147Sxc151355 	if (ieee80211_node_decref_nv(in) == 0)
12123147Sxc151355 		ieee80211_free_node_locked(in);
12133147Sxc151355 	if (nt != NULL)
12143147Sxc151355 		IEEE80211_NODE_UNLOCK(nt);
12153147Sxc151355 }
12163147Sxc151355 
12173147Sxc151355 /*
12183147Sxc151355  * Reclaim a node.  If this is the last reference count then
12193147Sxc151355  * do the normal free work.  Otherwise remove it from the node
12203147Sxc151355  * table and mark it gone by clearing the back-reference.
12213147Sxc151355  */
12223147Sxc151355 static void
12233147Sxc151355 ieee80211_node_reclaim(ieee80211_node_table_t *nt, ieee80211_node_t *in)
12243147Sxc151355 {
12253147Sxc151355 	int32_t hash;
12263147Sxc151355 
12273147Sxc151355 	IEEE80211_NODE_LOCK_ASSERT(nt);
12283147Sxc151355 	ieee80211_dbg(IEEE80211_MSG_NODE, "node_reclaim: "
12293147Sxc151355 		" remove %p<%s> from %s table, refcnt %d\n",
12303147Sxc151355 		in, ieee80211_macaddr_sprintf(in->in_macaddr), nt->nt_name,
12313147Sxc151355 		ieee80211_node_refcnt(in));
12323147Sxc151355 
12333147Sxc151355 	if (ieee80211_node_decref_nv(in) != 0) {
12343147Sxc151355 		/*
12353147Sxc151355 		 * Clear any entry in the unicast key mapping table.
12363147Sxc151355 		 * We need to do it here so rx lookups don't find it
12373147Sxc151355 		 * in the mapping table even if it's not in the hash
12383147Sxc151355 		 * table.  We cannot depend on the mapping table entry
12393147Sxc151355 		 * being cleared because the node may not be free'd.
12403147Sxc151355 		 */
12413147Sxc151355 		hash = ieee80211_node_hash(in->in_macaddr);
12423147Sxc151355 		list_remove(&nt->nt_hash[hash], in);
12433147Sxc151355 		list_remove(&nt->nt_node, in);
12443147Sxc151355 		in->in_table = NULL;
12453147Sxc151355 	} else {
12463147Sxc151355 		ieee80211_free_node_locked(in);
12473147Sxc151355 	}
12483147Sxc151355 }
12493147Sxc151355 
12503147Sxc151355 /*
12513147Sxc151355  * Iterate through the node list and reclaim all node in the node table.
12523147Sxc151355  * The node table lock is acquired by the caller
12533147Sxc151355  */
12543147Sxc151355 static void
12553147Sxc151355 ieee80211_free_allnodes_locked(ieee80211_node_table_t *nt)
12563147Sxc151355 {
12573147Sxc151355 	ieee80211_node_t *in;
12583147Sxc151355 
12593147Sxc151355 	ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_free_allnodes_locked(): "
12603147Sxc151355 		"free all nodes in %s table\n", nt->nt_name);
12613147Sxc151355 
12623147Sxc151355 	in = list_head(&nt->nt_node);
12633147Sxc151355 	while (in != NULL) {
12643147Sxc151355 		ieee80211_node_reclaim(nt, in);
12653147Sxc151355 		in = list_head(&nt->nt_node);
12663147Sxc151355 	}
12673147Sxc151355 	ieee80211_reset_erp(nt->nt_ic);
12683147Sxc151355 }
12693147Sxc151355 
12703147Sxc151355 /*
12713147Sxc151355  * Iterate through the node list, calling ieee80211_node_reclaim() for
12723147Sxc151355  * all nodes associated with the interface.
12733147Sxc151355  */
12743147Sxc151355 static void
12753147Sxc151355 ieee80211_free_allnodes(ieee80211_node_table_t *nt)
12763147Sxc151355 {
12773147Sxc151355 	IEEE80211_NODE_LOCK(nt);
12783147Sxc151355 	ieee80211_free_allnodes_locked(nt);
12793147Sxc151355 	IEEE80211_NODE_UNLOCK(nt);
12803147Sxc151355 }
12813147Sxc151355 
12823147Sxc151355 /*
12833147Sxc151355  * Timeout entries in the scan cache. This is the timeout callback
12843147Sxc151355  * function of node table ic_scan which is called when the inactivity
12853147Sxc151355  * timer expires.
12863147Sxc151355  */
12873147Sxc151355 static void
12883147Sxc151355 ieee80211_timeout_scan_candidates(ieee80211_node_table_t *nt)
12893147Sxc151355 {
12903147Sxc151355 	ieee80211com_t *ic = nt->nt_ic;
12913147Sxc151355 	ieee80211_node_t *in;
12923147Sxc151355 
12933147Sxc151355 	IEEE80211_NODE_LOCK(nt);
12943147Sxc151355 	in = ic->ic_bss;
12953147Sxc151355 	node_cleanfrag(in);	/* Free fragment if not needed */
12963147Sxc151355 	nt->nt_inact_timer = IEEE80211_INACT_WAIT;
12973147Sxc151355 	IEEE80211_NODE_UNLOCK(nt);
12983147Sxc151355 }
12993147Sxc151355 
13003147Sxc151355 /*
13013147Sxc151355  * Timeout inactive stations and do related housekeeping.
13023147Sxc151355  * Note that we cannot hold the node lock while sending a
13033147Sxc151355  * frame as this would lead to a LOR.  Instead we use a
13043147Sxc151355  * generation number to mark nodes that we've scanned and
13053147Sxc151355  * drop the lock and restart a scan if we have to time out
13063147Sxc151355  * a node.  Since we are single-threaded by virtue of
13073147Sxc151355  * controlling the inactivity timer we can be sure this will
13083147Sxc151355  * process each node only once.
13093147Sxc151355  */
13103147Sxc151355 static void
13113147Sxc151355 ieee80211_timeout_stations(ieee80211_node_table_t *nt)
13123147Sxc151355 {
13133147Sxc151355 	ieee80211com_t *ic = nt->nt_ic;
13143147Sxc151355 	ieee80211_impl_t *im = ic->ic_private;
13153147Sxc151355 	ieee80211_node_t *in = NULL;
13163147Sxc151355 	uint32_t gen;
13173147Sxc151355 	boolean_t isadhoc;
13183147Sxc151355 
13193147Sxc151355 	IEEE80211_LOCK_ASSERT(ic);
13203147Sxc151355 	isadhoc = (ic->ic_opmode == IEEE80211_M_IBSS ||
13213147Sxc151355 		ic->ic_opmode == IEEE80211_M_AHDEMO);
13223147Sxc151355 	IEEE80211_SCAN_LOCK(nt);
13233147Sxc151355 	gen = ++nt->nt_scangen;
13243147Sxc151355 restart:
13253147Sxc151355 	IEEE80211_NODE_LOCK(nt);
13263147Sxc151355 	for (in = list_head(&nt->nt_node); in != NULL;
13273147Sxc151355 		in = list_next(&nt->nt_node, in)) {
13283147Sxc151355 		if (in->in_scangen == gen)	/* previously handled */
13293147Sxc151355 			continue;
13303147Sxc151355 		in->in_scangen = gen;
13313147Sxc151355 		node_cleanfrag(in);	/* free fragment if not needed */
13323147Sxc151355 
13333147Sxc151355 		/*
13343147Sxc151355 		 * Special case ourself; we may be idle for extended periods
13353147Sxc151355 		 * of time and regardless reclaiming our state is wrong.
13363147Sxc151355 		 */
13373147Sxc151355 		if (in == ic->ic_bss)
13383147Sxc151355 			continue;
13393147Sxc151355 		in->in_inact--;
13403147Sxc151355 		if (in->in_associd != 0 || isadhoc) {
13413147Sxc151355 			/*
13423147Sxc151355 			 * Probe the station before time it out.  We
13433147Sxc151355 			 * send a null data frame which may not be
13443147Sxc151355 			 * uinversally supported by drivers (need it
13453147Sxc151355 			 * for ps-poll support so it should be...).
13463147Sxc151355 			 */
13473147Sxc151355 			if (0 < in->in_inact &&
13483147Sxc151355 			    in->in_inact <= im->im_inact_probe) {
13493147Sxc151355 				ieee80211_dbg(IEEE80211_MSG_NODE, "net80211: "
13503147Sxc151355 					"probe station due to inactivity\n");
13513147Sxc151355 				IEEE80211_NODE_UNLOCK(nt);
13523147Sxc151355 				IEEE80211_UNLOCK(ic);
13533147Sxc151355 				(void) ieee80211_send_nulldata(in);
13543147Sxc151355 				IEEE80211_LOCK(ic);
13553147Sxc151355 				goto restart;
13563147Sxc151355 			}
13573147Sxc151355 		}
13583147Sxc151355 		if (in->in_inact <= 0) {
13593147Sxc151355 			ieee80211_dbg(IEEE80211_MSG_NODE, "net80211: "
13603147Sxc151355 				"station timed out due to inact (refcnt %u)\n",
13613147Sxc151355 				ieee80211_node_refcnt(in));
13623147Sxc151355 			/*
13633147Sxc151355 			 * Send a deauthenticate frame and drop the station.
13643147Sxc151355 			 * This is somewhat complicated due to reference counts
13653147Sxc151355 			 * and locking.  At this point a station will typically
13663147Sxc151355 			 * have a reference count of 1.  ieee80211_node_leave
13673147Sxc151355 			 * will do a "free" of the node which will drop the
13683147Sxc151355 			 * reference count.  But in the meantime a reference
13693147Sxc151355 			 * wil be held by the deauth frame.  The actual reclaim
13703147Sxc151355 			 * of the node will happen either after the tx is
13713147Sxc151355 			 * completed or by ieee80211_node_leave.
13723147Sxc151355 			 *
13733147Sxc151355 			 * Separately we must drop the node lock before sending
13743147Sxc151355 			 * in case the driver takes a lock, as this will result
13753147Sxc151355 			 * in  LOR between the node lock and the driver lock.
13763147Sxc151355 			 */
13773147Sxc151355 			IEEE80211_NODE_UNLOCK(nt);
13783147Sxc151355 			if (in->in_associd != 0) {
13793147Sxc151355 				IEEE80211_UNLOCK(ic);
13803147Sxc151355 				IEEE80211_SEND_MGMT(ic, in,
13813147Sxc151355 					IEEE80211_FC0_SUBTYPE_DEAUTH,
13823147Sxc151355 					IEEE80211_REASON_AUTH_EXPIRE);
13833147Sxc151355 				IEEE80211_LOCK(ic);
13843147Sxc151355 			}
13853147Sxc151355 			ieee80211_node_leave(ic, in);
13863147Sxc151355 			goto restart;
13873147Sxc151355 		}
13883147Sxc151355 	}
13893147Sxc151355 	IEEE80211_NODE_UNLOCK(nt);
13903147Sxc151355 
13913147Sxc151355 	IEEE80211_SCAN_UNLOCK(nt);
13923147Sxc151355 
13933147Sxc151355 	nt->nt_inact_timer = IEEE80211_INACT_WAIT;
13943147Sxc151355 }
13953147Sxc151355 
13963147Sxc151355 /*
13973147Sxc151355  * Call the user-defined call back function for all nodes in
13983147Sxc151355  * the node cache. The callback is invoked with the user-supplied
13993147Sxc151355  * value and a pointer to the current node.
14003147Sxc151355  */
14013147Sxc151355 void
14023147Sxc151355 ieee80211_iterate_nodes(ieee80211_node_table_t *nt, ieee80211_iter_func *f,
14033147Sxc151355     void *arg)
14043147Sxc151355 {
14053147Sxc151355 	ieee80211_node_t *in;
14063147Sxc151355 
14073147Sxc151355 	IEEE80211_NODE_LOCK(nt);
14083147Sxc151355 	in = list_head(&nt->nt_node);
14093147Sxc151355 	while (in != NULL) {
14103147Sxc151355 		(void) ieee80211_ref_node(in);
14113147Sxc151355 		IEEE80211_NODE_UNLOCK(nt);
14123147Sxc151355 		(*f)(arg, in);
14133147Sxc151355 		ieee80211_free_node(in);
14143147Sxc151355 		IEEE80211_NODE_LOCK(nt);
14153147Sxc151355 		in = list_next(&nt->nt_node, in);
14163147Sxc151355 	}
14173147Sxc151355 	IEEE80211_NODE_UNLOCK(nt);
14183147Sxc151355 }
14193147Sxc151355 
14203147Sxc151355 /*
14213147Sxc151355  * Handle bookkeeping for station deauthentication/disassociation
14223147Sxc151355  * when operating as an ap.
14233147Sxc151355  */
14243147Sxc151355 static void
14253147Sxc151355 ieee80211_node_leave(ieee80211com_t *ic, ieee80211_node_t *in)
14263147Sxc151355 {
14273147Sxc151355 	ieee80211_node_table_t *nt = in->in_table;
14283147Sxc151355 
14293147Sxc151355 	ASSERT(ic->ic_opmode == IEEE80211_M_IBSS);
14303147Sxc151355 
14313147Sxc151355 	/*
14323147Sxc151355 	 * Remove the node from any table it's recorded in and
14333147Sxc151355 	 * drop the caller's reference.  Removal from the table
14343147Sxc151355 	 * is important to insure the node is not reprocessed
14353147Sxc151355 	 * for inactivity.
14363147Sxc151355 	 */
14373147Sxc151355 	if (nt != NULL) {
14383147Sxc151355 		IEEE80211_NODE_LOCK(nt);
14393147Sxc151355 		ieee80211_node_reclaim(nt, in);
14403147Sxc151355 		IEEE80211_NODE_UNLOCK(nt);
14413147Sxc151355 	} else {
14423147Sxc151355 		ieee80211_free_node(in);
14433147Sxc151355 	}
14443147Sxc151355 }
14453147Sxc151355 
14463147Sxc151355 /*
14473147Sxc151355  * Initialize a node table with specified name, inactivity timer value
14483147Sxc151355  * and callback inactivity timeout function. Associate the node table
14493147Sxc151355  * with interface softc, ic.
14503147Sxc151355  */
14513147Sxc151355 static void
14523147Sxc151355 ieee80211_node_table_init(ieee80211com_t *ic, ieee80211_node_table_t *nt,
14533147Sxc151355     const char *name, int inact, int keyixmax,
14543147Sxc151355     void (*timeout)(ieee80211_node_table_t *))
14553147Sxc151355 {
14563147Sxc151355 	int i;
14573147Sxc151355 
14583147Sxc151355 	ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_node_table_init():"
14593147Sxc151355 		"%s table, inact %d\n", name, inact);
14603147Sxc151355 
14613147Sxc151355 	nt->nt_ic = ic;
14623147Sxc151355 	nt->nt_name = name;
14633147Sxc151355 	nt->nt_inact_timer = 0;
14643147Sxc151355 	nt->nt_inact_init = inact;
14653147Sxc151355 	nt->nt_timeout = timeout;
14663147Sxc151355 	nt->nt_keyixmax = keyixmax;
14673147Sxc151355 	nt->nt_scangen = 1;
14683147Sxc151355 	mutex_init(&nt->nt_scanlock, NULL, MUTEX_DRIVER, NULL);
14693147Sxc151355 	mutex_init(&nt->nt_nodelock, NULL, MUTEX_DRIVER, NULL);
14703147Sxc151355 
14713147Sxc151355 	list_create(&nt->nt_node, sizeof (ieee80211_node_t),
14723147Sxc151355 		offsetof(ieee80211_node_t, in_node));
14733147Sxc151355 	for (i = 0; i < IEEE80211_NODE_HASHSIZE; i++) {
14743147Sxc151355 		list_create(&nt->nt_hash[i], sizeof (ieee80211_node_t),
14753147Sxc151355 			offsetof(ieee80211_node_t, in_hash));
14763147Sxc151355 	}
14773147Sxc151355 }
14783147Sxc151355 
14793147Sxc151355 /*
14803147Sxc151355  * Reset a node table. Clean its inactivity timer and call
14813147Sxc151355  * ieee80211_free_allnodes_locked() to free all nodes in the
14823147Sxc151355  * node table.
14833147Sxc151355  */
14843147Sxc151355 void
14853147Sxc151355 ieee80211_node_table_reset(ieee80211_node_table_t *nt)
14863147Sxc151355 {
14873147Sxc151355 	ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_node_table_reset(): "
14883147Sxc151355 		"%s table\n", nt->nt_name);
14893147Sxc151355 
14903147Sxc151355 	IEEE80211_NODE_LOCK(nt);
14913147Sxc151355 	nt->nt_inact_timer = 0;
14923147Sxc151355 	ieee80211_free_allnodes_locked(nt);
14933147Sxc151355 	IEEE80211_NODE_UNLOCK(nt);
14943147Sxc151355 }
14953147Sxc151355 
14963147Sxc151355 /*
14973147Sxc151355  * Destroy a node table. Free all nodes in the node table.
14983147Sxc151355  * This function is usually called by node detach function.
14993147Sxc151355  */
15003147Sxc151355 static void
15013147Sxc151355 ieee80211_node_table_cleanup(ieee80211_node_table_t *nt)
15023147Sxc151355 {
15033147Sxc151355 	ieee80211_dbg(IEEE80211_MSG_NODE, "ieee80211_node_table_cleanup(): "
15043147Sxc151355 	    "%s table\n", nt->nt_name);
15053147Sxc151355 
15063147Sxc151355 	IEEE80211_NODE_LOCK(nt);
15073147Sxc151355 	ieee80211_free_allnodes_locked(nt);
15083147Sxc151355 	IEEE80211_NODE_UNLOCK(nt);
15093147Sxc151355 	mutex_destroy(&nt->nt_nodelock);
15103147Sxc151355 	mutex_destroy(&nt->nt_scanlock);
15113147Sxc151355 }
1512