diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2005-02-17 18:28:06 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2005-02-17 18:28:06 +0000 |
commit | 86f341e75623affacf377939e31182d754a6a584 (patch) | |
tree | 962821474198da2e40b249d6aab3cace0c21233e /sys/net80211/ieee80211_node.c | |
parent | 47706ecaf12d67a399225153e56f1d84398bfc5a (diff) |
derived from NetBSD:
---
Make the node table into an LRU cache: least-recently used nodes
are at the end of the node queue. Change the reference-counting
discipline: ni->ni_refcnt indicates how many times net80211 has
granted ni to the driver. Every node in the table with ni_refcnt=0
is eligible to be garbage-collected. The mere presence of a node
in the table does not any longer indicate its auth/assoc state;
nodes have a ni_state variable, now.
While I am here, patch ieee80211_find_node_for_beacon to do a "best
match" by bssid/ssid/channel, not a "perfect match." This keeps
net80211 from caching duplicate nodes in the table.
---
ok deraadt@ dlg@, looks good jsg@
Diffstat (limited to 'sys/net80211/ieee80211_node.c')
-rw-r--r-- | sys/net80211/ieee80211_node.c | 472 |
1 files changed, 301 insertions, 171 deletions
diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index a73373f2b5b..f8d70b4e26e 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_node.c,v 1.2 2004/11/02 15:39:02 millert Exp $ */ +/* $OpenBSD: ieee80211_node.c,v 1.3 2005/02/17 18:28:05 reyk Exp $ */ /* $NetBSD: ieee80211_node.c,v 1.14 2004/05/09 09:18:47 dyoung Exp $ */ /*- @@ -105,8 +105,11 @@ static u_int8_t ieee80211_node_getrssi(struct ieee80211com *, static void ieee80211_setup_node(struct ieee80211com *ic, struct ieee80211_node *ni, u_int8_t *macaddr); -static void _ieee80211_free_node(struct ieee80211com *, +static void ieee80211_free_node(struct ieee80211com *, struct ieee80211_node *); +static struct ieee80211_node *ieee80211_alloc_node_helper(struct ieee80211com *); +static void ieee80211_node_cleanup(struct ieee80211com *, + struct ieee80211_node *); #ifdef __NetBSD__ MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state"); @@ -118,17 +121,43 @@ void ieee80211_node_attach(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; + int size; -#ifdef __FreeBSD__ - /* XXX need unit */ IEEE80211_NODE_LOCK_INIT(ic, ifp->if_xname); -#endif 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; + ic->ic_max_nnodes = ieee80211_cache_size; + + if (ic->ic_max_aid == 0) + ic->ic_max_aid = IEEE80211_AID_DEF; + else if (ic->ic_max_aid > IEEE80211_AID_MAX) + ic->ic_max_aid = IEEE80211_AID_MAX; + size = howmany(ic->ic_max_aid, 32) * sizeof(u_int32_t); + MALLOC(ic->ic_aid_bitmap, u_int32_t *, size, M_DEVBUF, M_NOWAIT); + if (ic->ic_aid_bitmap == NULL) { + /* XXX no way to recover */ + printf("%s: no memory for AID bitmap!\n", __func__); + ic->ic_max_aid = 0; + } + memset(ic->ic_aid_bitmap, 0, size); +} + +static struct ieee80211_node * +ieee80211_alloc_node_helper(struct ieee80211com *ic) +{ + struct ieee80211_node *ni; + if (ic->ic_nnodes >= ic->ic_max_nnodes) + ieee80211_clean_nodes(ic); + if (ic->ic_nnodes >= ic->ic_max_nnodes) + return NULL; + ni = (*ic->ic_node_alloc)(ic); + if (ni != NULL) + ic->ic_nnodes++; + return ni; } void @@ -137,10 +166,10 @@ ieee80211_node_lateattach(struct ifnet *ifp) struct ieee80211com *ic = (void *)ifp; struct ieee80211_node *ni; - ni = (*ic->ic_node_alloc)(ic); + ni = ieee80211_alloc_node_helper(ic); IASSERT(ni != NULL, ("unable to setup inital BSS node")); ni->ni_chan = IEEE80211_CHAN_ANYC; - ic->ic_bss = ni; + ic->ic_bss = ieee80211_ref_node(ni); ic->ic_txpower = IEEE80211_TXPOWER_MAX; } @@ -149,12 +178,14 @@ ieee80211_node_detach(struct ifnet *ifp) { struct ieee80211com *ic = (void *)ifp; - if (ic->ic_bss != NULL) + if (ic->ic_bss != NULL) { (*ic->ic_node_free)(ic, ic->ic_bss); + ic->ic_bss = NULL; + } ieee80211_free_allnodes(ic); -#ifdef __FreeBSD__ IEEE80211_NODE_LOCK_DESTROY(ic); -#endif + if (ic->ic_aid_bitmap != NULL) + FREE(ic->ic_aid_bitmap, M_DEVBUF); } /* @@ -240,7 +271,7 @@ ieee80211_next_scan(struct ifnet *ifp) } } clrbit(ic->ic_chan_scan, ieee80211_chan2ieee(ic, chan)); - IEEE80211_DPRINTF(("ieee80211_next_scan: chan %d->%d\n", + IEEE80211_DPRINTF(("%s: chan %d->%d\n", __func__, ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan), ieee80211_chan2ieee(ic, chan))); ic->ic_bss->ni_chan = chan; @@ -261,8 +292,12 @@ ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *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 */ + if (ic->ic_opmode == IEEE80211_M_IBSS) { + if ((ic->ic_flags & IEEE80211_F_DESBSSID) != 0) + IEEE80211_ADDR_COPY(ni->ni_bssid, ic->ic_des_bssid); + else + 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; @@ -353,6 +388,11 @@ ieee80211_end_scan(struct ifnet *ifp) struct ieee80211_node *ni, *nextbs, *selbs; int i, fail; + if (ifp->if_flags & IFF_DEBUG) + if_printf(ifp, "end %s scan\n", + (ic->ic_flags & IEEE80211_F_ASCAN) ? + "active" : "passive"); + ic->ic_flags &= ~IEEE80211_F_ASCAN; ni = TAILQ_FIRST(&ic->ic_node); @@ -367,10 +407,8 @@ ieee80211_end_scan(struct ifnet *ifp) * 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)) @@ -401,10 +439,8 @@ ieee80211_end_scan(struct ifnet *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) { /* @@ -419,38 +455,21 @@ ieee80211_end_scan(struct ifnet *ifp) if (ieee80211_match_bss(ic, ni) == 0) { if (selbs == NULL) selbs = ni; - else if (ni->ni_rssi > selbs->ni_rssi) { - ieee80211_unref_node(&selbs); + else if (ni->ni_rssi > selbs->ni_rssi) 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); + ieee80211_node_newstate(selbs, IEEE80211_STA_BSS); 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); + if (ic->ic_bss->ni_rates.rs_nrates == 0) goto notfound; - } - ieee80211_unref_node(&selbs); - /* - * Discard scan set; the nodes have a refcnt of zero - * and have not asked the driver to setup private - * node state. Let them be repopulated on demand either - * through transmission (ieee80211_find_txnode) or receipt - * of a probe response (to be added). - */ - ieee80211_free_allnodes(ic); ieee80211_new_state(ic, IEEE80211_S_RUN, -1); } else { - ieee80211_unref_node(&selbs); ieee80211_new_state(ic, IEEE80211_S_AUTH, -1); } } @@ -485,12 +504,18 @@ ieee80211_node_alloc(struct ieee80211com *ic) } static void +ieee80211_node_cleanup(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + if (ni->ni_challenge != NULL) { + FREE(ni->ni_challenge, M_DEVBUF); + ni->ni_challenge = NULL; + } +} + +static void ieee80211_node_free(struct ieee80211com *ic, struct ieee80211_node *ni) { - if (ni->ni_challenge != NULL) { - FREE(ni->ni_challenge, M_DEVBUF); - ni->ni_challenge = NULL; - } + ieee80211_node_cleanup(ic, ni); FREE(ni, M_80211_NODE); } @@ -498,6 +523,7 @@ static void ieee80211_node_copy(struct ieee80211com *ic, struct ieee80211_node *dst, const struct ieee80211_node *src) { + ieee80211_node_cleanup(ic, dst); *dst = *src; dst->ni_challenge = NULL; } @@ -513,15 +539,14 @@ ieee80211_setup_node(struct ieee80211com *ic, struct ieee80211_node *ni, u_int8_t *macaddr) { int hash; - ieee80211_node_critsec_decl(s); + IEEE80211_DPRINTF(("%s %s\n", __func__, ether_sprintf(macaddr))); IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr); hash = IEEE80211_NODE_HASH(macaddr); - ni->ni_refcnt = 1; /* mark referenced */ - ieee80211_node_critsec_begin(ic, s); - TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list); - LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash); - /* + ieee80211_node_newstate(ni, IEEE80211_STA_CACHE); + IEEE80211_NODE_LOCK_BH(ic); + + /* * 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 @@ -530,15 +555,18 @@ ieee80211_setup_node(struct ieee80211com *ic, * more importantly, we'll incorrectly deauthenticate * ourself because the inactivity timer will kick us off. */ - if (ic->ic_opmode != IEEE80211_M_STA) + if (ic->ic_opmode != IEEE80211_M_STA && + TAILQ_EMPTY(&ic->ic_node)) ic->ic_inact_timer = IEEE80211_INACT_WAIT; - ieee80211_node_critsec_end(ic, s); + TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list); + LIST_INSERT_HEAD(&ic->ic_hash[hash], ni, ni_hash); + IEEE80211_NODE_UNLOCK_BH(ic); } struct ieee80211_node * ieee80211_alloc_node(struct ieee80211com *ic, u_int8_t *macaddr) { - struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic); + struct ieee80211_node *ni = ieee80211_alloc_node_helper(ic); if (ni != NULL) ieee80211_setup_node(ic, ni, macaddr); else @@ -549,7 +577,7 @@ ieee80211_alloc_node(struct ieee80211com *ic, u_int8_t *macaddr) struct ieee80211_node * ieee80211_dup_bss(struct ieee80211com *ic, u_int8_t *macaddr) { - struct ieee80211_node *ni = (*ic->ic_node_alloc)(ic); + struct ieee80211_node *ni = ieee80211_alloc_node_helper(ic); if (ni != NULL) { ieee80211_setup_node(ic, ni, macaddr); /* @@ -568,14 +596,14 @@ _ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr) struct ieee80211_node *ni; int hash; -#ifdef __FreeBSD__ IEEE80211_NODE_LOCK_ASSERT(ic); -#endif /* __FreeBSD__ */ hash = IEEE80211_NODE_HASH(macaddr); LIST_FOREACH(ni, &ic->ic_hash[hash], ni_hash) { if (IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) { - ieee80211_node_incref(ni); /* mark referenced */ + /* least-recently used is at tail */ + TAILQ_REMOVE(&ic->ic_node, ni, ni_list); + TAILQ_INSERT_TAIL(&ic->ic_node, ni, ni_list); return ni; } } @@ -586,23 +614,24 @@ struct ieee80211_node * ieee80211_find_node(struct ieee80211com *ic, u_int8_t *macaddr) { struct ieee80211_node *ni; - ieee80211_node_critsec_decl(s); - ieee80211_node_critsec_begin(ic, s); + IEEE80211_NODE_LOCK(ic); ni = _ieee80211_find_node(ic, macaddr); - ieee80211_node_critsec_end(ic, s); + IEEE80211_NODE_UNLOCK(ic); return ni; } /* * Return a reference to the appropriate node for sending * a data frame. This handles node discovery in adhoc networks. + * + * Drivers will call this, so increase the reference count before + * returning the node. */ struct ieee80211_node * ieee80211_find_txnode(struct ieee80211com *ic, u_int8_t *macaddr) { struct ieee80211_node *ni; - ieee80211_node_critsec_decl(s); /* * The destination address should be in the node table @@ -610,15 +639,17 @@ ieee80211_find_txnode(struct ieee80211com *ic, u_int8_t *macaddr) * multicast/broadcast frame. */ if (ic->ic_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(macaddr)) - return ic->ic_bss; + return ieee80211_ref_node(ic->ic_bss); /* XXX can't hold lock across dup_bss 'cuz of recursive locking */ - ieee80211_node_critsec_begin(ic, s); + IEEE80211_NODE_LOCK(ic); ni = _ieee80211_find_node(ic, macaddr); - ieee80211_node_critsec_end(ic, s); - if (ni == NULL && - (ic->ic_opmode == IEEE80211_M_IBSS || - ic->ic_opmode == IEEE80211_M_AHDEMO)) { + IEEE80211_NODE_UNLOCK(ic); + if (ni == NULL) { + if (ic->ic_opmode != IEEE80211_M_IBSS && + ic->ic_opmode != IEEE80211_M_AHDEMO) + return NULL; + /* * Fake up a node; this handles node discovery in * adhoc mode. Note that for the driver's benefit @@ -628,32 +659,31 @@ ieee80211_find_txnode(struct ieee80211com *ic, u_int8_t *macaddr) * XXX need better way to handle this; issue probe * request so we can deduce rate set, etc. */ - ni = ieee80211_dup_bss(ic, macaddr); - if (ni != NULL) { - /* XXX no rate negotiation; just dup */ - ni->ni_rates = ic->ic_bss->ni_rates; - if (ic->ic_newassoc) - (*ic->ic_newassoc)(ic, ni, 1); - } + if ((ni = ieee80211_dup_bss(ic, macaddr)) == NULL) + return NULL; + /* XXX no rate negotiation; just dup */ + ni->ni_rates = ic->ic_bss->ni_rates; + if (ic->ic_newassoc) + (*ic->ic_newassoc)(ic, ni, 1); } - return ni; + return ieee80211_ref_node(ni); } /* - * For some types of packet and for some operating modes, it is - * desirable to process a Rx packet using its sender's node-record - * instead of the BSS record, when that is possible. + * It is usually desirable to process a Rx packet using its sender's + * node-record instead of the BSS record. * - * - AP mode: it is desirable to keep a node-record for every - * authenticated/associated station *in the BSS*. For future use, - * we also track neighboring APs, since they might belong to the - * same ESSID. + * - AP mode: keep a node-record for every authenticated/associated + * station *in the BSS*. For future use, we also track neighboring + * APs, since they might belong to the same ESS. APs in the same + * ESS may bridge packets to each other, forming a Wireless + * Distribution System (WDS). * - * - IBSS mode: it is desirable to keep a node-record for every - * station *in the BSS*. + * - IBSS mode: keep a node-record for every station *in the BSS*. + * Also track neighboring stations by their beacons/probe responses. * - * - monitor mode: it is desirable to keep a node-record for every - * sender, regardless of BSS. + * - monitor mode: keep a node-record for every sender, regardless + * of BSS. * * - STA mode: the only available node-record is the BSS record, * ic->ic_bss. @@ -669,23 +699,32 @@ ieee80211_needs_rxnode(struct ieee80211com *ic, struct ieee80211_frame *wh, u_int8_t **bssid) { struct ieee80211_node *bss = ic->ic_bss; - int needsnode, rc = 0; - - if (ic->ic_opmode == IEEE80211_M_STA) - return 0; + int monitor, rc = 0; - needsnode = (ic->ic_opmode == IEEE80211_M_MONITOR); + monitor = (ic->ic_opmode == IEEE80211_M_MONITOR); *bssid = NULL; switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { case IEEE80211_FC0_TYPE_CTL: + if (!monitor) + break; return (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_RTS; - case IEEE80211_FC0_TYPE_MGT: *bssid = wh->i_addr3; - rc = IEEE80211_ADDR_EQ(*bssid, bss->ni_bssid); + switch (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) { + case IEEE80211_FC0_SUBTYPE_BEACON: + case IEEE80211_FC0_SUBTYPE_PROBE_RESP: + rc = 1; + break; + default: + if (ic->ic_opmode == IEEE80211_M_STA) + break; + rc = IEEE80211_ADDR_EQ(*bssid, bss->ni_bssid) || + IEEE80211_ADDR_EQ(*bssid, etherbroadcastaddr); + break; + } break; case IEEE80211_FC0_TYPE_DATA: switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { @@ -708,99 +747,128 @@ ieee80211_needs_rxnode(struct ieee80211com *ic, struct ieee80211_frame *wh, } break; } - return needsnode || rc; + return monitor || rc; } +/* + * Drivers call this, so increase the reference count before returning + * the node. + */ struct ieee80211_node * ieee80211_find_rxnode(struct ieee80211com *ic, struct ieee80211_frame *wh) { struct ieee80211_node *ni; const static u_int8_t zero[IEEE80211_ADDR_LEN]; u_int8_t *bssid; - ieee80211_node_critsec_decl(s); if (!ieee80211_needs_rxnode(ic, wh, &bssid)) return ieee80211_ref_node(ic->ic_bss); - ieee80211_node_critsec_begin(ic, s); + IEEE80211_NODE_LOCK(ic); ni = _ieee80211_find_node(ic, wh->i_addr2); - ieee80211_node_critsec_end(ic, s); + IEEE80211_NODE_UNLOCK(ic); - if (ni == NULL) { - if (ic->ic_opmode != IEEE80211_M_HOSTAP) { - if ((ni = ieee80211_dup_bss(ic, wh->i_addr2)) != NULL) - IEEE80211_ADDR_COPY(ni->ni_bssid, - (bssid != NULL) ? bssid : zero); - - /* XXX see remarks in ieee80211_find_txnode */ - if (ni != NULL) { - /* XXX no rate negotiation; just dup */ - ni->ni_rates = ic->ic_bss->ni_rates; - if (ic->ic_newassoc) - (*ic->ic_newassoc)(ic, ni, 1); - } - IEEE80211_DPRINTF(("%s: faked-up node %p for %s\n", - __func__, ni, ether_sprintf(wh->i_addr2))); - } - ni = ieee80211_ref_node((ni == NULL) ? ic->ic_bss : ni); - } - IASSERT(ni != NULL, ("%s: null node", __func__)); - return ni; + if (ni != NULL) + return ieee80211_ref_node(ni); + if (ic->ic_opmode == IEEE80211_M_HOSTAP) + return ieee80211_ref_node(ic->ic_bss); + + /* XXX see remarks in ieee80211_find_txnode */ + /* XXX no rate negotiation; just dup */ + if ((ni = ieee80211_dup_bss(ic, wh->i_addr2)) == NULL) + return ieee80211_ref_node(ic->ic_bss); + + IEEE80211_ADDR_COPY(ni->ni_bssid, (bssid != NULL) ? bssid : zero); + + ni->ni_rates = ic->ic_bss->ni_rates; + if (ic->ic_newassoc) + (*ic->ic_newassoc)(ic, ni, 1); + + IEEE80211_DPRINTF(("%s: faked-up node %p for %s\n", __func__, ni, + ether_sprintf(wh->i_addr2))); + + return ieee80211_ref_node(ni); } /* * Like find but search based on the channel too. + * + * Note that ieee80211_find_node_for_beacon does not increase the + * reference count before returning the node, because drivers are not + * expected to call it. */ struct ieee80211_node * -ieee80211_lookup_node(struct ieee80211com *ic, - u_int8_t *macaddr, struct ieee80211_channel *chan) +ieee80211_find_node_for_beacon(struct ieee80211com *ic, u_int8_t *macaddr, + struct ieee80211_channel *chan, char *ssid) { - struct ieee80211_node *ni; - int hash; - ieee80211_node_critsec_decl(s); + struct ieee80211_node *ni, *best; + int hash, best_score, score; + + best = NULL; + best_score = -1; hash = IEEE80211_NODE_HASH(macaddr); - ieee80211_node_critsec_begin(ic, s); + 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) { - ieee80211_node_incref(ni);/* mark referenced */ - break; + if (!IEEE80211_ADDR_EQ(ni->ni_macaddr, macaddr)) + continue; + + score = (ni->ni_chan == chan) ? 1 : 0; + + if (ssid[1] == 0 || ni->ni_esslen == 0) + score++; + else if (ssid[1] != ni->ni_esslen || + memcmp(ssid + 2, ni->ni_essid, ssid[1]) != 0) + continue; + + if (score > best_score) { + best = ni; + best_score = score; } } - ieee80211_node_critsec_end(ic, s); - return ni; + IEEE80211_NODE_UNLOCK(ic); + return best; +} + +struct ieee80211_node * +ieee80211_lookup_node(struct ieee80211com *ic, + u_int8_t *macaddr, struct ieee80211_channel *chan) +{ + char ssid[2] = { 0, 0 }; + return ieee80211_find_node_for_beacon(ic, macaddr, chan, ssid); } static void -_ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) +ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) { IASSERT(ni != ic->ic_bss, ("freeing bss node")); + IEEE80211_DPRINTF(("%s %s\n", __func__, ether_sprintf(ni->ni_macaddr))); IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); TAILQ_REMOVE(&ic->ic_node, ni, ni_list); LIST_REMOVE(ni, ni_hash); + ic->ic_nnodes--; if (!IF_IS_EMPTY(&ni->ni_savedq)) { IF_PURGE(&ni->ni_savedq); if (ic->ic_set_tim) - ic->ic_set_tim(ic, ni->ni_associd, 0); + (*ic->ic_set_tim)(ic, ni->ni_associd, 0); } if (TAILQ_EMPTY(&ic->ic_node)) ic->ic_inact_timer = 0; (*ic->ic_node_free)(ic, ni); + /* TBD indicate to drivers that a new node can be allocated */ } void -ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) +ieee80211_release_node(struct ieee80211com *ic, struct ieee80211_node *ni) { - ieee80211_node_critsec_decl(s); - - IASSERT(ni != ic->ic_bss, ("freeing ic_bss")); - - if (ieee80211_node_decref(ni) == 0) { - ieee80211_node_critsec_begin(ic, s); - _ieee80211_free_node(ic, ni); - ieee80211_node_critsec_end(ic, s); + IEEE80211_DPRINTF(("%s %s refcnt %d\n", __func__, + ether_sprintf(ni->ni_macaddr), ni->ni_refcnt)); + if (ieee80211_node_decref(ni) == 0 && + ni->ni_state == IEEE80211_STA_COLLECT) { + IEEE80211_NODE_LOCK_BH(ic); + ieee80211_free_node(ic, ni); + IEEE80211_NODE_UNLOCK_BH(ic); } } @@ -808,12 +876,15 @@ void ieee80211_free_allnodes(struct ieee80211com *ic) { struct ieee80211_node *ni; - ieee80211_node_critsec_decl(s); - ieee80211_node_critsec_begin(ic, s); + IEEE80211_DPRINTF(("%s\n", __func__)); + IEEE80211_NODE_LOCK_BH(ic); while ((ni = TAILQ_FIRST(&ic->ic_node)) != NULL) - _ieee80211_free_node(ic, ni); - ieee80211_node_critsec_end(ic, s); + ieee80211_free_node(ic, ni); + IEEE80211_NODE_UNLOCK_BH(ic); + + if (ic->ic_bss != NULL) + ieee80211_node_cleanup(ic, ic->ic_bss); /* for station mode */ } /* @@ -826,53 +897,112 @@ ieee80211_free_allnodes(struct ieee80211com *ic) * process each node only once. */ void -ieee80211_timeout_nodes(struct ieee80211com *ic) +ieee80211_clean_nodes(struct ieee80211com *ic) { - struct ieee80211_node *ni; - ieee80211_node_critsec_decl(s); + struct ieee80211_node *ni, *next_ni; u_int gen = ic->ic_scangen++; /* NB: ok 'cuz single-threaded*/ -restart: - ieee80211_node_critsec_begin(ic, s); - TAILQ_FOREACH(ni, &ic->ic_node, ni_list) { + IEEE80211_NODE_LOCK(ic); + for (ni = TAILQ_FIRST(&ic->ic_node); ni != NULL; ni = next_ni) { + next_ni = TAILQ_NEXT(ni, ni_list); + if (ic->ic_nnodes <= ic->ic_max_nnodes) + break; 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. - */ - ieee80211_node_critsec_end(ic, s); + if (ni->ni_refcnt > 0) + continue; + IEEE80211_DPRINTF(("station %s purged from LRU cache\n", + ether_sprintf(ni->ni_macaddr))); + /* + * Send a deauthenticate frame. + */ + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + IEEE80211_NODE_UNLOCK(ic); IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_AUTH_EXPIRE); + IEEE80211_NODE_LOCK(ic); + ieee80211_node_leave(ic, ni); + } else ieee80211_free_node(ic, ni); - ic->ic_stats.is_node_timeout++; - goto restart; - } + ic->ic_stats.is_node_timeout++; } - if (!TAILQ_EMPTY(&ic->ic_node)) - ic->ic_inact_timer = IEEE80211_INACT_WAIT; - ieee80211_node_critsec_end(ic, s); + IEEE80211_NODE_UNLOCK(ic); } void ieee80211_iterate_nodes(struct ieee80211com *ic, ieee80211_iter_func *f, void *arg) { struct ieee80211_node *ni; - ieee80211_node_critsec_decl(s); - ieee80211_node_critsec_begin(ic, s); + IEEE80211_NODE_LOCK(ic); TAILQ_FOREACH(ni, &ic->ic_node, ni_list) (*f)(arg, ni); - ieee80211_node_critsec_end(ic, s); + IEEE80211_NODE_UNLOCK(ic); +} + +void +ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, + int resp) +{ + int newassoc; + + if (ni->ni_associd == 0) { + u_int16_t aid; + + /* + * It would be clever to search the bitmap + * more efficiently, but this will do for now. + */ + for (aid = 1; aid < ic->ic_max_aid; aid++) { + if (!IEEE80211_AID_ISSET(aid, + ic->ic_aid_bitmap)) + break; + } + if (aid >= ic->ic_max_aid) { + IEEE80211_SEND_MGMT(ic, ni, resp, + IEEE80211_REASON_ASSOC_TOOMANY); + ieee80211_node_leave(ic, ni); + return; + } + ni->ni_associd = aid | 0xc000; + IEEE80211_AID_SET(ni->ni_associd, ic->ic_aid_bitmap); + newassoc = 1; + /* XXX for 11g must turn off short slot time if long + slot time sta associates */ + } else + newassoc = 0; + + IEEE80211_DPRINTF(("station %s %s associated at aid %d\n", + ether_sprintf(ni->ni_macaddr), + (newassoc ? "newly" : "already"), + ni->ni_associd & ~0xc000)); + + /* give driver a chance to setup state like ni_txrate */ + if (ic->ic_newassoc) + (*ic->ic_newassoc)(ic, ni, newassoc); + IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS); + ieee80211_node_newstate(ni, IEEE80211_STA_ASSOC); +} + +/* + * Handle bookkeeping for station deauthentication/disassociation + * when operating as an ap. + */ +void +ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + + IASSERT(ic->ic_opmode == IEEE80211_M_HOSTAP, + ("not in ap mode, mode %u", ic->ic_opmode)); + /* + * If node wasn't previously associated all + * we need to do is reclaim the reference. + */ + if (ni->ni_associd == 0) + return; + IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); + ni->ni_associd = 0; + ieee80211_node_newstate(ni, IEEE80211_STA_COLLECT); } |