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