--- a/net80211/ieee80211_node.c +++ b/net80211/ieee80211_node.c @@ -123,6 +123,9 @@ static void ieee80211_node_table_cleanup static void ieee80211_node_table_reset(struct ieee80211_node_table *, struct ieee80211vap *); +static struct ieee80211_node * +lookup_rxnode(struct ieee80211com *ic, struct ieee80211vap *vap, const u_int8_t *addr); + MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state"); void @@ -697,7 +700,7 @@ ieee80211_sta_join(struct ieee80211vap * struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni; - ni = ieee80211_find_node(&ic->ic_sta, se->se_macaddr); + ni = lookup_rxnode(ic, vap, se->se_macaddr); if (ni == NULL) { ni = ieee80211_alloc_node_table(vap, se->se_macaddr); IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, @@ -1394,6 +1397,53 @@ ieee80211_add_neighbor(struct ieee80211v return ni; } +struct ieee80211vap * +ieee80211_find_rxvap(struct ieee80211com *ic, const u_int8_t *mac) +{ + struct ieee80211vap *vap; + + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + if (IEEE80211_ADDR_EQ(vap->iv_myaddr, mac)) + return vap; + } + return NULL; +} +EXPORT_SYMBOL(ieee80211_find_rxvap); + +static struct ieee80211_node * +lookup_rxnode(struct ieee80211com *ic, struct ieee80211vap *vap, + const u_int8_t *addr) +{ + struct ieee80211_node_table *nt; + struct ieee80211_node *ni = NULL; + int use_bss = 0; + int hash; + + nt = &ic->ic_sta; + IEEE80211_NODE_TABLE_LOCK_IRQ(nt); + hash = IEEE80211_NODE_HASH(addr); + LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) { + if (IEEE80211_ADDR_EQ(ni->ni_macaddr, addr)) { + /* allow multiple nodes on different vaps */ + if (vap && (ni->ni_vap != vap)) + continue; + + ieee80211_ref_node(ni); + goto out; + } + } + + /* no match found */ + ni = NULL; + +out: + IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt); + return ni; +#undef IS_PSPOLL +#undef IS_CTL +} + + /* * Return the node for the sender of a frame; if the sender is unknown return * NULL. The caller is expected to deal with this. (The frame is sent to all @@ -1403,10 +1453,10 @@ ieee80211_add_neighbor(struct ieee80211v */ struct ieee80211_node * #ifdef IEEE80211_DEBUG_REFCNT -ieee80211_find_rxnode_debug(struct ieee80211com *ic, +ieee80211_find_rxnode_debug(struct ieee80211com *ic, struct ieee80211vap *vap, const struct ieee80211_frame_min *wh, const char *func, int line) #else -ieee80211_find_rxnode(struct ieee80211com *ic, +ieee80211_find_rxnode(struct ieee80211com *ic, struct ieee80211vap *vap, const struct ieee80211_frame_min *wh) #endif { @@ -1414,9 +1464,8 @@ ieee80211_find_rxnode(struct ieee80211co ((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; - struct ieee80211vap *vap, *avp; + struct ieee80211_node *ni = NULL; + struct ieee80211vap *avp; const u_int8_t *addr; if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/) @@ -1429,32 +1478,25 @@ ieee80211_find_rxnode(struct ieee80211co /* XXX check ic_bss first in station mode */ /* XXX 4-address frames? */ - nt = &ic->ic_sta; - IEEE80211_NODE_TABLE_LOCK_IRQ(nt); if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) { - TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + if (vap) { /* assume unicast if vap is set, mcast not supported for wds */ TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) { - if (!IEEE80211_ADDR_EQ(addr, avp->wds_mac)) + if (!IEEE80211_ADDR_EQ(addr, avp->wds_mac) || + !IEEE80211_ADDR_EQ(wh->i_addr1, avp->iv_myaddr)) continue; if (avp->iv_wdsnode) - return ieee80211_ref_node(avp->iv_wdsnode); - else - return NULL; + ni = ieee80211_ref_node(avp->iv_wdsnode); + return ni; } + if (!(vap->iv_flags_ext & IEEE80211_FEXT_WDS)) + return NULL; + } else { + return NULL; } } -#ifdef IEEE80211_DEBUG_REFCNT - ni = ieee80211_find_node_locked_debug(nt, addr, func, line); -#else - ni = ieee80211_find_node_locked(nt, addr); -#endif - IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt); - - return ni; -#undef IS_PSPOLL -#undef IS_CTL + return lookup_rxnode(ic, vap, addr); } #ifdef IEEE80211_DEBUG_REFCNT EXPORT_SYMBOL(ieee80211_find_rxnode_debug); @@ -1479,15 +1521,14 @@ ieee80211_find_txnode(struct ieee80211va struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node_table *nt; struct ieee80211_node *ni = NULL; + int hash; - IEEE80211_LOCK_IRQ(ic); if (vap->iv_opmode == IEEE80211_M_WDS) { if (vap->iv_wdsnode && (vap->iv_state == IEEE80211_S_RUN)) return ieee80211_ref_node(vap->iv_wdsnode); else return NULL; } - IEEE80211_UNLOCK_IRQ(ic); /* * The destination address should be in the node table @@ -1505,11 +1546,22 @@ ieee80211_find_txnode(struct ieee80211va /* XXX: Can't hold lock across dup_bss due to recursive locking. */ nt = &vap->iv_ic->ic_sta; IEEE80211_NODE_TABLE_LOCK_IRQ(nt); + hash = IEEE80211_NODE_HASH(mac); + LIST_FOREACH(ni, &nt->nt_hash[hash], ni_hash) { + if (ni->ni_vap != vap) + continue; + + if (IEEE80211_ADDR_EQ(ni->ni_macaddr, mac)) { #ifdef IEEE80211_DEBUG_REFCNT - ni = ieee80211_find_node_locked_debug(nt, mac, func, line); + ieee80211_ref_node_debug(ni, func, line); #else - ni = ieee80211_find_node_locked(nt, mac); + ieee80211_ref_node(ni); #endif + goto found; + } + } + ni = NULL; +found: IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt); if (ni == NULL) { @@ -1964,13 +2016,32 @@ remove_worse_nodes(void *arg, struct iee } } +static void +remove_duplicate_nodes(void *arg, struct ieee80211_node *ni) +{ + struct ieee80211_node *rni = arg; + + if (ni == rni) + return; + + if (ni->ni_vap == rni->ni_vap) + return; + + if (!IEEE80211_ADDR_EQ(rni->ni_macaddr, ni->ni_macaddr)) + return; + + ieee80211_node_leave(ni); +} + void ieee80211_node_join(struct ieee80211_node *ni, int resp) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_node *tni; int newassoc; + ieee80211_iterate_nodes(&ic->ic_sta, remove_duplicate_nodes, ni); if (ni->ni_associd == 0) { u_int16_t aid; --- a/net80211/ieee80211_input.c +++ b/net80211/ieee80211_input.c @@ -216,16 +216,14 @@ ieee80211_input(struct ieee80211vap * va type = -1; /* undefined */ - if (!vap) - goto out; + if (!vap || !vap->iv_bss || !vap->iv_dev || !vap->iv_ic) + goto discard; ic = vap->iv_ic; - if (!ic) - goto out; - dev = vap->iv_dev; - if (!dev) - goto out; + + if ((vap->iv_dev->flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) + goto discard; /* initialize ni as in the previous API */ if (ni_or_null == NULL) { @@ -233,9 +231,10 @@ ieee80211_input(struct ieee80211vap * va * guarantee its existence during the following call, hence * briefly grab our own reference. */ ni = ieee80211_ref_node(vap->iv_bss); + KASSERT(ni != NULL, ("null node")); + } else { + ni->ni_inact = ni->ni_inact_reload; } - KASSERT(ni != NULL, ("null node")); - ni->ni_inact = ni->ni_inact_reload; KASSERT(skb->len >= sizeof(struct ieee80211_frame_min), ("frame length too short: %u", skb->len)); @@ -844,10 +843,11 @@ ieee80211_input(struct ieee80211vap * va err: vap->iv_devstats.rx_errors++; out: - if (skb != NULL) - ieee80211_dev_kfree_skb(&skb); if (ni_or_null == NULL) ieee80211_unref_node(&ni); +discard: + if (skb != NULL) + ieee80211_dev_kfree_skb(&skb); return type; #undef HAS_SEQ } @@ -929,16 +929,23 @@ int ieee80211_input_all(struct ieee80211com *ic, struct sk_buff *skb, int rssi, u_int64_t rtsf) { + struct ieee80211_frame_min *wh = (struct ieee80211_frame_min *) skb->data; struct ieee80211vap *vap; int type = -1; /* XXX locking */ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + struct ieee80211_node *ni = NULL; struct sk_buff *skb1; if ((vap->iv_dev->flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) continue; + if ((vap->iv_opmode == IEEE80211_M_HOSTAP) && + !IEEE80211_IS_MULTICAST(wh->i_addr1)) + continue; + + ni = ieee80211_find_rxnode(ic, vap, wh); if (TAILQ_NEXT(vap, iv_next) != NULL) { skb1 = skb_copy(skb, GFP_ATOMIC); if (skb1 == NULL) { @@ -950,8 +957,12 @@ ieee80211_input_all(struct ieee80211com skb1 = skb; skb = NULL; } - type = ieee80211_input(vap, NULL, skb1, rssi, rtsf); + type = ieee80211_input(vap, ni, skb1, rssi, rtsf); + if (ni) + ieee80211_unref_node(&ni); } + +out: if (skb != NULL) /* no vaps, reclaim skb */ ieee80211_dev_kfree_skb(&skb); return type; @@ -1146,11 +1157,9 @@ ieee80211_deliver_data(struct ieee80211_ * sending it will not work; just let it be * delivered normally. */ - struct ieee80211_node *ni1 = ieee80211_find_node( - &vap->iv_ic->ic_sta, eh->ether_dhost); + struct ieee80211_node *ni1 = ieee80211_find_txnode(vap, eh->ether_dhost); if (ni1 != NULL) { - if (ni1->ni_vap == vap && - ieee80211_node_is_authorized(ni1) && + if (ieee80211_node_is_authorized(ni1) && !ni1->ni_subif && ni1 != vap->iv_bss) { @@ -3519,6 +3528,7 @@ ieee80211_recv_mgmt(struct ieee80211vap (vap->iv_opmode == IEEE80211_M_WDS)) && (scan.capinfo & IEEE80211_CAPINFO_ESS))) { struct ieee80211vap *avp = NULL; + int do_unref = 0; int found = 0; IEEE80211_LOCK_IRQ(vap->iv_ic); @@ -3550,10 +3560,12 @@ ieee80211_recv_mgmt(struct ieee80211vap ni->ni_associd |= 0xc000; avp->iv_wdsnode = ieee80211_ref_node(ni); IEEE80211_UNLOCK_IRQ(ic); - } else if (vap->iv_opmode == IEEE80211_M_IBSS) { + } else if ((vap->iv_opmode == IEEE80211_M_IBSS) && + IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bssid)) { /* Create a new entry in the neighbor table. */ ni = ieee80211_add_neighbor(vap, wh, &scan); } + do_unref = 1; } else { /* * Copy data from beacon to neighbor table. @@ -3592,6 +3604,8 @@ ieee80211_recv_mgmt(struct ieee80211vap ni->ni_rssi = rssi; ni->ni_rtsf = rtsf; ni->ni_last_rx = jiffies; + if (do_unref) + ieee80211_unref_node(&ni); } } break; --- a/ath/if_ath.c +++ b/ath/if_ath.c @@ -6589,9 +6589,8 @@ ath_recv_mgmt(struct ieee80211vap * vap, sc->sc_recv_mgmt(vap, ni_or_null, skb, subtype, rssi, rtsf); - /* Lookup the new node if any (this grabs a reference to it) */ - ni = ieee80211_find_rxnode(vap->iv_ic, + ni = ieee80211_find_rxnode(vap->iv_ic, vap, (const struct ieee80211_frame_min *)skb->data); if (ni == NULL) { DPRINTF(sc, ATH_DEBUG_BEACON, "Dropping; node unknown.\n"); @@ -6746,7 +6745,9 @@ ath_rx_poll(struct net_device *dev, int struct ath_desc *ds; struct ath_rx_status *rs; struct sk_buff *skb = NULL; + struct ieee80211vap *vap; struct ieee80211_node *ni; + const struct ieee80211_frame_min *wh; unsigned int len; int type; u_int phyerr; @@ -6901,12 +6902,15 @@ rx_accept: skb_trim(skb, skb->len - IEEE80211_CRC_LEN); if (mic_fail) { + wh = (const struct ieee80211_frame_min *) skb->data; + /* Ignore control frames which are reported with mic error */ - if ((((struct ieee80211_frame *)skb->data)->i_fc[0] & + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_CTL) goto drop_micfail; - ni = ieee80211_find_rxnode(ic, (const struct ieee80211_frame_min *) skb->data); + vap = ieee80211_find_rxvap(ic, wh->i_addr1); + ni = ieee80211_find_rxnode(ic, vap, wh); if (ni && ni->ni_table) { ieee80211_check_mic(ni, skb); @@ -6968,11 +6972,24 @@ drop_micfail: * for its use. If the sender is unknown spam the * frame; it'll be dropped where it's not wanted. */ - if (rs->rs_keyix != HAL_RXKEYIX_INVALID && + wh = (const struct ieee80211_frame_min *) skb->data; + if ((rs->rs_keyix != HAL_RXKEYIX_INVALID) && (ni = sc->sc_keyixmap[rs->rs_keyix]) != NULL) { /* Fast path: node is present in the key map; * grab a reference for processing the frame. */ - ni = ieee80211_ref_node(ni); + ieee80211_ref_node(ni); + if ((ATH_GET_VAP_ID(wh->i_addr1) != + ATH_GET_VAP_ID(ni->ni_vap->iv_myaddr)) || + ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == + IEEE80211_FC1_DIR_DSTODS)) { + /* key cache node lookup is fast, but it can + * lead to problems in multi-bss (foreign vap + * node reference) or wds (wdsap node ref instead + * of base ap node ref). + * use slowpath lookup in both cases + */ + goto lookup_slowpath; + } ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi); type = ieee80211_input(ni->ni_vap, ni, skb, rs->rs_rssi, bf->bf_tsf); ieee80211_unref_node(&ni); @@ -6981,24 +6998,39 @@ drop_micfail: * No key index or no entry, do a lookup and * add the node to the mapping table if possible. */ - ni = ieee80211_find_rxnode(ic, - (const struct ieee80211_frame_min *)skb->data); + +lookup_slowpath: + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) + vap = NULL; + else + vap = ieee80211_find_rxvap(ic, wh->i_addr1); + + if (vap) + ni = ieee80211_find_rxnode(ic, vap, wh); + else + ni = NULL; + if (ni != NULL) { ieee80211_keyix_t keyix; ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi); - type = ieee80211_input(ni->ni_vap, ni, skb, rs->rs_rssi, bf->bf_tsf); + type = ieee80211_input(vap, ni, skb, rs->rs_rssi, bf->bf_tsf); /* * If the station has a key cache slot assigned * update the key->node mapping table. */ keyix = ni->ni_ucastkey.wk_keyix; if (keyix != IEEE80211_KEYIX_NONE && - sc->sc_keyixmap[keyix] == NULL) + sc->sc_keyixmap[keyix] == NULL) { sc->sc_keyixmap[keyix] = ieee80211_ref_node(ni); + } ieee80211_unref_node(&ni); - } else - type = ieee80211_input_all(ic, skb, rs->rs_rssi, bf->bf_tsf); + } else { + if (vap) + type = ieee80211_input(vap, NULL, skb, rs->rs_rssi, bf->bf_tsf); + else + type = ieee80211_input_all(ic, skb, rs->rs_rssi, bf->bf_tsf); + } } if (sc->sc_diversity) { --- a/net80211/ieee80211_node.h +++ b/net80211/ieee80211_node.h @@ -286,15 +286,18 @@ struct ieee80211_node *ieee80211_find_no const u_int8_t *); #endif /* #ifdef IEEE80211_DEBUG_REFCNT */ +struct ieee80211vap * +ieee80211_find_rxvap(struct ieee80211com *ic, const u_int8_t *mac); + /* Returns a ieee80211_node* with refcount incremented, if found */ #ifdef IEEE80211_DEBUG_REFCNT -#define ieee80211_find_rxnode(_nt, _wh) \ - ieee80211_find_rxnode_debug(_nt, _wh, __func__, __LINE__) +#define ieee80211_find_rxnode(_nt, _vap, _wh) \ + ieee80211_find_rxnode_debug(_nt, _vap, _wh, __func__, __LINE__) struct ieee80211_node *ieee80211_find_rxnode_debug(struct ieee80211com *, - const struct ieee80211_frame_min *, const char *, int); + struct ieee80211vap *, const struct ieee80211_frame_min *, const char *, int); #else struct ieee80211_node *ieee80211_find_rxnode(struct ieee80211com *, - const struct ieee80211_frame_min *); + struct ieee80211vap *, const struct ieee80211_frame_min *); #endif /* #ifdef IEEE80211_DEBUG_REFCNT */ /* Returns a ieee80211_node* with refcount incremented, if found */