ieee80211_node.c revision 127770
1116742Ssam/*-
2116904Ssam * Copyright (c) 2001 Atsushi Onoe
3116742Ssam * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting
4116742Ssam * All rights reserved.
5116742Ssam *
6116742Ssam * Redistribution and use in source and binary forms, with or without
7116742Ssam * modification, are permitted provided that the following conditions
8116742Ssam * are met:
9116742Ssam * 1. Redistributions of source code must retain the above copyright
10116904Ssam *    notice, this list of conditions and the following disclaimer.
11116904Ssam * 2. Redistributions in binary form must reproduce the above copyright
12116904Ssam *    notice, this list of conditions and the following disclaimer in the
13116904Ssam *    documentation and/or other materials provided with the distribution.
14116904Ssam * 3. The name of the author may not be used to endorse or promote products
15116904Ssam *    derived from this software without specific prior written permission.
16116742Ssam *
17116742Ssam * Alternatively, this software may be distributed under the terms of the
18116742Ssam * GNU General Public License ("GPL") version 2 as published by the Free
19116742Ssam * Software Foundation.
20116742Ssam *
21116904Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22116904Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23116904Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24116904Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25116904Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26116904Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27116904Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28116904Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29116904Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30116904Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31116742Ssam */
32116742Ssam
33116742Ssam#include <sys/cdefs.h>
34116742Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_node.c 127770 2004-04-02 23:09:24Z sam $");
35116742Ssam
36116742Ssam#include "opt_inet.h"
37116742Ssam
38116742Ssam#include <sys/param.h>
39116742Ssam#include <sys/systm.h>
40116742Ssam#include <sys/mbuf.h>
41116742Ssam#include <sys/malloc.h>
42116742Ssam#include <sys/kernel.h>
43116742Ssam#include <sys/socket.h>
44116742Ssam#include <sys/sockio.h>
45116742Ssam#include <sys/endian.h>
46116742Ssam#include <sys/errno.h>
47116742Ssam#include <sys/bus.h>
48116742Ssam#include <sys/proc.h>
49116742Ssam#include <sys/sysctl.h>
50116742Ssam
51116742Ssam#include <machine/atomic.h>
52116742Ssam
53116742Ssam#include <net/if.h>
54116742Ssam#include <net/if_dl.h>
55116742Ssam#include <net/if_media.h>
56116742Ssam#include <net/if_arp.h>
57116742Ssam#include <net/ethernet.h>
58116742Ssam#include <net/if_llc.h>
59116742Ssam
60116742Ssam#include <net80211/ieee80211_var.h>
61116742Ssam
62116742Ssam#include <net/bpf.h>
63116742Ssam
64116742Ssam#ifdef INET
65116742Ssam#include <netinet/in.h>
66116742Ssam#include <netinet/if_ether.h>
67116742Ssam#endif
68116742Ssam
69116742Ssamstatic struct ieee80211_node *ieee80211_node_alloc(struct ieee80211com *);
70116742Ssamstatic void ieee80211_node_free(struct ieee80211com *, struct ieee80211_node *);
71116742Ssamstatic void ieee80211_node_copy(struct ieee80211com *,
72116742Ssam		struct ieee80211_node *, const struct ieee80211_node *);
73120104Ssamstatic u_int8_t ieee80211_node_getrssi(struct ieee80211com *,
74120104Ssam		struct ieee80211_node *);
75120104Ssam
76116742Ssamstatic void ieee80211_setup_node(struct ieee80211com *ic,
77116742Ssam		struct ieee80211_node *ni, u_int8_t *macaddr);
78116742Ssamstatic void _ieee80211_free_node(struct ieee80211com *,
79116742Ssam		struct ieee80211_node *);
80116742Ssam
81120481SsamMALLOC_DEFINE(M_80211_NODE, "node", "802.11 node state");
82120481Ssam
83116742Ssamvoid
84116742Ssamieee80211_node_attach(struct ifnet *ifp)
85116742Ssam{
86116742Ssam	struct ieee80211com *ic = (void *)ifp;
87116742Ssam
88116742Ssam	/* XXX need unit */
89121816Sbrooks	IEEE80211_NODE_LOCK_INIT(ic, ifp->if_xname);
90116742Ssam	TAILQ_INIT(&ic->ic_node);
91116742Ssam	ic->ic_node_alloc = ieee80211_node_alloc;
92116742Ssam	ic->ic_node_free = ieee80211_node_free;
93116742Ssam	ic->ic_node_copy = ieee80211_node_copy;
94120104Ssam	ic->ic_node_getrssi = ieee80211_node_getrssi;
95120483Ssam	ic->ic_scangen = 1;
96118887Ssam}
97118887Ssam
98118887Ssamvoid
99118887Ssamieee80211_node_lateattach(struct ifnet *ifp)
100118887Ssam{
101118887Ssam	struct ieee80211com *ic = (void *)ifp;
102127766Ssam	struct ieee80211_node *ni;
103118887Ssam
104127766Ssam	ni = (*ic->ic_node_alloc)(ic);
105127766Ssam	KASSERT(ni != NULL, ("unable to setup inital BSS node"));
106127766Ssam	ni->ni_chan = IEEE80211_CHAN_ANYC;
107127766Ssam	ic->ic_bss = ni;
108127648Ssam	ic->ic_txpower = IEEE80211_TXPOWER_MAX;
109116742Ssam}
110116742Ssam
111116742Ssamvoid
112116742Ssamieee80211_node_detach(struct ifnet *ifp)
113116742Ssam{
114116742Ssam	struct ieee80211com *ic = (void *)ifp;
115116742Ssam
116116742Ssam	if (ic->ic_bss != NULL)
117116742Ssam		(*ic->ic_node_free)(ic, ic->ic_bss);
118116742Ssam	ieee80211_free_allnodes(ic);
119121172Ssam	IEEE80211_NODE_LOCK_DESTROY(ic);
120116742Ssam}
121116742Ssam
122116742Ssam/*
123116742Ssam * AP scanning support.
124116742Ssam */
125116742Ssam
126116742Ssam/*
127116742Ssam * Initialize the active channel set based on the set
128116742Ssam * of available channels and the current PHY mode.
129116742Ssam */
130117811Ssamstatic void
131116742Ssamieee80211_reset_scan(struct ifnet *ifp)
132116742Ssam{
133116742Ssam	struct ieee80211com *ic = (void *)ifp;
134116742Ssam
135116742Ssam	memcpy(ic->ic_chan_scan, ic->ic_chan_active,
136116742Ssam		sizeof(ic->ic_chan_active));
137117811Ssam	/* NB: hack, setup so next_scan starts with the first channel */
138117811Ssam	if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC)
139117811Ssam		ic->ic_bss->ni_chan = &ic->ic_channels[IEEE80211_CHAN_MAX];
140116742Ssam}
141116742Ssam
142116742Ssam/*
143116742Ssam * Begin an active scan.
144116742Ssam */
145116742Ssamvoid
146117811Ssamieee80211_begin_scan(struct ifnet *ifp)
147116742Ssam{
148116742Ssam	struct ieee80211com *ic = (void *)ifp;
149116742Ssam
150117811Ssam	/*
151117811Ssam	 * In all but hostap mode scanning starts off in
152117811Ssam	 * an active mode before switching to passive.
153117811Ssam	 */
154121180Ssam	if (ic->ic_opmode != IEEE80211_M_HOSTAP) {
155117811Ssam		ic->ic_flags |= IEEE80211_F_ASCAN;
156121180Ssam		ic->ic_stats.is_scan_active++;
157121180Ssam	} else
158121180Ssam		ic->ic_stats.is_scan_passive++;
159116742Ssam	if (ifp->if_flags & IFF_DEBUG)
160116742Ssam		if_printf(ifp, "begin %s scan\n",
161117811Ssam			(ic->ic_flags & IEEE80211_F_ASCAN) ?
162116742Ssam				"active" : "passive");
163116742Ssam	/*
164117811Ssam	 * Clear scan state and flush any previously seen
165117811Ssam	 * AP's.  Note that the latter assumes we don't act
166117811Ssam	 * as both an AP and a station, otherwise we'll
167117811Ssam	 * potentially flush state of stations associated
168117811Ssam	 * with us.
169116742Ssam	 */
170117811Ssam	ieee80211_reset_scan(ifp);
171116742Ssam	ieee80211_free_allnodes(ic);
172116742Ssam
173117811Ssam	/* Scan the next channel. */
174117811Ssam	ieee80211_next_scan(ifp);
175116742Ssam}
176116742Ssam
177116742Ssam/*
178116742Ssam * Switch to the next channel marked for scanning.
179116742Ssam */
180116742Ssamvoid
181116742Ssamieee80211_next_scan(struct ifnet *ifp)
182116742Ssam{
183116742Ssam	struct ieee80211com *ic = (void *)ifp;
184116742Ssam	struct ieee80211_channel *chan;
185116742Ssam
186116742Ssam	chan = ic->ic_bss->ni_chan;
187116742Ssam	for (;;) {
188116742Ssam		if (++chan > &ic->ic_channels[IEEE80211_CHAN_MAX])
189116742Ssam			chan = &ic->ic_channels[0];
190116742Ssam		if (isset(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan))) {
191116742Ssam			/*
192116742Ssam			 * Honor channels marked passive-only
193116742Ssam			 * during an active scan.
194116742Ssam			 */
195116742Ssam			if ((ic->ic_flags & IEEE80211_F_ASCAN) == 0 ||
196116742Ssam			    (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0)
197116742Ssam				break;
198116742Ssam		}
199116742Ssam		if (chan == ic->ic_bss->ni_chan) {
200116742Ssam			ieee80211_end_scan(ifp);
201116742Ssam			return;
202116742Ssam		}
203116742Ssam	}
204116742Ssam	clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan));
205116742Ssam	IEEE80211_DPRINTF(("ieee80211_next_scan: chan %d->%d\n",
206116742Ssam	    ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan),
207116742Ssam	    ieee80211_chan2ieee(ic, chan)));
208116742Ssam	ic->ic_bss->ni_chan = chan;
209117811Ssam	ieee80211_new_state(ic, IEEE80211_S_SCAN, -1);
210116742Ssam}
211116742Ssam
212116742Ssamvoid
213116742Ssamieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan)
214116742Ssam{
215116742Ssam	struct ieee80211_node *ni;
216116742Ssam	struct ifnet *ifp = &ic->ic_if;
217116742Ssam
218116742Ssam	ni = ic->ic_bss;
219116742Ssam	if (ifp->if_flags & IFF_DEBUG)
220116742Ssam		if_printf(ifp, "creating ibss\n");
221116742Ssam	ic->ic_flags |= IEEE80211_F_SIBSS;
222116742Ssam	ni->ni_chan = chan;
223116742Ssam	ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)];
224116742Ssam	IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr);
225116742Ssam	IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr);
226116742Ssam	if (ic->ic_opmode == IEEE80211_M_IBSS)
227116742Ssam		ni->ni_bssid[0] |= 0x02;	/* local bit for IBSS */
228116742Ssam	ni->ni_esslen = ic->ic_des_esslen;
229116742Ssam	memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen);
230116742Ssam	ni->ni_rssi = 0;
231116742Ssam	ni->ni_rstamp = 0;
232116742Ssam	memset(ni->ni_tstamp, 0, sizeof(ni->ni_tstamp));
233116742Ssam	ni->ni_intval = ic->ic_lintval;
234116742Ssam	ni->ni_capinfo = IEEE80211_CAPINFO_IBSS;
235116742Ssam	if (ic->ic_flags & IEEE80211_F_WEPON)
236116742Ssam		ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY;
237116742Ssam	if (ic->ic_phytype == IEEE80211_T_FH) {
238116742Ssam		ni->ni_fhdwell = 200;	/* XXX */
239116742Ssam		ni->ni_fhindex = 1;
240116742Ssam	}
241117811Ssam	ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
242116742Ssam}
243116742Ssam
244127767Ssamstatic int
245127767Ssamieee80211_match_bss(struct ifnet *ifp, struct ieee80211_node *ni)
246127767Ssam{
247127767Ssam	struct ieee80211com *ic = (void *)ifp;
248127767Ssam        u_int8_t rate;
249127767Ssam        int fail;
250127767Ssam
251127767Ssam	fail = 0;
252127767Ssam	if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan)))
253127767Ssam		fail |= 0x01;
254127767Ssam	if (ic->ic_des_chan != IEEE80211_CHAN_ANYC &&
255127767Ssam	    ni->ni_chan != ic->ic_des_chan)
256127767Ssam		fail |= 0x01;
257127767Ssam	if (ic->ic_opmode == IEEE80211_M_IBSS) {
258127767Ssam		if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0)
259127767Ssam			fail |= 0x02;
260127767Ssam	} else {
261127767Ssam		if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0)
262127767Ssam			fail |= 0x02;
263127767Ssam	}
264127767Ssam	if (ic->ic_flags & IEEE80211_F_WEPON) {
265127767Ssam		if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0)
266127767Ssam			fail |= 0x04;
267127767Ssam	} else {
268127767Ssam		/* XXX does this mean privacy is supported or required? */
269127767Ssam		if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY)
270127767Ssam			fail |= 0x04;
271127767Ssam	}
272127767Ssam	rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO);
273127767Ssam	if (rate & IEEE80211_RATE_BASIC)
274127767Ssam		fail |= 0x08;
275127767Ssam	if (ic->ic_des_esslen != 0 &&
276127767Ssam	    (ni->ni_esslen != ic->ic_des_esslen ||
277127767Ssam	     memcmp(ni->ni_essid, ic->ic_des_essid, ic->ic_des_esslen) != 0))
278127767Ssam		fail |= 0x10;
279127767Ssam	if ((ic->ic_flags & IEEE80211_F_DESBSSID) &&
280127767Ssam	    !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid))
281127767Ssam		fail |= 0x20;
282127767Ssam#ifdef IEEE80211_DEBUG
283127767Ssam	if (ifp->if_flags & IFF_DEBUG) {
284127767Ssam		printf(" %c %s", fail ? '-' : '+',
285127767Ssam		    ether_sprintf(ni->ni_macaddr));
286127767Ssam		printf(" %s%c", ether_sprintf(ni->ni_bssid),
287127767Ssam		    fail & 0x20 ? '!' : ' ');
288127767Ssam		printf(" %3d%c", ieee80211_chan2ieee(ic, ni->ni_chan),
289127767Ssam			fail & 0x01 ? '!' : ' ');
290127767Ssam		printf(" %+4d", ni->ni_rssi);
291127767Ssam		printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2,
292127767Ssam		    fail & 0x08 ? '!' : ' ');
293127767Ssam		printf(" %4s%c",
294127767Ssam		    (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" :
295127767Ssam		    (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" :
296127767Ssam		    "????",
297127767Ssam		    fail & 0x02 ? '!' : ' ');
298127767Ssam		printf(" %3s%c ",
299127767Ssam		    (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ?
300127767Ssam		    "wep" : "no",
301127767Ssam		    fail & 0x04 ? '!' : ' ');
302127767Ssam		ieee80211_print_essid(ni->ni_essid, ni->ni_esslen);
303127767Ssam		printf("%s\n", fail & 0x10 ? "!" : "");
304127767Ssam	}
305127767Ssam#endif
306127767Ssam	return fail;
307127767Ssam}
308127767Ssam
309116742Ssam/*
310116742Ssam * Complete a scan of potential channels.
311116742Ssam */
312116742Ssamvoid
313116742Ssamieee80211_end_scan(struct ifnet *ifp)
314116742Ssam{
315116742Ssam	struct ieee80211com *ic = (void *)ifp;
316116742Ssam	struct ieee80211_node *ni, *nextbs, *selbs;
317116742Ssam	int i, fail;
318116742Ssam
319116742Ssam	ic->ic_flags &= ~IEEE80211_F_ASCAN;
320116742Ssam	ni = TAILQ_FIRST(&ic->ic_node);
321116742Ssam
322116742Ssam	if (ic->ic_opmode == IEEE80211_M_HOSTAP) {
323116742Ssam		/* XXX off stack? */
324116742Ssam		u_char occupied[roundup(IEEE80211_CHAN_MAX, NBBY)];
325116742Ssam		/*
326116742Ssam		 * The passive scan to look for existing AP's completed,
327116742Ssam		 * select a channel to camp on.  Identify the channels
328116742Ssam		 * that already have one or more AP's and try to locate
329116742Ssam		 * an unnoccupied one.  If that fails, pick a random
330116742Ssam		 * channel from the active set.
331116742Ssam		 */
332116742Ssam		for (; ni != NULL; ni = nextbs) {
333116742Ssam			ieee80211_ref_node(ni);
334116742Ssam			nextbs = TAILQ_NEXT(ni, ni_list);
335116742Ssam			setbit(occupied, ieee80211_chan2ieee(ic, ni->ni_chan));
336116742Ssam			ieee80211_free_node(ic, ni);
337116742Ssam		}
338116742Ssam		for (i = 0; i < IEEE80211_CHAN_MAX; i++)
339116742Ssam			if (isset(ic->ic_chan_active, i) && isclr(occupied, i))
340116742Ssam				break;
341116742Ssam		if (i == IEEE80211_CHAN_MAX) {
342116742Ssam			fail = arc4random() & 3;	/* random 0-3 */
343116742Ssam			for (i = 0; i < IEEE80211_CHAN_MAX; i++)
344116742Ssam				if (isset(ic->ic_chan_active, i) && fail-- == 0)
345116742Ssam					break;
346116742Ssam		}
347116742Ssam		ieee80211_create_ibss(ic, &ic->ic_channels[i]);
348116742Ssam		return;
349116742Ssam	}
350116742Ssam	if (ni == NULL) {
351116742Ssam		IEEE80211_DPRINTF(("%s: no scan candidate\n", __func__));
352116742Ssam  notfound:
353116742Ssam		if (ic->ic_opmode == IEEE80211_M_IBSS &&
354116742Ssam		    (ic->ic_flags & IEEE80211_F_IBSSON) &&
355116742Ssam		    ic->ic_des_esslen != 0) {
356116742Ssam			ieee80211_create_ibss(ic, ic->ic_ibss_chan);
357116742Ssam			return;
358116742Ssam		}
359116742Ssam		/*
360116742Ssam		 * Reset the list of channels to scan and start again.
361116742Ssam		 */
362116742Ssam		ieee80211_reset_scan(ifp);
363116742Ssam		ieee80211_next_scan(ifp);
364116742Ssam		return;
365116742Ssam	}
366116742Ssam	selbs = NULL;
367116742Ssam	if (ifp->if_flags & IFF_DEBUG)
368119150Ssam		if_printf(ifp, "\tmacaddr          bssid         chan  rssi rate flag  wep  essid\n");
369116742Ssam	for (; ni != NULL; ni = nextbs) {
370116742Ssam		ieee80211_ref_node(ni);
371116742Ssam		nextbs = TAILQ_NEXT(ni, ni_list);
372116742Ssam		if (ni->ni_fails) {
373116742Ssam			/*
374116742Ssam			 * The configuration of the access points may change
375116742Ssam			 * during my scan.  So delete the entry for the AP
376116742Ssam			 * and retry to associate if there is another beacon.
377116742Ssam			 */
378116742Ssam			if (ni->ni_fails++ > 2)
379116742Ssam				ieee80211_free_node(ic, ni);
380116742Ssam			continue;
381116742Ssam		}
382127767Ssam		if (ieee80211_match_bss(ifp, ni) == 0) {
383116742Ssam			if (selbs == NULL)
384116742Ssam				selbs = ni;
385116742Ssam			else if (ni->ni_rssi > selbs->ni_rssi) {
386116742Ssam				ieee80211_unref_node(&selbs);
387116742Ssam				selbs = ni;
388116742Ssam			} else
389116742Ssam				ieee80211_unref_node(&ni);
390116742Ssam		} else {
391116742Ssam			ieee80211_unref_node(&ni);
392116742Ssam		}
393116742Ssam	}
394116742Ssam	if (selbs == NULL)
395116742Ssam		goto notfound;
396116742Ssam	(*ic->ic_node_copy)(ic, ic->ic_bss, selbs);
397116742Ssam	if (ic->ic_opmode == IEEE80211_M_IBSS) {
398116742Ssam		ieee80211_fix_rate(ic, ic->ic_bss, IEEE80211_F_DOFRATE |
399116742Ssam		    IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
400116742Ssam		if (ic->ic_bss->ni_rates.rs_nrates == 0) {
401116742Ssam			selbs->ni_fails++;
402116742Ssam			ieee80211_unref_node(&selbs);
403116742Ssam			goto notfound;
404116742Ssam		}
405116742Ssam		ieee80211_unref_node(&selbs);
406117811Ssam		ieee80211_new_state(ic, IEEE80211_S_RUN, -1);
407116742Ssam	} else {
408116742Ssam		ieee80211_unref_node(&selbs);
409117811Ssam		ieee80211_new_state(ic, IEEE80211_S_AUTH, -1);
410116742Ssam	}
411116742Ssam}
412116742Ssam
413116742Ssamstatic struct ieee80211_node *
414116742Ssamieee80211_node_alloc(struct ieee80211com *ic)
415116742Ssam{
416127768Ssam	struct ieee80211_node *ni;
417127768Ssam	MALLOC(ni, struct ieee80211_node *, sizeof(struct ieee80211_node),
418127768Ssam		M_80211_NODE, M_NOWAIT | M_ZERO);
419127768Ssam	return ni;
420116742Ssam}
421116742Ssam
422116742Ssamstatic void
423116742Ssamieee80211_node_free(struct ieee80211com *ic, struct ieee80211_node *ni)
424116742Ssam{
425127768Ssam	FREE(ni, M_80211_NODE);
426116742Ssam}
427116742Ssam
428116742Ssamstatic void
429116742Ssamieee80211_node_copy(struct ieee80211com *ic,
430116742Ssam	struct ieee80211_node *dst, const struct ieee80211_node *src)
431116742Ssam{
432116742Ssam	*dst = *src;
433116742Ssam}
434116742Ssam
435120104Ssamstatic u_int8_t
436120104Ssamieee80211_node_getrssi(struct ieee80211com *ic, struct ieee80211_node *ni)
437120104Ssam{
438120104Ssam	return ni->ni_rssi;
439120104Ssam}
440120104Ssam
441116742Ssamstatic void
442116742Ssamieee80211_setup_node(struct ieee80211com *ic,
443116742Ssam	struct ieee80211_node *ni, u_int8_t *macaddr)
444116742Ssam{
445116742Ssam	int hash;
446116742Ssam
447116742Ssam	IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr);
448116742Ssam	hash = IEEE80211_NODE_HASH(macaddr);
449116742Ssam	ni->ni_refcnt = 1;		/* mark referenced */
450121172Ssam	IEEE80211_NODE_LOCK(ic);
451116742Ssam	TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list);
452116742Ssam	LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash);
453116742Ssam	/*
454116742Ssam	 * Note we don't enable the inactive timer when acting
455116742Ssam	 * as a station.  Nodes created in this mode represent
456116742Ssam	 * AP's identified while scanning.  If we time them out
457116742Ssam	 * then several things happen: we can't return the data
458116742Ssam	 * to users to show the list of AP's we encountered, and
459116742Ssam	 * more importantly, we'll incorrectly deauthenticate
460116742Ssam	 * ourself because the inactivity timer will kick us off.
461116742Ssam	 */
462116742Ssam	if (ic->ic_opmode != IEEE80211_M_STA)
463116742Ssam		ic->ic_inact_timer = IEEE80211_INACT_WAIT;
464121172Ssam	IEEE80211_NODE_UNLOCK(ic);
465116742Ssam}
466116742Ssam
467116742Ssamstruct ieee80211_node *
468116742Ssamieee80211_alloc_node(struct ieee80211com *ic, u_int8_t *macaddr)
469116742Ssam{
470116742Ssam	struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic);
471116742Ssam	if (ni != NULL)
472116742Ssam		ieee80211_setup_node(ic, ni, macaddr);
473127769Ssam	else
474127769Ssam		ic->ic_stats.is_rx_nodealloc++;
475116742Ssam	return ni;
476116742Ssam}
477116742Ssam
478116742Ssamstruct ieee80211_node *
479116742Ssamieee80211_dup_bss(struct ieee80211com *ic, u_int8_t *macaddr)
480116742Ssam{
481116742Ssam	struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic);
482116742Ssam	if (ni != NULL) {
483116742Ssam		ieee80211_setup_node(ic, ni, macaddr);
484127770Ssam		/*
485127770Ssam		 * Inherit from ic_bss.
486127770Ssam		 */
487127770Ssam		IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_bss->ni_bssid);
488127770Ssam		ni->ni_chan = ic->ic_bss->ni_chan;
489127770Ssam	} else
490127770Ssam		ic->ic_stats.is_rx_nodealloc++;
491116742Ssam	return ni;
492116742Ssam}
493116742Ssam
494116742Ssamstruct ieee80211_node *
495116742Ssamieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr)
496116742Ssam{
497116742Ssam	struct ieee80211_node *ni;
498116742Ssam	int hash;
499116742Ssam
500116742Ssam	hash = IEEE80211_NODE_HASH(macaddr);
501121172Ssam	IEEE80211_NODE_LOCK(ic);
502116742Ssam	LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) {
503116742Ssam		if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) {
504116742Ssam			atomic_add_int(&ni->ni_refcnt, 1); /* mark referenced */
505116742Ssam			break;
506116742Ssam		}
507116742Ssam	}
508121172Ssam	IEEE80211_NODE_UNLOCK(ic);
509116742Ssam	return ni;
510116742Ssam}
511116742Ssam
512116742Ssam/*
513116742Ssam * Like find but search based on the channel too.
514116742Ssam */
515116742Ssamstruct ieee80211_node *
516116742Ssamieee80211_lookup_node(struct ieee80211com *ic,
517116742Ssam	u_int8_t *macaddr, struct ieee80211_channel *chan)
518116742Ssam{
519116742Ssam	struct ieee80211_node *ni;
520116742Ssam	int hash;
521116742Ssam
522116742Ssam	hash = IEEE80211_NODE_HASH(macaddr);
523121172Ssam	IEEE80211_NODE_LOCK(ic);
524116742Ssam	LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) {
525116742Ssam		if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) && ni->ni_chan == chan) {
526116742Ssam			atomic_add_int(&ni->ni_refcnt, 1);/* mark referenced */
527116742Ssam			break;
528116742Ssam		}
529116742Ssam	}
530121172Ssam	IEEE80211_NODE_UNLOCK(ic);
531116742Ssam	return ni;
532116742Ssam}
533116742Ssam
534116742Ssamstatic void
535116742Ssam_ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
536116742Ssam{
537119150Ssam	KASSERT(ni != ic->ic_bss, ("freeing bss node"));
538119150Ssam
539116742Ssam	TAILQ_REMOVE(&ic->ic_node, ni, ni_list);
540116742Ssam	LIST_REMOVE(ni, ni_hash);
541116742Ssam	if (TAILQ_EMPTY(&ic->ic_node))
542116742Ssam		ic->ic_inact_timer = 0;
543116742Ssam	(*ic->ic_node_free)(ic, ni);
544116742Ssam}
545116742Ssam
546116742Ssamvoid
547116742Ssamieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni)
548116742Ssam{
549119150Ssam	KASSERT(ni != ic->ic_bss, ("freeing ic_bss"));
550119150Ssam
551116742Ssam	/* XXX need equivalent of atomic_dec_and_test */
552116742Ssam	atomic_subtract_int(&ni->ni_refcnt, 1);
553116742Ssam	if (atomic_cmpset_int(&ni->ni_refcnt, 0, 1)) {
554121172Ssam		IEEE80211_NODE_LOCK(ic);
555116742Ssam		_ieee80211_free_node(ic, ni);
556121172Ssam		IEEE80211_NODE_UNLOCK(ic);
557116742Ssam	}
558116742Ssam}
559116742Ssam
560116742Ssamvoid
561116742Ssamieee80211_free_allnodes(struct ieee80211com *ic)
562116742Ssam{
563116742Ssam	struct ieee80211_node *ni;
564116742Ssam
565121172Ssam	IEEE80211_NODE_LOCK(ic);
566116742Ssam	while ((ni = TAILQ_FIRST(&ic->ic_node)) != NULL)
567116742Ssam		_ieee80211_free_node(ic, ni);
568121172Ssam	IEEE80211_NODE_UNLOCK(ic);
569116742Ssam}
570116742Ssam
571120483Ssam/*
572120483Ssam * Timeout inactive nodes.  Note that we cannot hold the node
573120483Ssam * lock while sending a frame as this would lead to a LOR.
574120483Ssam * Instead we use a generation number to mark nodes that we've
575120483Ssam * scanned and drop the lock and restart a scan if we have to
576120483Ssam * time out a node.  Since we are single-threaded by virtue of
577120483Ssam * controlling the inactivity timer we can be sure this will
578120483Ssam * process each node only once.
579120483Ssam */
580116742Ssamvoid
581116742Ssamieee80211_timeout_nodes(struct ieee80211com *ic)
582116742Ssam{
583120483Ssam	struct ieee80211_node *ni;
584120483Ssam	u_int gen = ic->ic_scangen++;		/* NB: ok 'cuz single-threaded*/
585116742Ssam
586120483Ssamrestart:
587121172Ssam	IEEE80211_NODE_LOCK(ic);
588120483Ssam	TAILQ_FOREACH(ni, &ic->ic_node, ni_list) {
589120483Ssam		if (ni->ni_scangen == gen)	/* previously handled */
590120483Ssam			continue;
591120483Ssam		ni->ni_scangen = gen;
592119150Ssam		if (++ni->ni_inact > IEEE80211_INACT_MAX) {
593119150Ssam			IEEE80211_DPRINTF(("station %s timed out "
594116742Ssam			    "due to inactivity (%u secs)\n",
595116742Ssam			    ether_sprintf(ni->ni_macaddr),
596116742Ssam			    ni->ni_inact));
597119150Ssam			/*
598119150Ssam			 * Send a deauthenticate frame.
599120483Ssam			 *
600120483Ssam			 * Drop the node lock before sending the
601120483Ssam			 * deauthentication frame in case the driver takes
602120483Ssam			 * a lock, as this will result in a LOR between the
603120483Ssam			 * node lock and the driver lock.
604119150Ssam			 */
605121172Ssam			IEEE80211_NODE_UNLOCK(ic);
606119150Ssam			IEEE80211_SEND_MGMT(ic, ni,
607119150Ssam			    IEEE80211_FC0_SUBTYPE_DEAUTH,
608119150Ssam			    IEEE80211_REASON_AUTH_EXPIRE);
609119150Ssam			ieee80211_free_node(ic, ni);
610121180Ssam			ic->ic_stats.is_node_timeout++;
611120483Ssam			goto restart;
612120483Ssam		}
613116742Ssam	}
614116742Ssam	if (!TAILQ_EMPTY(&ic->ic_node))
615116742Ssam		ic->ic_inact_timer = IEEE80211_INACT_WAIT;
616121172Ssam	IEEE80211_NODE_UNLOCK(ic);
617116742Ssam}
618116742Ssam
619116742Ssamvoid
620116742Ssamieee80211_iterate_nodes(struct ieee80211com *ic, ieee80211_iter_func *f, void *arg)
621116742Ssam{
622116742Ssam	struct ieee80211_node *ni;
623116742Ssam
624121172Ssam	IEEE80211_NODE_LOCK(ic);
625116742Ssam	TAILQ_FOREACH(ni, &ic->ic_node, ni_list)
626116742Ssam		(*f)(arg, ni);
627121172Ssam	IEEE80211_NODE_UNLOCK(ic);
628116742Ssam}
629