ieee80211_node.c revision 121816
11592Srgrimes/*-
22286Sjkh * Copyright (c) 2001 Atsushi Onoe
32286Sjkh * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
41592Srgrimes * All rights reserved.
51592Srgrimes *
61592Srgrimes * Redistribution and use in source and binary forms, with or without
71592Srgrimes * modification, are permitted provided that the following conditions
81592Srgrimes * are met:
91592Srgrimes * 1. Redistributions of source code must retain the above copyright
101592Srgrimes *    notice, this list of conditions and the following disclaimer.
111592Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
121592Srgrimes *    notice, this list of conditions and the following disclaimer in the
131592Srgrimes *    documentation and/or other materials provided with the distribution.
141592Srgrimes * 3. The name of the author may not be used to endorse or promote products
151592Srgrimes *    derived from this software without specific prior written permission.
161592Srgrimes *
171592Srgrimes * Alternatively, this software may be distributed under the terms of the
181592Srgrimes * GNU General Public License ("GPL") version 2 as published by the Free
191592Srgrimes * Software Foundation.
201592Srgrimes *
211592Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
221592Srgrimes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
231592Srgrimes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
241592Srgrimes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
251592Srgrimes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
261592Srgrimes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
271592Srgrimes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
281592Srgrimes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
291592Srgrimes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
301592Srgrimes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
311592Srgrimes */
322286Sjkh
332286Sjkh#include <sys/cdefs.h>
342286Sjkh__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_node.c 121816 2003-10-31 18:32:15Z brooks $");
352286Sjkh
362286Sjkh#include "opt_inet.h"
372286Sjkh
382286Sjkh#include <sys/param.h>
392286Sjkh#include <sys/systm.h>
401592Srgrimes#include <sys/mbuf.h>
411592Srgrimes#include <sys/malloc.h>
421592Srgrimes#include <sys/kernel.h>
432286Sjkh#include <sys/socket.h>
441592Srgrimes#include <sys/sockio.h>
451592Srgrimes#include <sys/endian.h>
461592Srgrimes#include <sys/errno.h>
471592Srgrimes#include <sys/bus.h>
481592Srgrimes#include <sys/proc.h>
497317Sache#include <sys/sysctl.h>
501592Srgrimes
511592Srgrimes#include <machine/atomic.h>
522286Sjkh
531592Srgrimes#include <net/if.h>
541592Srgrimes#include <net/if_dl.h>
551592Srgrimes#include <net/if_media.h>
561592Srgrimes#include <net/if_arp.h>
571592Srgrimes#include <net/ethernet.h>
581592Srgrimes#include <net/if_llc.h>
591592Srgrimes
601592Srgrimes#include <net80211/ieee80211_var.h>
611592Srgrimes
621592Srgrimes#include <net/bpf.h>
632286Sjkh
642286Sjkh#ifdef INET
651592Srgrimes#include <netinet/in.h>
661592Srgrimes#include <netinet/if_ether.h>
671592Srgrimes#endif
681592Srgrimes
692286Sjkhstatic struct ieee80211_node *ieee80211_node_alloc(struct ieee80211com *);
701592Srgrimesstatic void ieee80211_node_free(struct ieee80211com *, struct ieee80211_node *);
712286Sjkhstatic void ieee80211_node_copy(struct ieee80211com *,
722286Sjkh		struct ieee80211_node *, const struct ieee80211_node *);
731592Srgrimesstatic u_int8_t ieee80211_node_getrssi(struct ieee80211com *,
741592Srgrimes		struct ieee80211_node *);
752286Sjkh
762286Sjkhstatic void ieee80211_setup_node(struct ieee80211com *ic,
771592Srgrimes		struct ieee80211_node *ni, u_int8_t *macaddr);
782286Sjkhstatic void _ieee80211_free_node(struct ieee80211com *,
792286Sjkh		struct ieee80211_node *);
801592Srgrimes
811592SrgrimesMALLOC_DEFINE(M_80211_NODE, "node", "802.11 node state");
821592Srgrimes
831592Srgrimesvoid
841592Srgrimesieee80211_node_attach(struct ifnet *ifp)
851592Srgrimes{
861592Srgrimes	struct ieee80211com *ic = (void *)ifp;
872286Sjkh
882286Sjkh	/* XXX need unit */
891592Srgrimes	IEEE80211_NODE_LOCK_INIT(ic, ifp->if_xname);
901592Srgrimes	TAILQ_INIT(&ic->ic_node);
911592Srgrimes	ic->ic_node_alloc = ieee80211_node_alloc;
922286Sjkh	ic->ic_node_free = ieee80211_node_free;
931592Srgrimes	ic->ic_node_copy = ieee80211_node_copy;
941592Srgrimes	ic->ic_node_getrssi = ieee80211_node_getrssi;
951592Srgrimes	ic->ic_scangen = 1;
961592Srgrimes}
971592Srgrimes
981592Srgrimesvoid
991592Srgrimesieee80211_node_lateattach(struct ifnet *ifp)
1001592Srgrimes{
1011592Srgrimes	struct ieee80211com *ic = (void *)ifp;
1021592Srgrimes
1031592Srgrimes	ic->ic_bss = (*ic->ic_node_alloc)(ic);
1041592Srgrimes	KASSERT(ic->ic_bss != NULL, ("unable to setup inital BSS node"));
1051592Srgrimes	ic->ic_bss->ni_chan = IEEE80211_CHAN_ANYC;
1061592Srgrimes}
1071592Srgrimes
1081592Srgrimesvoid
1091592Srgrimesieee80211_node_detach(struct ifnet *ifp)
1101592Srgrimes{
1111592Srgrimes	struct ieee80211com *ic = (void *)ifp;
1121592Srgrimes
1131592Srgrimes	if (ic->ic_bss != NULL)
1141592Srgrimes		(*ic->ic_node_free)(ic, ic->ic_bss);
1151592Srgrimes	ieee80211_free_allnodes(ic);
1161592Srgrimes	IEEE80211_NODE_LOCK_DESTROY(ic);
1171592Srgrimes}
1181592Srgrimes
1191592Srgrimes/*
1201592Srgrimes * AP scanning support.
1211592Srgrimes */
1221592Srgrimes
1231592Srgrimes/*
1241592Srgrimes * Initialize the active channel set based on the set
1251592Srgrimes * of available channels and the current PHY mode.
1261592Srgrimes */
1271592Srgrimesstatic void
1281592Srgrimesieee80211_reset_scan(struct ifnet *ifp)
1291592Srgrimes{
1301592Srgrimes	struct ieee80211com *ic = (void *)ifp;
1311592Srgrimes
1321592Srgrimes	memcpy(ic->ic_chan_scan, ic->ic_chan_active,
1331592Srgrimes		sizeof(ic->ic_chan_active));
1341592Srgrimes	/* NB: hack, setup so next_scan starts with the first channel */
1351592Srgrimes	if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC)
1361592Srgrimes		ic->ic_bss->ni_chan = &ic->ic_channels[IEEE80211_CHAN_MAX];
1371592Srgrimes}
1381592Srgrimes
1391592Srgrimes/*
1401592Srgrimes * Begin an active scan.
1411592Srgrimes */
1421592Srgrimesvoid
1431592Srgrimesieee80211_begin_scan(struct ifnet *ifp)
1441592Srgrimes{
1451592Srgrimes	struct ieee80211com *ic = (void *)ifp;
1461592Srgrimes
1471592Srgrimes	/*
1481592Srgrimes	 * In all but hostap mode scanning starts off in
1491592Srgrimes	 * an active mode before switching to passive.
1501592Srgrimes	 */
1511592Srgrimes	if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
1521592Srgrimes		ic->ic_flags |= IEEE80211_F_ASCAN;
1531592Srgrimes		ic->ic_stats.is_scan_active++;
1541592Srgrimes	} else
1551592Srgrimes		ic->ic_stats.is_scan_passive++;
1561592Srgrimes	if (ifp->if_flags & IFF_DEBUG)
1571592Srgrimes		if_printf(ifp, "begin %s scan\n",
1581592Srgrimes			(ic->ic_flags & IEEE80211_F_ASCAN) ?
1591592Srgrimes				"active" : "passive");
1601592Srgrimes	/*
1611592Srgrimes	 * Clear scan state and flush any previously seen
1621592Srgrimes	 * AP's.  Note that the latter assumes we don't act
1631592Srgrimes	 * as both an AP and a station, otherwise we'll
1641592Srgrimes	 * potentially flush state of stations associated
1651592Srgrimes	 * with us.
1661592Srgrimes	 */
1671592Srgrimes	ieee80211_reset_scan(ifp);
1681592Srgrimes	ieee80211_free_allnodes(ic);
1691592Srgrimes
1701592Srgrimes	/* Scan the next channel. */
1711592Srgrimes	ieee80211_next_scan(ifp);
1721592Srgrimes}
1731592Srgrimes
1741592Srgrimes/*
1751592Srgrimes * Switch to the next channel marked for scanning.
1761592Srgrimes */
1771592Srgrimesvoid
1781592Srgrimesieee80211_next_scan(struct ifnet *ifp)
1791592Srgrimes{
1801592Srgrimes	struct ieee80211com *ic = (void *)ifp;
1811592Srgrimes	struct ieee80211_channel *chan;
1821592Srgrimes
1831592Srgrimes	chan = ic->ic_bss->ni_chan;
1841592Srgrimes	for (;;) {
1851592Srgrimes		if (++chan > &ic->ic_channels[IEEE80211_CHAN_MAX])
1861592Srgrimes			chan = &ic->ic_channels[0];
1871592Srgrimes		if (isset(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan))) {
1881592Srgrimes			/*
1891592Srgrimes			 * Honor channels marked passive-only
1902286Sjkh			 * during an active scan.
1912286Sjkh			 */
1921592Srgrimes			if ((ic->ic_flags & IEEE80211_F_ASCAN) == 0 ||
1931592Srgrimes			    (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0)
1941592Srgrimes				break;
1951592Srgrimes		}
1961592Srgrimes		if (chan == ic->ic_bss->ni_chan) {
1971592Srgrimes			ieee80211_end_scan(ifp);
1981592Srgrimes			return;
1991592Srgrimes		}
2001592Srgrimes	}
2011592Srgrimes	clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan));
2021592Srgrimes	IEEE80211_DPRINTF(("ieee80211_next_scan: chan %d->%d\n",
2031592Srgrimes	    ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan),
2041592Srgrimes	    ieee80211_chan2ieee(ic, chan)));
2051592Srgrimes	ic->ic_bss->ni_chan = chan;
2061592Srgrimes	ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
2071592Srgrimes}
2081592Srgrimes
2091592Srgrimesvoid
2101592Srgrimesieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan)
2111592Srgrimes{
2121592Srgrimes	struct ieee80211_node *ni;
2131592Srgrimes	struct ifnet *ifp = &ic->ic_if;
2141592Srgrimes
2151592Srgrimes	ni = ic->ic_bss;
2161592Srgrimes	if (ifp->if_flags & IFF_DEBUG)
2171592Srgrimes		if_printf(ifp, "creating ibss\n");
2181592Srgrimes	ic->ic_flags |= IEEE80211_F_SIBSS;
2191592Srgrimes	ni->ni_chan = chan;
2201592Srgrimes	ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)];
2211592Srgrimes	IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr);
2221592Srgrimes	IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr);
2231592Srgrimes	if (ic->ic_opmode == IEEE80211_M_IBSS)
2241592Srgrimes		ni->ni_bssid[0] |= 0x02;	/* local bit for IBSS */
2251592Srgrimes	ni->ni_esslen = ic->ic_des_esslen;
2261592Srgrimes	memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen);
2271592Srgrimes	ni->ni_rssi = 0;
2281592Srgrimes	ni->ni_rstamp = 0;
2291592Srgrimes	memset(ni->ni_tstamp, 0, sizeof(ni->ni_tstamp));
2301592Srgrimes	ni->ni_intval = ic->ic_lintval;
2311592Srgrimes	ni->ni_capinfo = IEEE80211_CAPINFO_IBSS;
2321592Srgrimes	if (ic->ic_flags & IEEE80211_F_WEPON)
2331592Srgrimes		ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY;
2341592Srgrimes	if (ic->ic_phytype == IEEE80211_T_FH) {
2351592Srgrimes		ni->ni_fhdwell = 200;	/* XXX */
2361592Srgrimes		ni->ni_fhindex = 1;
2371592Srgrimes	}
2381592Srgrimes	ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
2391592Srgrimes}
2401592Srgrimes
2411592Srgrimes/*
2421592Srgrimes * Complete a scan of potential channels.
2431592Srgrimes */
2441592Srgrimesvoid
2451592Srgrimesieee80211_end_scan(struct ifnet *ifp)
2461592Srgrimes{
2471592Srgrimes	struct ieee80211com *ic = (void *)ifp;
2481592Srgrimes	struct ieee80211_node *ni, *nextbs, *selbs;
2492286Sjkh	u_int8_t rate;
2502286Sjkh	int i, fail;
2512286Sjkh
2522286Sjkh	ic->ic_flags &= ~IEEE80211_F_ASCAN;
2532286Sjkh	ni = TAILQ_FIRST(&ic->ic_node);
2542286Sjkh
2551592Srgrimes	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
2561592Srgrimes		/* XXX off stack? */
2571592Srgrimes		u_char occupied[roundup(IEEE80211_CHAN_MAX, NBBY)];
2582286Sjkh		/*
2592286Sjkh		 * The passive scan to look for existing AP's completed,
2602286Sjkh		 * select a channel to camp on.  Identify the channels
2612286Sjkh		 * that already have one or more AP's and try to locate
2622286Sjkh		 * an unnoccupied one.  If that fails, pick a random
2631592Srgrimes		 * channel from the active set.
2641592Srgrimes		 */
2651592Srgrimes		for (; ni != NULL; ni = nextbs) {
2662286Sjkh			ieee80211_ref_node(ni);
2672286Sjkh			nextbs = TAILQ_NEXT(ni, ni_list);
2681592Srgrimes			setbit(occupied, ieee80211_chan2ieee(ic, ni->ni_chan));
2691592Srgrimes			ieee80211_free_node(ic, ni);
2701592Srgrimes		}
2712286Sjkh		for (i = 0; i < IEEE80211_CHAN_MAX; i++)
2722286Sjkh			if (isset(ic->ic_chan_active, i) && isclr(occupied, i))
2732286Sjkh				break;
2741592Srgrimes		if (i == IEEE80211_CHAN_MAX) {
2751592Srgrimes			fail = arc4random() & 3;	/* random 0-3 */
2761592Srgrimes			for (i = 0; i < IEEE80211_CHAN_MAX; i++)
2772286Sjkh				if (isset(ic->ic_chan_active, i) && fail-- == 0)
2782286Sjkh					break;
2792286Sjkh		}
2802286Sjkh		ieee80211_create_ibss(ic, &ic->ic_channels[i]);
2812286Sjkh		return;
2821592Srgrimes	}
2831592Srgrimes	if (ni == NULL) {
2841592Srgrimes		IEEE80211_DPRINTF(("%s: no scan candidate\n", __func__));
2851592Srgrimes  notfound:
2862286Sjkh		if (ic->ic_opmode == IEEE80211_M_IBSS &&
2871592Srgrimes		    (ic->ic_flags & IEEE80211_F_IBSSON) &&
2881592Srgrimes		    ic->ic_des_esslen != 0) {
2891592Srgrimes			ieee80211_create_ibss(ic, ic->ic_ibss_chan);
2901592Srgrimes			return;
2911592Srgrimes		}
2921592Srgrimes		/*
2931592Srgrimes		 * Reset the list of channels to scan and start again.
2941592Srgrimes		 */
2951592Srgrimes		ieee80211_reset_scan(ifp);
2961592Srgrimes		ieee80211_next_scan(ifp);
2971592Srgrimes		return;
2981592Srgrimes	}
2991592Srgrimes	selbs = NULL;
3001592Srgrimes	if (ifp->if_flags & IFF_DEBUG)
3011592Srgrimes		if_printf(ifp, "\tmacaddr          bssid         chan  rssi rate flag  wep  essid\n");
3021592Srgrimes	for (; ni != NULL; ni = nextbs) {
3031592Srgrimes		ieee80211_ref_node(ni);
3041592Srgrimes		nextbs = TAILQ_NEXT(ni, ni_list);
3051592Srgrimes		if (ni->ni_fails) {
3061592Srgrimes			/*
3077317Sache			 * The configuration of the access points may change
3081592Srgrimes			 * during my scan.  So delete the entry for the AP
3091592Srgrimes			 * and retry to associate if there is another beacon.
3101592Srgrimes			 */
3111592Srgrimes			if (ni->ni_fails++ > 2)
3121592Srgrimes				ieee80211_free_node(ic, ni);
3131592Srgrimes			continue;
3141592Srgrimes		}
3151592Srgrimes		fail = 0;
3161592Srgrimes		if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan)))
3171592Srgrimes			fail |= 0x01;
3181592Srgrimes		if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
3191592Srgrimes		    ni->ni_chan != ic->ic_des_chan)
3201592Srgrimes			fail |= 0x01;
3211592Srgrimes		if (ic->ic_opmode == IEEE80211_M_IBSS) {
3221592Srgrimes			if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
3231592Srgrimes				fail |= 0x02;
3241592Srgrimes		} else {
3251592Srgrimes			if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0)
3261592Srgrimes				fail |= 0x02;
3271592Srgrimes		}
3281592Srgrimes		if (ic->ic_flags & IEEE80211_F_WEPON) {
3291592Srgrimes			if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
3301592Srgrimes				fail |= 0x04;
3311592Srgrimes		} else {
3321592Srgrimes			if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY)
3331592Srgrimes				fail |= 0x04;
3341592Srgrimes		}
3351592Srgrimes		rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO);
3361592Srgrimes		if (rate & IEEE80211_RATE_BASIC)
3371592Srgrimes			fail |= 0x08;
3381592Srgrimes		if (ic->ic_des_esslen != 0 &&
3391592Srgrimes		    (ni->ni_esslen != ic->ic_des_esslen ||
3401592Srgrimes		     memcmp(ni->ni_essid, ic->ic_des_essid,
3411592Srgrimes		     ic->ic_des_esslen != 0)))
3421592Srgrimes			fail |= 0x10;
3431592Srgrimes		if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
3441592Srgrimes		    !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
3451592Srgrimes			fail |= 0x20;
3461592Srgrimes		if (ifp->if_flags & IFF_DEBUG) {
3471592Srgrimes			printf(" %c %s", fail ? '-' : '+',
3481592Srgrimes			    ether_sprintf(ni->ni_macaddr));
3491592Srgrimes			printf(" %s%c", ether_sprintf(ni->ni_bssid),
3501592Srgrimes			    fail & 0x20 ? '!' : ' ');
3511592Srgrimes			printf(" %3d%c", ieee80211_chan2ieee(ic, ni->ni_chan),
3522286Sjkh				fail & 0x01 ? '!' : ' ');
3532286Sjkh			printf(" %+4d", ni->ni_rssi);
3542286Sjkh			printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2,
3552286Sjkh			    fail & 0x08 ? '!' : ' ');
3562286Sjkh			printf(" %4s%c",
3572286Sjkh			    (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" :
3582286Sjkh			    (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" :
3592286Sjkh			    "????",
3602286Sjkh			    fail & 0x02 ? '!' : ' ');
3612286Sjkh			printf(" %3s%c ",
3622286Sjkh			    (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ?
3632286Sjkh			    "wep" : "no",
3642286Sjkh			    fail & 0x04 ? '!' : ' ');
3652286Sjkh			ieee80211_print_essid(ni->ni_essid, ni->ni_esslen);
3662286Sjkh			printf("%s\n", fail & 0x10 ? "!" : "");
3672286Sjkh		}
3682286Sjkh		if (!fail) {
3692286Sjkh			if (selbs == NULL)
3702286Sjkh				selbs = ni;
3712286Sjkh			else if (ni->ni_rssi > selbs->ni_rssi) {
3722286Sjkh				ieee80211_unref_node(&selbs);
3731592Srgrimes				selbs = ni;
3741592Srgrimes			} else
3751592Srgrimes				ieee80211_unref_node(&ni);
3761592Srgrimes		} else {
3771592Srgrimes			ieee80211_unref_node(&ni);
3781592Srgrimes		}
3791592Srgrimes	}
3801592Srgrimes	if (selbs == NULL)
3811592Srgrimes		goto notfound;
3821592Srgrimes	(*ic->ic_node_copy)(ic, ic->ic_bss, selbs);
3831592Srgrimes	if (ic->ic_opmode == IEEE80211_M_IBSS) {
3841592Srgrimes		ieee80211_fix_rate(ic, ic->ic_bss, IEEE80211_F_DOFRATE |
3858870Srgrimes		    IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
3861592Srgrimes		if (ic->ic_bss->ni_rates.rs_nrates == 0) {
3871592Srgrimes			selbs->ni_fails++;
3881592Srgrimes			ieee80211_unref_node(&selbs);
3891592Srgrimes			goto notfound;
3901592Srgrimes		}
3911592Srgrimes		ieee80211_unref_node(&selbs);
3921592Srgrimes		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
3931592Srgrimes	} else {
3941592Srgrimes		ieee80211_unref_node(&selbs);
3952286Sjkh		ieee80211_new_state(ic, IEEE80211_S_AUTH, -1);
3961592Srgrimes	}
3971592Srgrimes}
3981592Srgrimes
3991592Srgrimesstatic struct ieee80211_node *
4001592Srgrimesieee80211_node_alloc(struct ieee80211com *ic)
4011592Srgrimes{
4021592Srgrimes	return malloc(sizeof(struct ieee80211_node), M_80211_NODE,
4031592Srgrimes		M_NOWAIT | M_ZERO);
4042286Sjkh}
4051592Srgrimes
4061592Srgrimesstatic void
4071592Srgrimesieee80211_node_free(struct ieee80211com *ic, struct ieee80211_node *ni)
4081592Srgrimes{
4091592Srgrimes	free(ni, M_80211_NODE);
4101592Srgrimes}
4111592Srgrimes
4121592Srgrimesstatic void
4131592Srgrimesieee80211_node_copy(struct ieee80211com *ic,
4141592Srgrimes	struct ieee80211_node *dst, const struct ieee80211_node *src)
4151592Srgrimes{
4161592Srgrimes	*dst = *src;
4171592Srgrimes}
4181592Srgrimes
4191592Srgrimesstatic u_int8_t
4201592Srgrimesieee80211_node_getrssi(struct ieee80211com *ic, struct ieee80211_node *ni)
4211592Srgrimes{
4221592Srgrimes	return ni->ni_rssi;
4231592Srgrimes}
4241592Srgrimes
4251592Srgrimesstatic void
4261592Srgrimesieee80211_setup_node(struct ieee80211com *ic,
4271592Srgrimes	struct ieee80211_node *ni, u_int8_t *macaddr)
4281592Srgrimes{
4291592Srgrimes	int hash;
4301592Srgrimes
4311592Srgrimes	IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr);
4321592Srgrimes	hash = IEEE80211_NODE_HASH(macaddr);
4331592Srgrimes	ni->ni_refcnt = 1;		/* mark referenced */
4341592Srgrimes	IEEE80211_NODE_LOCK(ic);
4351592Srgrimes	TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list);
4361592Srgrimes	LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash);
4371592Srgrimes	/*
4381592Srgrimes	 * Note we don't enable the inactive timer when acting
4391592Srgrimes	 * as a station.  Nodes created in this mode represent
4401592Srgrimes	 * AP's identified while scanning.  If we time them out
4411592Srgrimes	 * then several things happen: we can't return the data
4421592Srgrimes	 * to users to show the list of AP's we encountered, and
4431592Srgrimes	 * more importantly, we'll incorrectly deauthenticate
4441592Srgrimes	 * ourself because the inactivity timer will kick us off.
4451592Srgrimes	 */
4461592Srgrimes	if (ic->ic_opmode != IEEE80211_M_STA)
4471592Srgrimes		ic->ic_inact_timer = IEEE80211_INACT_WAIT;
4481592Srgrimes	IEEE80211_NODE_UNLOCK(ic);
4491592Srgrimes}
4501592Srgrimes
4511592Srgrimesstruct ieee80211_node *
4521592Srgrimesieee80211_alloc_node(struct ieee80211com *ic, u_int8_t *macaddr)
4531592Srgrimes{
4541592Srgrimes	struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic);
4551592Srgrimes	if (ni != NULL)
4561592Srgrimes		ieee80211_setup_node(ic, ni, macaddr);
4571592Srgrimes	return ni;
4581592Srgrimes}
4591592Srgrimes
4601592Srgrimesstruct ieee80211_node *
4611592Srgrimesieee80211_dup_bss(struct ieee80211com *ic, u_int8_t *macaddr)
4621592Srgrimes{
4631592Srgrimes	struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic);
4641592Srgrimes	if (ni != NULL) {
4651592Srgrimes		memcpy(ni, ic->ic_bss, sizeof(struct ieee80211_node));
4661592Srgrimes		ieee80211_setup_node(ic, ni, macaddr);
4671592Srgrimes	}
4681592Srgrimes	return ni;
4691592Srgrimes}
4701592Srgrimes
4711592Srgrimesstruct ieee80211_node *
4721592Srgrimesieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr)
4731592Srgrimes{
4741592Srgrimes	struct ieee80211_node *ni;
4751592Srgrimes	int hash;
4761592Srgrimes
4771592Srgrimes	hash = IEEE80211_NODE_HASH(macaddr);
4781592Srgrimes	IEEE80211_NODE_LOCK(ic);
4791592Srgrimes	LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) {
4801592Srgrimes		if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
4811592Srgrimes			atomic_add_int(&ni->ni_refcnt, 1); /* mark referenced */
4821592Srgrimes			break;
4831592Srgrimes		}
4841592Srgrimes	}
4851592Srgrimes	IEEE80211_NODE_UNLOCK(ic);
4861592Srgrimes	return ni;
4871592Srgrimes}
4881592Srgrimes
4891592Srgrimes/*
4901592Srgrimes * Like find but search based on the channel too.
4911592Srgrimes */
4921592Srgrimesstruct ieee80211_node *
4931592Srgrimesieee80211_lookup_node(struct ieee80211com *ic,
4941592Srgrimes	u_int8_t *macaddr, struct ieee80211_channel *chan)
4951592Srgrimes{
4961592Srgrimes	struct ieee80211_node *ni;
4971592Srgrimes	int hash;
4981592Srgrimes
4991592Srgrimes	hash = IEEE80211_NODE_HASH(macaddr);
5001592Srgrimes	IEEE80211_NODE_LOCK(ic);
5011592Srgrimes	LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) {
5021592Srgrimes		if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) && ni->ni_chan == chan) {
5031592Srgrimes			atomic_add_int(&ni->ni_refcnt, 1);/* mark referenced */
5041592Srgrimes			break;
5051592Srgrimes		}
5061592Srgrimes	}
5071592Srgrimes	IEEE80211_NODE_UNLOCK(ic);
5081592Srgrimes	return ni;
5091592Srgrimes}
5101592Srgrimes
5111592Srgrimesstatic void
5121592Srgrimes_ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
5131592Srgrimes{
5141592Srgrimes	KASSERT(ni != ic->ic_bss, ("freeing bss node"));
5151592Srgrimes
5161592Srgrimes	TAILQ_REMOVE(&ic->ic_node, ni, ni_list);
5171592Srgrimes	LIST_REMOVE(ni, ni_hash);
5181592Srgrimes	if (TAILQ_EMPTY(&ic->ic_node))
5191592Srgrimes		ic->ic_inact_timer = 0;
520	(*ic->ic_node_free)(ic, ni);
521}
522
523void
524ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
525{
526	KASSERT(ni != ic->ic_bss, ("freeing ic_bss"));
527
528	/* XXX need equivalent of atomic_dec_and_test */
529	atomic_subtract_int(&ni->ni_refcnt, 1);
530	if (atomic_cmpset_int(&ni->ni_refcnt, 0, 1)) {
531		IEEE80211_NODE_LOCK(ic);
532		_ieee80211_free_node(ic, ni);
533		IEEE80211_NODE_UNLOCK(ic);
534	}
535}
536
537void
538ieee80211_free_allnodes(struct ieee80211com *ic)
539{
540	struct ieee80211_node *ni;
541
542	IEEE80211_NODE_LOCK(ic);
543	while ((ni = TAILQ_FIRST(&ic->ic_node)) != NULL)
544		_ieee80211_free_node(ic, ni);
545	IEEE80211_NODE_UNLOCK(ic);
546}
547
548/*
549 * Timeout inactive nodes.  Note that we cannot hold the node
550 * lock while sending a frame as this would lead to a LOR.
551 * Instead we use a generation number to mark nodes that we've
552 * scanned and drop the lock and restart a scan if we have to
553 * time out a node.  Since we are single-threaded by virtue of
554 * controlling the inactivity timer we can be sure this will
555 * process each node only once.
556 */
557void
558ieee80211_timeout_nodes(struct ieee80211com *ic)
559{
560	struct ieee80211_node *ni;
561	u_int gen = ic->ic_scangen++;		/* NB: ok 'cuz single-threaded*/
562
563restart:
564	IEEE80211_NODE_LOCK(ic);
565	TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
566		if (ni->ni_scangen == gen)	/* previously handled */
567			continue;
568		ni->ni_scangen = gen;
569		if (++ni->ni_inact > IEEE80211_INACT_MAX) {
570			IEEE80211_DPRINTF(("station %s timed out "
571			    "due to inactivity (%u secs)\n",
572			    ether_sprintf(ni->ni_macaddr),
573			    ni->ni_inact));
574			/*
575			 * Send a deauthenticate frame.
576			 *
577			 * Drop the node lock before sending the
578			 * deauthentication frame in case the driver takes
579			 * a lock, as this will result in a LOR between the
580			 * node lock and the driver lock.
581			 */
582			IEEE80211_NODE_UNLOCK(ic);
583			IEEE80211_SEND_MGMT(ic, ni,
584			    IEEE80211_FC0_SUBTYPE_DEAUTH,
585			    IEEE80211_REASON_AUTH_EXPIRE);
586			ieee80211_free_node(ic, ni);
587			ic->ic_stats.is_node_timeout++;
588			goto restart;
589		}
590	}
591	if (!TAILQ_EMPTY(&ic->ic_node))
592		ic->ic_inact_timer = IEEE80211_INACT_WAIT;
593	IEEE80211_NODE_UNLOCK(ic);
594}
595
596void
597ieee80211_iterate_nodes(struct ieee80211com *ic, ieee80211_iter_func *f, void *arg)
598{
599	struct ieee80211_node *ni;
600
601	IEEE80211_NODE_LOCK(ic);
602	TAILQ_FOREACH(ni, &ic->ic_node, ni_list)
603		(*f)(arg, ni);
604	IEEE80211_NODE_UNLOCK(ic);
605}
606