summaryrefslogtreecommitdiff
path: root/sys/net80211/ieee80211_node.c
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2005-02-17 18:28:06 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2005-02-17 18:28:06 +0000
commit86f341e75623affacf377939e31182d754a6a584 (patch)
tree962821474198da2e40b249d6aab3cace0c21233e /sys/net80211/ieee80211_node.c
parent47706ecaf12d67a399225153e56f1d84398bfc5a (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.c472
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);
}