diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index 966f812a75f3..eae29e91d3ed 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -1,601 +1,601 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_inet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #endif static struct ieee80211_node *ieee80211_node_alloc(struct ieee80211com *); static void ieee80211_node_free(struct ieee80211com *, struct ieee80211_node *); static void ieee80211_node_copy(struct ieee80211com *, struct ieee80211_node *, const struct ieee80211_node *); static u_int8_t ieee80211_node_getrssi(struct ieee80211com *, struct ieee80211_node *); static void ieee80211_setup_node(struct ieee80211com *ic, struct ieee80211_node *ni, u_int8_t *macaddr); static void _ieee80211_free_node(struct ieee80211com *, struct ieee80211_node *); MALLOC_DEFINE(M_80211_NODE, "node", "802.11 node state"); void ieee80211_node_attach(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; /* XXX need unit */ - mtx_init(&ic->ic_nodelock, ifp->if_name, "802.11 node table", MTX_DEF); + IEEE80211_NODE_LOCK_INIT(ic, ifp->if_name); TAILQ_INIT(&ic->ic_node); ic->ic_node_alloc = ieee80211_node_alloc; ic->ic_node_free = ieee80211_node_free; ic->ic_node_copy = ieee80211_node_copy; ic->ic_node_getrssi = ieee80211_node_getrssi; ic->ic_scangen = 1; } void ieee80211_node_lateattach(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; ic->ic_bss = (*ic->ic_node_alloc)(ic); KASSERT(ic->ic_bss != NULL, ("unable to setup inital BSS node")); ic->ic_bss->ni_chan = IEEE80211_CHAN_ANYC; } void ieee80211_node_detach(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; if (ic->ic_bss != NULL) (*ic->ic_node_free)(ic, ic->ic_bss); ieee80211_free_allnodes(ic); - mtx_destroy(&ic->ic_nodelock); + IEEE80211_NODE_LOCK_DESTROY(ic); } /* * AP scanning support. */ /* * Initialize the active channel set based on the set * of available channels and the current PHY mode. */ static void ieee80211_reset_scan(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; memcpy(ic->ic_chan_scan, ic->ic_chan_active, sizeof(ic->ic_chan_active)); /* NB: hack, setup so next_scan starts with the first channel */ if (ic->ic_bss->ni_chan == IEEE80211_CHAN_ANYC) ic->ic_bss->ni_chan = &ic->ic_channels[IEEE80211_CHAN_MAX]; } /* * Begin an active scan. */ void ieee80211_begin_scan(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; /* * In all but hostap mode scanning starts off in * an active mode before switching to passive. */ if (ic->ic_opmode != IEEE80211_M_HOSTAP) ic->ic_flags |= IEEE80211_F_ASCAN; if (ifp->if_flags & IFF_DEBUG) if_printf(ifp, "begin %s scan\n", (ic->ic_flags & IEEE80211_F_ASCAN) ? "active" : "passive"); /* * Clear scan state and flush any previously seen * AP's. Note that the latter assumes we don't act * as both an AP and a station, otherwise we'll * potentially flush state of stations associated * with us. */ ieee80211_reset_scan(ifp); ieee80211_free_allnodes(ic); /* Scan the next channel. */ ieee80211_next_scan(ifp); } /* * Switch to the next channel marked for scanning. */ void ieee80211_next_scan(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; struct ieee80211_channel *chan; chan = ic->ic_bss->ni_chan; for (;;) { if (++chan > &ic->ic_channels[IEEE80211_CHAN_MAX]) chan = &ic->ic_channels[0]; if (isset(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan))) { /* * Honor channels marked passive-only * during an active scan. */ if ((ic->ic_flags & IEEE80211_F_ASCAN) == 0 || (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0) break; } if (chan == ic->ic_bss->ni_chan) { ieee80211_end_scan(ifp); return; } } clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan)); IEEE80211_DPRINTF(("ieee80211_next_scan: chan %d->%d\n", ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan), ieee80211_chan2ieee(ic, chan))); ic->ic_bss->ni_chan = chan; ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); } void ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan) { struct ieee80211_node *ni; struct ifnet *ifp = &ic->ic_if; ni = ic->ic_bss; if (ifp->if_flags & IFF_DEBUG) if_printf(ifp, "creating ibss\n"); ic->ic_flags |= IEEE80211_F_SIBSS; ni->ni_chan = chan; ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; IEEE80211_ADDR_COPY(ni->ni_macaddr, ic->ic_myaddr); IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_myaddr); if (ic->ic_opmode == IEEE80211_M_IBSS) ni->ni_bssid[0] |= 0x02; /* local bit for IBSS */ ni->ni_esslen = ic->ic_des_esslen; memcpy(ni->ni_essid, ic->ic_des_essid, ni->ni_esslen); ni->ni_rssi = 0; ni->ni_rstamp = 0; memset(ni->ni_tstamp, 0, sizeof(ni->ni_tstamp)); ni->ni_intval = ic->ic_lintval; ni->ni_capinfo = IEEE80211_CAPINFO_IBSS; if (ic->ic_flags & IEEE80211_F_WEPON) ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY; if (ic->ic_phytype == IEEE80211_T_FH) { ni->ni_fhdwell = 200; /* XXX */ ni->ni_fhindex = 1; } ieee80211_new_state(ic, IEEE80211_S_RUN, -1); } /* * Complete a scan of potential channels. */ void ieee80211_end_scan(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; struct ieee80211_node *ni, *nextbs, *selbs; u_int8_t rate; int i, fail; ic->ic_flags &= ~IEEE80211_F_ASCAN; ni = TAILQ_FIRST(&ic->ic_node); if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* XXX off stack? */ u_char occupied[roundup(IEEE80211_CHAN_MAX, NBBY)]; /* * The passive scan to look for existing AP's completed, * select a channel to camp on. Identify the channels * that already have one or more AP's and try to locate * an unnoccupied one. If that fails, pick a random * channel from the active set. */ for (; ni != NULL; ni = nextbs) { ieee80211_ref_node(ni); nextbs = TAILQ_NEXT(ni, ni_list); setbit(occupied, ieee80211_chan2ieee(ic, ni->ni_chan)); ieee80211_free_node(ic, ni); } for (i = 0; i < IEEE80211_CHAN_MAX; i++) if (isset(ic->ic_chan_active, i) && isclr(occupied, i)) break; if (i == IEEE80211_CHAN_MAX) { fail = arc4random() & 3; /* random 0-3 */ for (i = 0; i < IEEE80211_CHAN_MAX; i++) if (isset(ic->ic_chan_active, i) && fail-- == 0) break; } ieee80211_create_ibss(ic, &ic->ic_channels[i]); return; } if (ni == NULL) { IEEE80211_DPRINTF(("%s: no scan candidate\n", __func__)); notfound: if (ic->ic_opmode == IEEE80211_M_IBSS && (ic->ic_flags & IEEE80211_F_IBSSON) && ic->ic_des_esslen != 0) { ieee80211_create_ibss(ic, ic->ic_ibss_chan); return; } /* * Reset the list of channels to scan and start again. */ ieee80211_reset_scan(ifp); ieee80211_next_scan(ifp); return; } selbs = NULL; if (ifp->if_flags & IFF_DEBUG) if_printf(ifp, "\tmacaddr bssid chan rssi rate flag wep essid\n"); for (; ni != NULL; ni = nextbs) { ieee80211_ref_node(ni); nextbs = TAILQ_NEXT(ni, ni_list); if (ni->ni_fails) { /* * The configuration of the access points may change * during my scan. So delete the entry for the AP * and retry to associate if there is another beacon. */ if (ni->ni_fails++ > 2) ieee80211_free_node(ic, ni); continue; } fail = 0; if (isclr(ic->ic_chan_active, ieee80211_chan2ieee(ic, ni->ni_chan))) fail |= 0x01; if (ic->ic_des_chan != IEEE80211_CHAN_ANYC && ni->ni_chan != ic->ic_des_chan) fail |= 0x01; if (ic->ic_opmode == IEEE80211_M_IBSS) { if ((ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) == 0) fail |= 0x02; } else { if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0) fail |= 0x02; } if (ic->ic_flags & IEEE80211_F_WEPON) { if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) fail |= 0x04; } else { if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) fail |= 0x04; } rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO); if (rate & IEEE80211_RATE_BASIC) fail |= 0x08; if (ic->ic_des_esslen != 0 && (ni->ni_esslen != ic->ic_des_esslen || memcmp(ni->ni_essid, ic->ic_des_essid, ic->ic_des_esslen != 0))) fail |= 0x10; if ((ic->ic_flags & IEEE80211_F_DESBSSID) && !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid)) fail |= 0x20; if (ifp->if_flags & IFF_DEBUG) { printf(" %c %s", fail ? '-' : '+', ether_sprintf(ni->ni_macaddr)); printf(" %s%c", ether_sprintf(ni->ni_bssid), fail & 0x20 ? '!' : ' '); printf(" %3d%c", ieee80211_chan2ieee(ic, ni->ni_chan), fail & 0x01 ? '!' : ' '); printf(" %+4d", ni->ni_rssi); printf(" %2dM%c", (rate & IEEE80211_RATE_VAL) / 2, fail & 0x08 ? '!' : ' '); printf(" %4s%c", (ni->ni_capinfo & IEEE80211_CAPINFO_ESS) ? "ess" : (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : "????", fail & 0x02 ? '!' : ' '); printf(" %3s%c ", (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ? "wep" : "no", fail & 0x04 ? '!' : ' '); ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); printf("%s\n", fail & 0x10 ? "!" : ""); } if (!fail) { if (selbs == NULL) selbs = ni; else if (ni->ni_rssi > selbs->ni_rssi) { ieee80211_unref_node(&selbs); selbs = ni; } else ieee80211_unref_node(&ni); } else { ieee80211_unref_node(&ni); } } if (selbs == NULL) goto notfound; (*ic->ic_node_copy)(ic, ic->ic_bss, selbs); if (ic->ic_opmode == IEEE80211_M_IBSS) { ieee80211_fix_rate(ic, ic->ic_bss, IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); if (ic->ic_bss->ni_rates.rs_nrates == 0) { selbs->ni_fails++; ieee80211_unref_node(&selbs); goto notfound; } ieee80211_unref_node(&selbs); ieee80211_new_state(ic, IEEE80211_S_RUN, -1); } else { ieee80211_unref_node(&selbs); ieee80211_new_state(ic, IEEE80211_S_AUTH, -1); } } static struct ieee80211_node * ieee80211_node_alloc(struct ieee80211com *ic) { return malloc(sizeof(struct ieee80211_node), M_80211_NODE, M_NOWAIT | M_ZERO); } static void ieee80211_node_free(struct ieee80211com *ic, struct ieee80211_node *ni) { free(ni, M_80211_NODE); } static void ieee80211_node_copy(struct ieee80211com *ic, struct ieee80211_node *dst, const struct ieee80211_node *src) { *dst = *src; } static u_int8_t ieee80211_node_getrssi(struct ieee80211com *ic, struct ieee80211_node *ni) { return ni->ni_rssi; } static void ieee80211_setup_node(struct ieee80211com *ic, struct ieee80211_node *ni, u_int8_t *macaddr) { int hash; IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr); hash = IEEE80211_NODE_HASH(macaddr); ni->ni_refcnt = 1; /* mark referenced */ - mtx_lock(&ic->ic_nodelock); + IEEE80211_NODE_LOCK(ic); TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list); LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash); /* * Note we don't enable the inactive timer when acting * as a station. Nodes created in this mode represent * AP's identified while scanning. If we time them out * then several things happen: we can't return the data * to users to show the list of AP's we encountered, and * more importantly, we'll incorrectly deauthenticate * ourself because the inactivity timer will kick us off. */ if (ic->ic_opmode != IEEE80211_M_STA) ic->ic_inact_timer = IEEE80211_INACT_WAIT; - mtx_unlock(&ic->ic_nodelock); + IEEE80211_NODE_UNLOCK(ic); } struct ieee80211_node * ieee80211_alloc_node(struct ieee80211com *ic, u_int8_t *macaddr) { struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic); if (ni != NULL) ieee80211_setup_node(ic, ni, macaddr); return ni; } struct ieee80211_node * ieee80211_dup_bss(struct ieee80211com *ic, u_int8_t *macaddr) { struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic); if (ni != NULL) { memcpy(ni, ic->ic_bss, sizeof(struct ieee80211_node)); ieee80211_setup_node(ic, ni, macaddr); } return ni; } struct ieee80211_node * ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr) { struct ieee80211_node *ni; int hash; hash = IEEE80211_NODE_HASH(macaddr); - mtx_lock(&ic->ic_nodelock); + IEEE80211_NODE_LOCK(ic); LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) { atomic_add_int(&ni->ni_refcnt, 1); /* mark referenced */ break; } } - mtx_unlock(&ic->ic_nodelock); + IEEE80211_NODE_UNLOCK(ic); return ni; } /* * Like find but search based on the channel too. */ struct ieee80211_node * ieee80211_lookup_node(struct ieee80211com *ic, u_int8_t *macaddr, struct ieee80211_channel *chan) { struct ieee80211_node *ni; int hash; hash = IEEE80211_NODE_HASH(macaddr); - mtx_lock(&ic->ic_nodelock); + IEEE80211_NODE_LOCK(ic); LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr) && ni->ni_chan == chan) { atomic_add_int(&ni->ni_refcnt, 1);/* mark referenced */ break; } } - mtx_unlock(&ic->ic_nodelock); + IEEE80211_NODE_UNLOCK(ic); return ni; } static void _ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) { KASSERT(ni != ic->ic_bss, ("freeing bss node")); TAILQ_REMOVE(&ic->ic_node, ni, ni_list); LIST_REMOVE(ni, ni_hash); if (TAILQ_EMPTY(&ic->ic_node)) ic->ic_inact_timer = 0; (*ic->ic_node_free)(ic, ni); } void ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) { KASSERT(ni != ic->ic_bss, ("freeing ic_bss")); /* XXX need equivalent of atomic_dec_and_test */ atomic_subtract_int(&ni->ni_refcnt, 1); if (atomic_cmpset_int(&ni->ni_refcnt, 0, 1)) { - mtx_lock(&ic->ic_nodelock); + IEEE80211_NODE_LOCK(ic); _ieee80211_free_node(ic, ni); - mtx_unlock(&ic->ic_nodelock); + IEEE80211_NODE_UNLOCK(ic); } } void ieee80211_free_allnodes(struct ieee80211com *ic) { struct ieee80211_node *ni; - mtx_lock(&ic->ic_nodelock); + IEEE80211_NODE_LOCK(ic); while ((ni = TAILQ_FIRST(&ic->ic_node)) != NULL) _ieee80211_free_node(ic, ni); - mtx_unlock(&ic->ic_nodelock); + IEEE80211_NODE_UNLOCK(ic); } /* * Timeout inactive nodes. Note that we cannot hold the node * lock while sending a frame as this would lead to a LOR. * Instead we use a generation number to mark nodes that we've * scanned and drop the lock and restart a scan if we have to * time out a node. Since we are single-threaded by virtue of * controlling the inactivity timer we can be sure this will * process each node only once. */ void ieee80211_timeout_nodes(struct ieee80211com *ic) { struct ieee80211_node *ni; u_int gen = ic->ic_scangen++; /* NB: ok 'cuz single-threaded*/ restart: - mtx_lock(&ic->ic_nodelock); + IEEE80211_NODE_LOCK(ic); TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { if (ni->ni_scangen == gen) /* previously handled */ continue; ni->ni_scangen = gen; if (++ni->ni_inact > IEEE80211_INACT_MAX) { IEEE80211_DPRINTF(("station %s timed out " "due to inactivity (%u secs)\n", ether_sprintf(ni->ni_macaddr), ni->ni_inact)); /* * Send a deauthenticate frame. * * Drop the node lock before sending the * deauthentication frame in case the driver takes * a lock, as this will result in a LOR between the * node lock and the driver lock. */ - mtx_unlock(&ic->ic_nodelock); + IEEE80211_NODE_UNLOCK(ic); IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_AUTH_EXPIRE); ieee80211_free_node(ic, ni); goto restart; } } if (!TAILQ_EMPTY(&ic->ic_node)) ic->ic_inact_timer = IEEE80211_INACT_WAIT; - mtx_unlock(&ic->ic_nodelock); + IEEE80211_NODE_UNLOCK(ic); } void ieee80211_iterate_nodes(struct ieee80211com *ic, ieee80211_iter_func *f, void *arg) { struct ieee80211_node *ni; - mtx_lock(&ic->ic_nodelock); + IEEE80211_NODE_LOCK(ic); TAILQ_FOREACH(ni, &ic->ic_node, ni_list) (*f)(arg, ni); - mtx_unlock(&ic->ic_nodelock); + IEEE80211_NODE_UNLOCK(ic); } diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h index d61ff7bd21af..b0e043749f73 100644 --- a/sys/net80211/ieee80211_node.h +++ b/sys/net80211/ieee80211_node.h @@ -1,148 +1,156 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _NET80211_IEEE80211_NODE_H_ #define _NET80211_IEEE80211_NODE_H_ #define IEEE80211_PSCAN_WAIT 5 /* passive scan wait */ #define IEEE80211_TRANS_WAIT 5 /* transition wait */ #define IEEE80211_INACT_WAIT 5 /* inactivity timer interval */ #define IEEE80211_INACT_MAX (300/IEEE80211_INACT_WAIT) #define IEEE80211_NODE_HASHSIZE 32 /* simple hash is enough for variation of macaddr */ #define IEEE80211_NODE_HASH(addr) \ (((u_int8_t *)(addr))[IEEE80211_ADDR_LEN - 1] % IEEE80211_NODE_HASHSIZE) #define IEEE80211_RATE_SIZE 8 /* 802.11 standard */ #define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */ struct ieee80211_rateset { u_int8_t rs_nrates; u_int8_t rs_rates[IEEE80211_RATE_MAXSIZE]; }; /* * Node specific information. Note that drivers are expected * to derive from this structure to add device-specific per-node * state. This is done by overriding the ic_node_* methods in * the ieee80211com structure. */ struct ieee80211_node { TAILQ_ENTRY(ieee80211_node) ni_list; LIST_ENTRY(ieee80211_node) ni_hash; u_int ni_refcnt; u_int ni_scangen; /* gen# for timeout scan */ /* hardware */ u_int32_t ni_rstamp; /* recv timestamp */ u_int8_t ni_rssi; /* recv ssi */ /* header */ u_int8_t ni_macaddr[IEEE80211_ADDR_LEN]; u_int8_t ni_bssid[IEEE80211_ADDR_LEN]; /* beacon, probe response */ u_int8_t ni_tstamp[8]; /* from last rcv'd beacon */ u_int16_t ni_intval; /* beacon interval */ u_int16_t ni_capinfo; /* capabilities */ u_int8_t ni_esslen; u_int8_t ni_essid[IEEE80211_NWID_LEN]; struct ieee80211_rateset ni_rates; /* negotiated rate set */ u_int8_t *ni_country; /* country information XXX */ struct ieee80211_channel *ni_chan; u_int16_t ni_fhdwell; /* FH only */ u_int8_t ni_fhindex; /* FH only */ u_int8_t ni_erp; /* 11g only */ #ifdef notyet /* DTIM and contention free period (CFP) */ u_int8_t ni_dtimperiod; u_int8_t ni_cfpperiod; /* # of DTIMs between CFPs */ u_int16_t ni_cfpduremain; /* remaining cfp duration */ u_int16_t ni_cfpmaxduration;/* max CFP duration in TU */ u_int16_t ni_nextdtim; /* time to next DTIM */ u_int16_t ni_timoffset; #endif /* others */ u_int16_t ni_associd; /* assoc response */ u_int16_t ni_txseq; /* seq to be transmitted */ u_int16_t ni_rxseq; /* seq previous received */ int ni_fails; /* failure count to associate */ int ni_inact; /* inactivity mark count */ int ni_txrate; /* index to ni_rates[] */ }; static __inline struct ieee80211_node * ieee80211_ref_node(struct ieee80211_node *ni) { atomic_add_int(&ni->ni_refcnt, 1); return ni; } static __inline void ieee80211_unref_node(struct ieee80211_node **ni) { atomic_subtract_int(&(*ni)->ni_refcnt, 1); *ni = NULL; /* guard against use */ } +#define IEEE80211_NODE_LOCK_INIT(_ic, _name) \ + mtx_init(&(_ic)->ic_nodelock, _name, "802.11 node table", MTX_DEF) +#define IEEE80211_NODE_LOCK_DESTROY(_ic) mtx_destroy(&(_ic)->ic_nodelock) +#define IEEE80211_NODE_LOCK(_ic) mtx_lock(&(_ic)->ic_nodelock) +#define IEEE80211_NODE_UNLOCK(_ic) mtx_unlock(&(_ic)->ic_nodelock) +#define IEEE80211_NODE_LOCK_ASSERT(_ic) \ + mtx_assert(&(_ic)->ic_nodelock, MA_OWNED) + struct ieee80211com; extern void ieee80211_node_attach(struct ifnet *); extern void ieee80211_node_lateattach(struct ifnet *); extern void ieee80211_node_detach(struct ifnet *); extern void ieee80211_begin_scan(struct ifnet *); extern void ieee80211_next_scan(struct ifnet *); extern void ieee80211_end_scan(struct ifnet *); extern struct ieee80211_node *ieee80211_alloc_node(struct ieee80211com *, u_int8_t *); extern struct ieee80211_node *ieee80211_dup_bss(struct ieee80211com *, u_int8_t *); extern struct ieee80211_node *ieee80211_find_node(struct ieee80211com *, u_int8_t *); extern struct ieee80211_node * ieee80211_lookup_node(struct ieee80211com *, u_int8_t *macaddr, struct ieee80211_channel *); extern void ieee80211_free_node(struct ieee80211com *, struct ieee80211_node *); extern void ieee80211_free_allnodes(struct ieee80211com *); typedef void ieee80211_iter_func(void *, struct ieee80211_node *); extern void ieee80211_iterate_nodes(struct ieee80211com *ic, ieee80211_iter_func *, void *); extern void ieee80211_timeout_nodes(struct ieee80211com *); extern void ieee80211_create_ibss(struct ieee80211com* , struct ieee80211_channel *); #endif /* _NET80211_IEEE80211_NODE_H_ */ diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c index 047d29c77caa..143f1351cbd7 100644 --- a/sys/net80211/ieee80211_proto.c +++ b/sys/net80211/ieee80211_proto.c @@ -1,512 +1,512 @@ /*- * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * IEEE 802.11 protocol support. */ #include "opt_inet.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #endif #define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) const char *ieee80211_mgt_subtype_name[] = { "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp", "probe_req", "probe_resp", "reserved#6", "reserved#7", "beacon", "atim", "disassoc", "auth", "deauth", "reserved#13", "reserved#14", "reserved#15" }; const char *ieee80211_state_name[IEEE80211_S_MAX] = { "INIT", /* IEEE80211_S_INIT */ "SCAN", /* IEEE80211_S_SCAN */ "AUTH", /* IEEE80211_S_AUTH */ "ASSOC", /* IEEE80211_S_ASSOC */ "RUN" /* IEEE80211_S_RUN */ }; static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int); void ieee80211_proto_attach(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; ifp->if_hdrlen = sizeof(struct ieee80211_frame); #ifdef notdef ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT; #else ic->ic_rtsthreshold = IEEE80211_RTS_MAX; #endif ic->ic_fragthreshold = 2346; /* XXX not used yet */ ic->ic_fixed_rate = -1; /* no fixed rate */ mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_name, "mgmt send q", MTX_DEF); /* protocol state change handler */ ic->ic_newstate = ieee80211_newstate; /* initialize management frame handlers */ ic->ic_recv_mgmt = ieee80211_recv_mgmt; ic->ic_send_mgmt = ieee80211_send_mgmt; } void ieee80211_proto_detach(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; IF_DRAIN(&ic->ic_mgtq); mtx_destroy(&ic->ic_mgtq.ifq_mtx); } void ieee80211_print_essid(u_int8_t *essid, int len) { int i; u_int8_t *p; if (len > IEEE80211_NWID_LEN) len = IEEE80211_NWID_LEN; /* determine printable or not */ for (i = 0, p = essid; i < len; i++, p++) { if (*p < ' ' || *p > 0x7e) break; } if (i == len) { printf("\""); for (i = 0, p = essid; i < len; i++, p++) printf("%c", *p); printf("\""); } else { printf("0x"); for (i = 0, p = essid; i < len; i++, p++) printf("%02x", *p); } } void ieee80211_dump_pkt(u_int8_t *buf, int len, int rate, int rssi) { struct ieee80211_frame *wh; int i; wh = (struct ieee80211_frame *)buf; switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { case IEEE80211_FC1_DIR_NODS: printf("NODS %s", ether_sprintf(wh->i_addr2)); printf("->%s", ether_sprintf(wh->i_addr1)); printf("(%s)", ether_sprintf(wh->i_addr3)); break; case IEEE80211_FC1_DIR_TODS: printf("TODS %s", ether_sprintf(wh->i_addr2)); printf("->%s", ether_sprintf(wh->i_addr3)); printf("(%s)", ether_sprintf(wh->i_addr1)); break; case IEEE80211_FC1_DIR_FROMDS: printf("FRDS %s", ether_sprintf(wh->i_addr3)); printf("->%s", ether_sprintf(wh->i_addr1)); printf("(%s)", ether_sprintf(wh->i_addr2)); break; case IEEE80211_FC1_DIR_DSTODS: printf("DSDS %s", ether_sprintf((u_int8_t *)&wh[1])); printf("->%s", ether_sprintf(wh->i_addr3)); printf("(%s", ether_sprintf(wh->i_addr2)); printf("->%s)", ether_sprintf(wh->i_addr1)); break; } switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { case IEEE80211_FC0_TYPE_DATA: printf(" data"); break; case IEEE80211_FC0_TYPE_MGT: printf(" %s", ieee80211_mgt_subtype_name[ (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) >> IEEE80211_FC0_SUBTYPE_SHIFT]); break; default: printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); break; } if (wh->i_fc[1] & IEEE80211_FC1_WEP) printf(" WEP"); if (rate >= 0) printf(" %dM", rate / 2); if (rssi >= 0) printf(" +%d", rssi); printf("\n"); if (len > 0) { for (i = 0; i < len; i++) { if ((i & 1) == 0) printf(" "); printf("%02x", buf[i]); } printf("\n"); } } int ieee80211_fix_rate(struct ieee80211com *ic, struct ieee80211_node *ni, int flags) { #define RV(v) ((v) & IEEE80211_RATE_VAL) int i, j, ignore, error; int okrate, badrate; struct ieee80211_rateset *srs, *nrs; u_int8_t r; error = 0; okrate = badrate = 0; srs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; nrs = &ni->ni_rates; for (i = 0; i < nrs->rs_nrates; ) { ignore = 0; if (flags & IEEE80211_F_DOSORT) { /* * Sort rates. */ for (j = i + 1; j < nrs->rs_nrates; j++) { if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) { r = nrs->rs_rates[i]; nrs->rs_rates[i] = nrs->rs_rates[j]; nrs->rs_rates[j] = r; } } } r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; badrate = r; if (flags & IEEE80211_F_DOFRATE) { /* * Apply fixed rate constraint. Note that we do * not apply the constraint to basic rates as * otherwise we may not be able to associate if * the rate set we submit to the AP is invalid * (e.g. fix rate at 36Mb/s which is not a basic * rate for 11a operation). */ if ((nrs->rs_rates[i] & IEEE80211_RATE_BASIC) == 0 && ic->ic_fixed_rate >= 0 && r != RV(srs->rs_rates[ic->ic_fixed_rate])) ignore++; } if (flags & IEEE80211_F_DONEGO) { /* * Check against supported rates. */ for (j = 0; j < srs->rs_nrates; j++) { if (r == RV(srs->rs_rates[j])) break; } if (j == srs->rs_nrates) { /* * A rate in the node's rate set is not * supported. If this is a basic rate and we * are operating as an AP then this is an error. * Otherwise we just discard/ignore the rate. * Note that this is important for 11b stations * when they want to associate with an 11g AP. */ if (ic->ic_opmode == IEEE80211_M_HOSTAP && (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)) error++; ignore++; } } if (flags & IEEE80211_F_DODEL) { /* * Delete unacceptable rates. */ if (ignore) { nrs->rs_nrates--; for (j = i; j < nrs->rs_nrates; j++) nrs->rs_rates[j] = nrs->rs_rates[j + 1]; nrs->rs_rates[j] = 0; continue; } } if (!ignore) okrate = nrs->rs_rates[i]; i++; } if (okrate == 0 || error != 0) return badrate | IEEE80211_RATE_BASIC; else return RV(okrate); #undef RV } static int ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int mgt) { struct ifnet *ifp = &ic->ic_if; struct ieee80211_node *ni; enum ieee80211_state ostate; ostate = ic->ic_state; IEEE80211_DPRINTF(("%s: %s -> %s\n", __func__, ieee80211_state_name[ostate], ieee80211_state_name[nstate])); ic->ic_state = nstate; /* state transition */ ni = ic->ic_bss; /* NB: no reference held */ switch (nstate) { case IEEE80211_S_INIT: switch (ostate) { case IEEE80211_S_INIT: break; case IEEE80211_S_RUN: switch (ic->ic_opmode) { case IEEE80211_M_STA: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, IEEE80211_REASON_ASSOC_LEAVE); break; case IEEE80211_M_HOSTAP: - mtx_lock(&ic->ic_nodelock); + IEEE80211_NODE_LOCK(ic); TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { if (ni->ni_associd == 0) continue; IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, IEEE80211_REASON_ASSOC_LEAVE); } - mtx_unlock(&ic->ic_nodelock); + IEEE80211_NODE_UNLOCK(ic); break; default: break; } /* FALLTHRU */ case IEEE80211_S_ASSOC: switch (ic->ic_opmode) { case IEEE80211_M_STA: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_AUTH_LEAVE); break; case IEEE80211_M_HOSTAP: - mtx_lock(&ic->ic_nodelock); + IEEE80211_NODE_LOCK(ic); TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_AUTH_LEAVE); } - mtx_unlock(&ic->ic_nodelock); + IEEE80211_NODE_UNLOCK(ic); break; default: break; } /* FALLTHRU */ case IEEE80211_S_AUTH: case IEEE80211_S_SCAN: ic->ic_mgt_timer = 0; IF_DRAIN(&ic->ic_mgtq); if (ic->ic_wep_ctx != NULL) { free(ic->ic_wep_ctx, M_DEVBUF); ic->ic_wep_ctx = NULL; } ieee80211_free_allnodes(ic); break; } break; case IEEE80211_S_SCAN: ic->ic_flags &= ~IEEE80211_F_SIBSS; /* initialize bss for probe request */ IEEE80211_ADDR_COPY(ni->ni_macaddr, ifp->if_broadcastaddr); IEEE80211_ADDR_COPY(ni->ni_bssid, ifp->if_broadcastaddr); ni->ni_rates = ic->ic_sup_rates[ ieee80211_chan2mode(ic, ni->ni_chan)]; ni->ni_associd = 0; ni->ni_rstamp = 0; switch (ostate) { case IEEE80211_S_INIT: if (ic->ic_opmode == IEEE80211_M_HOSTAP && ic->ic_des_chan != IEEE80211_CHAN_ANYC) { /* * AP operation and we already have a channel; * bypass the scan and startup immediately. */ ieee80211_create_ibss(ic, ic->ic_des_chan); } else { ieee80211_begin_scan(ifp); } break; case IEEE80211_S_SCAN: /* scan next */ if (ic->ic_flags & IEEE80211_F_ASCAN) { IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_REQ, 0); } break; case IEEE80211_S_RUN: /* beacon miss */ if (ifp->if_flags & IFF_DEBUG) { /* XXX bssid clobbered above */ if_printf(ifp, "no recent beacons from %s;" " rescanning\n", ether_sprintf(ic->ic_bss->ni_bssid)); } ieee80211_free_allnodes(ic); /* FALLTHRU */ case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: /* timeout restart scan */ ni = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr); if (ni != NULL) { ni->ni_fails++; ieee80211_unref_node(&ni); } ieee80211_begin_scan(ifp); break; } break; case IEEE80211_S_AUTH: switch (ostate) { case IEEE80211_S_INIT: IEEE80211_DPRINTF(("%s: invalid transition\n", __func__)); break; case IEEE80211_S_SCAN: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 1); break; case IEEE80211_S_AUTH: case IEEE80211_S_ASSOC: switch (mgt) { case IEEE80211_FC0_SUBTYPE_AUTH: /* ??? */ IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 2); break; case IEEE80211_FC0_SUBTYPE_DEAUTH: /* ignore and retry scan on timeout */ break; } break; case IEEE80211_S_RUN: switch (mgt) { case IEEE80211_FC0_SUBTYPE_AUTH: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 2); ic->ic_state = ostate; /* stay RUN */ break; case IEEE80211_FC0_SUBTYPE_DEAUTH: /* try to reauth */ IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 1); break; } break; } break; case IEEE80211_S_ASSOC: switch (ostate) { case IEEE80211_S_INIT: case IEEE80211_S_SCAN: case IEEE80211_S_ASSOC: IEEE80211_DPRINTF(("%s: invalid transition\n", __func__)); break; case IEEE80211_S_AUTH: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); break; case IEEE80211_S_RUN: IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1); break; } break; case IEEE80211_S_RUN: switch (ostate) { case IEEE80211_S_INIT: case IEEE80211_S_AUTH: case IEEE80211_S_RUN: IEEE80211_DPRINTF(("%s: invalid transition\n", __func__)); break; case IEEE80211_S_SCAN: /* adhoc/hostap mode */ case IEEE80211_S_ASSOC: /* infra mode */ KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates, ("%s: bogus xmit rate %u setup\n", __func__, ni->ni_txrate)); if (ifp->if_flags & IFF_DEBUG) { if_printf(ifp, " "); if (ic->ic_opmode == IEEE80211_M_STA) printf("associated "); else printf("synchronized "); printf("with %s ssid ", ether_sprintf(ni->ni_bssid)); ieee80211_print_essid(ic->ic_bss->ni_essid, ni->ni_esslen); printf(" channel %d start %uMb\n", ieee80211_chan2ieee(ic, ni->ni_chan), IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate])); } ic->ic_mgt_timer = 0; (*ifp->if_start)(ifp); break; } break; } return 0; }