📄 ieee80211_node.c
字号:
* us to use QoS to disable ACK's and to use short * preamble on 2.4G channels. */ if (vap->iv_flags & IEEE80211_F_WME) ni->ni_flags |= IEEE80211_NODE_QOS; if (vap->iv_flags & IEEE80211_F_SHPREAMBLE) ni->ni_capinfo |= IEEE80211_CAPINFO_SHORT_PREAMBLE; } IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "%s: %p<%s> refcnt %d\n", __func__, ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)); } return ni;}/* * Locate the node for sender, track state, and then pass the * (referenced) node up to the 802.11 layer for its use. We * return NULL when the sender is unknown; the driver is required * locate the appropriate virtual ap in that case; possibly * sending it to all (using ieee80211_input_all). */struct ieee80211_node *#ifdef IEEE80211_DEBUG_REFCNTieee80211_find_rxnode_debug(struct ieee80211com *ic, const struct ieee80211_frame_min *wh, const char *func, int line)#elseieee80211_find_rxnode(struct ieee80211com *ic, const struct ieee80211_frame_min *wh)#endif{#define IS_CTL(wh) \ ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL)#define IS_PSPOLL(wh) \ ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL) struct ieee80211_node_table *nt; struct ieee80211_node *ni; /* XXX check ic_bss first in station mode */ /* XXX 4-address frames? */ nt = &ic->ic_sta; IEEE80211_NODE_LOCK_IRQ(nt); if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/) ni = _ieee80211_find_node(nt, wh->i_addr1); else ni = _ieee80211_find_node(nt, wh->i_addr2); IEEE80211_NODE_UNLOCK_IRQ(nt); return ni;#undef IS_PSPOLL#undef IS_CTL}#ifdef IEEE80211_DEBUG_REFCNTEXPORT_SYMBOL(ieee80211_find_rxnode_debug);#elseEXPORT_SYMBOL(ieee80211_find_rxnode);#endif/* * Return a reference to the appropriate node for sending * a data frame. This handles node discovery in adhoc networks. */struct ieee80211_node *#ifdef IEEE80211_DEBUG_REFCNTieee80211_find_txnode_debug(struct ieee80211vap *vap, const u_int8_t *mac, const char *func, int line)#elseieee80211_find_txnode(struct ieee80211vap *vap, const u_int8_t *mac)#endif{ struct ieee80211_node_table *nt; struct ieee80211_node *ni; /* * The destination address should be in the node table * unless we are operating in station mode or this is a * multicast/broadcast frame. */ if (vap->iv_opmode == IEEE80211_M_STA || IEEE80211_IS_MULTICAST(mac)) return ieee80211_ref_node(vap->iv_bss); /* XXX can't hold lock across dup_bss due to recursive locking */ nt = &vap->iv_ic->ic_sta; IEEE80211_NODE_LOCK_IRQ(nt); ni = _ieee80211_find_node(nt, mac); IEEE80211_NODE_UNLOCK_IRQ(nt); if (ni == NULL) { if (vap->iv_opmode == IEEE80211_M_IBSS || vap->iv_opmode == IEEE80211_M_AHDEMO) { /* * In adhoc mode cons up a node for the destination. * Note that we need an additional reference for the * caller to be consistent with _ieee80211_find_node. */ ni = ieee80211_fakeup_adhoc_node(vap, mac); if (ni != NULL) (void) ieee80211_ref_node(ni); } else { IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_OUTPUT, mac, "no node, discard frame (%s)", __func__); vap->iv_stats.is_tx_nonode++; } } return ni;}#ifdef IEEE80211_DEBUG_REFCNTEXPORT_SYMBOL(ieee80211_find_txnode_debug);#elseEXPORT_SYMBOL(ieee80211_find_txnode);#endif/* Caller must lock the IEEE80211_NODE_LOCK * * Context: hwIRQ, softIRQ and process context */static void_ieee80211_free_node(struct ieee80211_node *ni){ struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_node_table *nt = ni->ni_table; IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "%s: %p<%s> in %s table, refcnt %d\n", __func__, ni, ether_sprintf(ni->ni_macaddr), nt != NULL ? nt->nt_name : "<gone>", ieee80211_node_refcnt(ni)); if (vap->iv_aid_bitmap != NULL) IEEE80211_AID_CLR(vap, ni->ni_associd); if (nt != NULL) { TAILQ_REMOVE(&nt->nt_node, ni, ni_list); LIST_REMOVE(ni, ni_hash); } vap->iv_ic->ic_node_free(ni);}void#ifdef IEEE80211_DEBUG_REFCNTieee80211_free_node_debug(struct ieee80211_node *ni, const char *func, int line)#elseieee80211_free_node(struct ieee80211_node *ni)#endif{ struct ieee80211_node_table *nt = ni->ni_table; struct ieee80211com *ic = ni->ni_ic;#ifdef IEEE80211_DEBUG_REFCNT IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, "%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line, ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni) - 1);#endif /* * XXX: may need to lock out the following race. we dectestref * and determine it's time to free the node. between the if() * and lock, we take an rx intr to receive a frame from this * node. the rx path (tasklet or intr) bumps this node's * refcnt and xmits a response frame. eventually that response * will get reaped, and the reaping code will attempt to use * the node. the code below will delete the node prior * to the reap and we could get a crash. * * as a stopgap before delving deeper, lock intrs to * prevent this case. */ IEEE80211_LOCK_IRQ(ic); if (ieee80211_node_dectestref(ni)) { /* * Beware; if the node is marked gone then it's already * been removed from the table and we cannot assume the * table still exists. Regardless, there's no need to lock * the table. */ if (ni->ni_table != NULL) { IEEE80211_NODE_LOCK(nt); _ieee80211_free_node(ni); IEEE80211_NODE_UNLOCK(nt); } else _ieee80211_free_node(ni); } IEEE80211_UNLOCK_IRQ(ic);}#ifdef IEEE80211_DEBUG_REFCNTEXPORT_SYMBOL(ieee80211_free_node_debug);#elseEXPORT_SYMBOL(ieee80211_free_node);#endif/* * Reclaim a node. If this is the last reference count then * do the normal free work. Otherwise remove it from the node * table and mark it gone by clearing the back-reference. */static voidnode_reclaim(struct ieee80211_node_table *nt, struct ieee80211_node *ni){ IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE, "%s: remove %p<%s> from %s table, refcnt %d\n", __func__, ni, ether_sprintf(ni->ni_macaddr), nt->nt_name, ieee80211_node_refcnt(ni)-1); if (!ieee80211_node_dectestref(ni)) { /* * Other references are present, just remove the * node from the table so it cannot be found. When * the references are dropped storage will be * reclaimed. This normally only happens for ic_bss. */ TAILQ_REMOVE(&nt->nt_node, ni, ni_list); LIST_REMOVE(ni, ni_hash); ni->ni_table = NULL; /* clear reference */ } else _ieee80211_free_node(ni);}static voidieee80211_node_table_reset(struct ieee80211_node_table *nt, struct ieee80211vap *match){ struct ieee80211_node *ni, *next; IEEE80211_NODE_LOCK_IRQ(nt); TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, next) { if (match != NULL && ni->ni_vap != match) continue; if (ni->ni_associd != 0) { struct ieee80211vap *vap = ni->ni_vap; if (vap->iv_auth->ia_node_leave != NULL) vap->iv_auth->ia_node_leave(ni); if (vap->iv_aid_bitmap != NULL) IEEE80211_AID_CLR(vap, ni->ni_associd); } node_reclaim(nt, ni); } IEEE80211_NODE_UNLOCK_IRQ(nt);}static voidieee80211_node_table_cleanup(struct ieee80211_node_table *nt){ struct ieee80211_node *ni, *next; TAILQ_FOREACH_SAFE(ni, &nt->nt_node, ni_list, next) { if (ni->ni_associd != 0) { struct ieee80211vap *vap = ni->ni_vap; if (vap->iv_auth->ia_node_leave != NULL) vap->iv_auth->ia_node_leave(ni); if (vap->iv_aid_bitmap != NULL) IEEE80211_AID_CLR(vap, ni->ni_associd); } node_reclaim(nt, ni); } del_timer(&nt->nt_wds_aging_timer); IEEE80211_SCAN_LOCK_DESTROY(nt); IEEE80211_NODE_LOCK_DESTROY(nt);}/* * Timeout inactive stations and do related housekeeping. * 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. * * Context: softIRQ (tasklet) */static voidieee80211_timeout_stations(struct ieee80211_node_table *nt){ struct ieee80211com *ic = nt->nt_ic; struct ieee80211_node *ni; u_int gen; int isadhoc; isadhoc = (ic->ic_opmode == IEEE80211_M_IBSS || ic->ic_opmode == IEEE80211_M_AHDEMO); IEEE80211_SCAN_LOCK_IRQ(nt); gen = ++nt->nt_scangen;restart: IEEE80211_NODE_LOCK_IRQ(nt); TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { if (ni->ni_scangen == gen) /* previously handled */ continue; /* * Ignore entries for which have yet to receive an * authentication frame. These are transient and * will be reclaimed when the last reference to them * goes away (when frame xmits complete). */ if (ic->ic_opmode == IEEE80211_M_HOSTAP && (ni->ni_flags & IEEE80211_NODE_AREF) == 0) continue; ni->ni_scangen = gen; /* * Free fragment if not needed anymore * (last fragment older than 1s). * XXX doesn't belong here */ if (ni->ni_rxfrag[0] != NULL && jiffies > ni->ni_rxfragstamp + HZ) { dev_kfree_skb(ni->ni_rxfrag[0]); ni->ni_rxfrag[0] = NULL; } /* * Special case ourself; we may be idle for extended periods * of time and regardless reclaiming our state is wrong. */ if (ni == ni->ni_vap->iv_bss) { /* NB: don't permit it to go negative */ if (ni->ni_inact > 0) ni->ni_inact--; continue; } ni->ni_inact--; if (ni->ni_associd != 0 || isadhoc) { struct ieee80211vap *vap = ni->ni_vap; /* * Age frames on the power save queue. */ if (ieee80211_node_saveq_age(ni) != 0 && IEEE80211_NODE_SAVEQ_QLEN(ni) == 0 && vap->iv_set_tim != NULL) vap->iv_set_tim(ni, 0); /* * Probe the station before time it out. We * send a null data frame which may not be * universally supported by drivers (need it * for ps-poll support so it should be...). */ if (0 < ni->ni_inact && ni->ni_inact <= vap->iv_inact_probe) { IEEE80211_NOTE(vap, IEEE80211_MSG_INACT | IEEE80211_MSG_NODE, ni, "%s", "probe station due to inactivity"); /* * Grab a reference before unlocking the table * so the node cannot be reclaimed before we * send the frame. ieee80211_send_nulldata * understands we've done this and reclaims the * ref for us as needed. */ ieee80211_ref_node(ni); IEEE80211_NODE_UNLOCK_IRQ_EARLY(nt); ieee80211_send_nulldata(ni); /* XXX stat? */ goto restart; } } if (ni->ni_inact <= 0) { IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_INACT | IEEE80211_MSG_NODE, ni, "station timed out due to inactivity (refcnt %u)", ieee80211_node_refcnt(ni)); /* * Send a deauthenticate frame and drop the station. * We grab a reference before unlocking the table so * the node cannot be reclaimed before we complete our * work. * * Separately we must drop the node lock before sending * in case the driver takes a lock, as this may result * in a LOR between the node lock and the driver lock. */ ni->ni_vap->iv_stats.is_node_timeout++; ieee80211_ref_node(ni); IEEE80211_NODE_UNLOCK_IRQ_EARLY(nt); if (ni->ni_associd != 0) { IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_DEAUTH, IEEE80211_REASON_AUTH_EXPIRE); } ieee80211_node_leave(ni); ieee80211_free_node(ni); goto restart; } } IEEE80211_NODE_UNLOCK_IRQ(nt); IEEE80211_SCAN_UNLOCK_IRQ(nt);}/* * Per-ieee80211com inactivity timer callback. */static voidieee80211_node_timeout(unsigned long arg){ struct ieee80211com *ic = (struct ieee80211com *) arg; ieee80211_scan_timeout(ic); ieee80211_timeout_stations(&ic->ic_sta); ic->ic_inact.expires = jiffies + IEEE80211_INACT_WAIT * HZ; add_timer(&ic->ic_inact);}voidieee80211_iterate_nodes(struct ieee80211_node_table *nt, ieee80211_iter_func *f, void *arg){ struct ieee80211_node *ni; u_int gen; IEEE80211_SCAN_LOCK_IRQ(nt); gen = ++nt->nt_scangen;restart: IEEE80211_NODE_LOCK(nt); TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { if (ni->ni_scangen != gen) { ni->ni_scangen = gen; (void) ieee80211_ref_node(ni);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -