From e7a16f463a691ca1e53a2812654e2e054abe9937 Mon Sep 17 00:00:00 2001
From: nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73>
Date: Sun, 25 Jan 2009 00:37:41 +0000
Subject: madwifi: fix the long standing bug that is triggered by nodes getting
 a timeout on one vap, then moving to another

git-svn-id: svn://svn.openwrt.org/openwrt/trunk@14171 3c298f89-4303-0410-b956-a3cf2f4a3e73
---
 package/madwifi/patches/391-vap_auth.patch         |  29 ++
 .../patches/392-remove_wds_nodetracking.patch      | 388 +++++++++++++++++++
 package/madwifi/patches/393-mbss_vap_auth.patch    | 423 +++++++++++++++++++++
 package/madwifi/patches/394-probereq.patch         |   2 +-
 package/madwifi/patches/405-retransmit_check.patch |   2 +-
 package/madwifi/patches/450-new_hal.patch          |   2 +-
 6 files changed, 843 insertions(+), 3 deletions(-)
 create mode 100644 package/madwifi/patches/391-vap_auth.patch
 create mode 100644 package/madwifi/patches/392-remove_wds_nodetracking.patch
 create mode 100644 package/madwifi/patches/393-mbss_vap_auth.patch

diff --git a/package/madwifi/patches/391-vap_auth.patch b/package/madwifi/patches/391-vap_auth.patch
new file mode 100644
index 000000000..acee271c3
--- /dev/null
+++ b/package/madwifi/patches/391-vap_auth.patch
@@ -0,0 +1,29 @@
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -1374,7 +1386,7 @@ ieee80211_auth_open(struct ieee80211_nod
+ 		vap->iv_stats.is_rx_bad_auth++;	/* XXX maybe a unique error? */
+ 		if (vap->iv_opmode == IEEE80211_M_HOSTAP) {
+ 			if (ni == vap->iv_bss) {
+-				ni = ieee80211_dup_bss(vap, wh->i_addr2, 0);
++				ni = ieee80211_dup_bss(vap, wh->i_addr2, 1);
+ 				if (ni == NULL)
+ 					return;
+ 				tmpnode = 1;
+@@ -1762,6 +1774,8 @@ ieee80211_ssid_mismatch(struct ieee80211
+ }
+ 
+ #define	IEEE80211_VERIFY_SSID(_ni, _ssid) do {				\
++	if ((_ni)->ni_esslen == 0)					\
++		return;							\
+ 	if ((_ssid)[1] != 0 &&						\
+ 	    ((_ssid)[1] != (_ni)->ni_esslen ||				\
+ 	    memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) {	\
+@@ -1776,6 +1790,8 @@ ieee80211_ssid_mismatch(struct ieee80211
+ } while (0)
+ #else /* !IEEE80211_DEBUG */
+ #define	IEEE80211_VERIFY_SSID(_ni, _ssid) do {				\
++	if ((_ni)->ni_esslen == 0)					\
++		return;							\
+ 	if ((_ssid)[1] != 0 &&						\
+ 	    ((_ssid)[1] != (_ni)->ni_esslen ||				\
+ 	    memcmp((_ssid) + 2, (_ni)->ni_essid, (_ssid)[1]) != 0)) {	\
diff --git a/package/madwifi/patches/392-remove_wds_nodetracking.patch b/package/madwifi/patches/392-remove_wds_nodetracking.patch
new file mode 100644
index 000000000..d035fd615
--- /dev/null
+++ b/package/madwifi/patches/392-remove_wds_nodetracking.patch
@@ -0,0 +1,388 @@
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -572,36 +572,6 @@ ieee80211_input(struct ieee80211vap * va
+ 				}
+ 			}
+ 
+-			/* XXX: Useless node mgmt API; make better */
+-			if ((dir == IEEE80211_FC1_DIR_DSTODS) && !vap->iv_wdsnode &&
+-					!ni_wds && !ni->ni_subif) {
+-				struct ieee80211_node_table *nt = &ic->ic_sta;
+-				struct ieee80211_frame_addr4 *wh4;
+-
+-				if (!(vap->iv_flags_ext & IEEE80211_FEXT_WDS)) {
+-					IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
+-						wh, "data", "%s", "4 addr not allowed");
+-					goto err;
+-				}
+-				wh4 = (struct ieee80211_frame_addr4 *)skb->data;
+-				ni_wds = ieee80211_find_wds_node(nt, wh4->i_addr4);
+-				/* Last call increments ref count if !NULL */
+-				if ((ni_wds != NULL) && (ni_wds != ni)) {
+-					/*
+-					 * node with source address (addr4) moved
+-					 * to another WDS capable station. remove the
+-					 * reference to the previous station and add 
+-					 * reference to the new one
+-					 */
+-					 (void) ieee80211_remove_wds_addr(nt, wh4->i_addr4);
+-					 ieee80211_add_wds_addr(nt, ni, wh4->i_addr4, 0);
+-				}
+-				if (ni_wds == NULL)
+-					ieee80211_add_wds_addr(nt, ni, wh4->i_addr4, 0);
+-				else
+-					ieee80211_unref_node(&ni_wds);
+-			}
+-
+ 			/*
+ 			 * Check for power save state change.
+ 			 */
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -122,7 +122,6 @@ static void ieee80211_node_table_init(st
+ static void ieee80211_node_table_cleanup(struct ieee80211_node_table *);
+ static void ieee80211_node_table_reset(struct ieee80211_node_table *,
+ 	struct ieee80211vap *);
+-static void ieee80211_node_wds_ageout(unsigned long);
+ 
+ MALLOC_DEFINE(M_80211_NODE, "80211node", "802.11 node state");
+ 
+@@ -785,10 +784,6 @@ ieee80211_node_table_init(struct ieee802
+ 	nt->nt_name = name;
+ 	nt->nt_scangen = 1;
+ 	nt->nt_inact_init = inact;
+-	init_timer(&nt->nt_wds_aging_timer);
+-	nt->nt_wds_aging_timer.function = ieee80211_node_wds_ageout;
+-	nt->nt_wds_aging_timer.data = (unsigned long) nt;
+-	mod_timer(&nt->nt_wds_aging_timer, jiffies + HZ * WDS_AGING_TIMER_VAL);
+ }
+ 
+ static __inline 
+@@ -1201,142 +1196,6 @@ void ieee80211_wds_addif(struct ieee8021
+ 	schedule_work(&ni->ni_create);
+ }
+ 
+-/* Add wds address to the node table */
+-int
+-#ifdef IEEE80211_DEBUG_REFCNT
+-ieee80211_add_wds_addr_debug(struct ieee80211_node_table *nt,
+-	struct ieee80211_node *ni, const u_int8_t *macaddr, u_int8_t wds_static,
+-	const char* func, int line)
+-#else
+-ieee80211_add_wds_addr(struct ieee80211_node_table *nt,
+-	struct ieee80211_node *ni, const u_int8_t *macaddr, u_int8_t wds_static)
+-#endif
+-{
+-	int hash;
+-	struct ieee80211_wds_addr *wds;
+-
+-	MALLOC(wds, struct ieee80211_wds_addr *, sizeof(struct ieee80211_wds_addr),
+-		M_80211_WDS, M_NOWAIT | M_ZERO);
+-	if (wds == NULL) {
+-		/* XXX msg */
+-		return 1;
+-	}
+-	if (wds_static)
+-		wds->wds_agingcount = WDS_AGING_STATIC;
+-	else
+-		wds->wds_agingcount = WDS_AGING_COUNT;
+-	hash = IEEE80211_NODE_HASH(macaddr);
+-	IEEE80211_ADDR_COPY(wds->wds_macaddr, macaddr);
+-
+-	IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
+-#ifdef IEEE80211_DEBUG_REFCNT
+-	wds->wds_ni = ieee80211_ref_node_debug(ni, func, line);
+-#else
+-	wds->wds_ni = ieee80211_ref_node(ni);
+-#endif
+-	LIST_INSERT_HEAD(&nt->nt_wds_hash[hash], wds, wds_hash);
+-	IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
+-	return 0;
+-}
+-#ifdef IEEE80211_DEBUG_REFCNT
+-EXPORT_SYMBOL(ieee80211_add_wds_addr_debug);
+-#else
+-EXPORT_SYMBOL(ieee80211_add_wds_addr);
+-#endif
+-
+-/* remove wds address from the wds hash table */
+-void
+-#ifdef IEEE80211_DEBUG_REFCNT
+-ieee80211_remove_wds_addr_debug(struct ieee80211_node_table *nt, const u_int8_t *macaddr, 
+-			   const char* func, int line)
+-#else
+-ieee80211_remove_wds_addr(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
+-#endif
+-{
+-	int hash;
+-	struct ieee80211_wds_addr *wds, *twds;
+-
+-	hash = IEEE80211_NODE_HASH(macaddr);
+-	IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
+-	LIST_FOREACH_SAFE(wds, &nt->nt_wds_hash[hash], wds_hash, twds) {
+-		if (IEEE80211_ADDR_EQ(wds->wds_macaddr, macaddr)) {
+-			LIST_REMOVE(wds, wds_hash);
+-#ifdef IEEE80211_DEBUG_REFCNT
+-			ieee80211_unref_node_debug(&wds->wds_ni, func, line);
+-#else
+-			ieee80211_unref_node(&wds->wds_ni);
+-#endif
+-			FREE(wds, M_80211_WDS);
+-			break;
+-		}
+-	}
+-	IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
+-}
+-#ifdef IEEE80211_DEBUG_REFCNT
+-EXPORT_SYMBOL(ieee80211_remove_wds_addr_debug);
+-#else
+-EXPORT_SYMBOL(ieee80211_remove_wds_addr);
+-#endif
+-
+-/* Remove node references from wds table */
+-void
+-#ifdef IEEE80211_DEBUG_REFCNT
+-ieee80211_del_wds_node_debug(struct ieee80211_node_table *nt, struct ieee80211_node *ni, 
+-			const char* func, int line)
+-#else
+-ieee80211_del_wds_node(struct ieee80211_node_table *nt, struct ieee80211_node *ni)
+-#endif
+-{
+-	int hash;
+-	struct ieee80211_wds_addr *wds, *twds;
+-
+-	IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
+-	for (hash = 0; hash < IEEE80211_NODE_HASHSIZE; hash++) {
+-		LIST_FOREACH_SAFE(wds, &nt->nt_wds_hash[hash], wds_hash, twds) {
+-			if (wds->wds_ni == ni) {
+-				LIST_REMOVE(wds, wds_hash);
+-#ifdef IEEE80211_DEBUG_REFCNT
+-				ieee80211_unref_node_debug(&wds->wds_ni, func, line);
+-#else
+-				ieee80211_unref_node(&wds->wds_ni);
+-#endif
+-				FREE(wds, M_80211_WDS);
+-			}
+-		}
+-	}
+-	IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
+-}
+-#ifdef IEEE80211_DEBUG_REFCNT
+-EXPORT_SYMBOL(ieee80211_del_wds_node_debug);
+-#else
+-EXPORT_SYMBOL(ieee80211_del_wds_node);
+-#endif
+-
+-static void
+-ieee80211_node_wds_ageout(unsigned long data)
+-{
+-	struct ieee80211_node_table *nt = (struct ieee80211_node_table *)data;
+-	int hash;
+-	struct ieee80211_wds_addr *wds, *twds;
+-
+-	IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
+-	for (hash = 0; hash < IEEE80211_NODE_HASHSIZE; hash++) {
+-		LIST_FOREACH_SAFE(wds, &nt->nt_wds_hash[hash], wds_hash, twds) {
+-			if (wds->wds_agingcount != WDS_AGING_STATIC) {
+-				if (!wds->wds_agingcount) {
+-					LIST_REMOVE(wds, wds_hash);
+-					ieee80211_unref_node(&wds->wds_ni);
+-					FREE(wds, M_80211_WDS);
+-				} else
+-					wds->wds_agingcount--;
+-			}
+-		}
+-	}
+-	IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
+-	mod_timer(&nt->nt_wds_aging_timer, jiffies + HZ * WDS_AGING_TIMER_VAL);
+-}
+-
+-
+ /* Add the specified station to the station table.
+  * Allocates a new ieee80211_node* that has a reference count of one
+  * If tmp is 0, it is added to the node table and the reference is used.
+@@ -1382,34 +1241,6 @@ ieee80211_dup_bss(struct ieee80211vap *v
+ 	return ni;
+ }
+ 
+-static struct ieee80211_node *
+-#ifdef IEEE80211_DEBUG_REFCNT
+-ieee80211_find_wds_node_locked_debug(struct ieee80211_node_table *nt, 
+-			 const u_int8_t *macaddr, const char* func, int line)
+-#else
+-ieee80211_find_wds_node_locked(struct ieee80211_node_table *nt, 
+-			 const u_int8_t *macaddr)
+-#endif
+-{
+-	struct ieee80211_wds_addr *wds;
+-	int hash;
+-	IEEE80211_NODE_TABLE_LOCK_ASSERT(nt);
+-
+-	hash = IEEE80211_NODE_HASH(macaddr);
+-	LIST_FOREACH(wds, &nt->nt_wds_hash[hash], wds_hash) {
+-		if (IEEE80211_ADDR_EQ(wds->wds_macaddr, macaddr)) {
+-			if (wds->wds_agingcount != WDS_AGING_STATIC)
+-				wds->wds_agingcount = WDS_AGING_COUNT; /* reset the aging count */
+-#ifdef IEEE80211_DEBUG_REFCNT
+-			return ieee80211_ref_node_debug(wds->wds_ni, func, line);
+-#else
+-			return ieee80211_ref_node(wds->wds_ni);
+-#endif
+-		}
+-	}
+-	return NULL;
+-}
+-
+ /* NB: A node reference is acquired here; the caller MUST release it. */
+ #ifdef IEEE80211_DEBUG_REFCNT
+ #define	ieee80211_find_node_locked(nt, mac) \
+@@ -1427,7 +1258,6 @@ ieee80211_find_node_locked(struct ieee80
+ {
+ 	struct ieee80211_node *ni;
+ 	int hash;
+-	struct ieee80211_wds_addr *wds;
+ 
+ 	IEEE80211_NODE_TABLE_LOCK_ASSERT(nt);
+ 
+@@ -1442,48 +1272,11 @@ ieee80211_find_node_locked(struct ieee80
+ 			return ni;
+ 		}
+ 	}
+-
+-	/* Now, we look for the desired mac address in the 4 address
+-	   nodes. */
+-	LIST_FOREACH(wds, &nt->nt_wds_hash[hash], wds_hash) {
+-		if (IEEE80211_ADDR_EQ(wds->wds_macaddr, macaddr)) {
+-#ifdef IEEE80211_DEBUG_REFCNT
+-			return ieee80211_ref_node_debug(wds->wds_ni, func, line);
+-#else
+-			return ieee80211_ref_node(wds->wds_ni);
+-#endif 
+-		}
+-	}
+ 	return NULL;
+ }
+ 
+ struct ieee80211_node *
+ #ifdef IEEE80211_DEBUG_REFCNT
+-ieee80211_find_wds_node_debug(struct ieee80211_node_table *nt, const u_int8_t *macaddr, 
+-			 const char* func, int line)
+-#else
+-ieee80211_find_wds_node(struct ieee80211_node_table *nt, const u_int8_t *macaddr)
+-#endif
+-{
+-	struct ieee80211_node *ni;
+-
+-	IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
+-#ifdef IEEE80211_DEBUG_REFCNT
+-	ni = ieee80211_find_wds_node_locked_debug(nt, macaddr, func, line);
+-#else
+-	ni = ieee80211_find_wds_node_locked(nt, macaddr);
+-#endif
+-	IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
+-	return ni;
+-}
+-#ifdef IEEE80211_DEBUG_REFCNT
+-EXPORT_SYMBOL(ieee80211_find_wds_node_debug);
+-#else
+-EXPORT_SYMBOL(ieee80211_find_wds_node);
+-#endif
+-
+-struct ieee80211_node *
+-#ifdef IEEE80211_DEBUG_REFCNT
+ ieee80211_find_node_debug(struct ieee80211_node_table *nt,
+ 	const u_int8_t *macaddr, const char *func, int line)
+ #else
+@@ -1835,7 +1628,6 @@ ieee80211_node_table_cleanup(struct ieee
+ 		ic->ic_node_cleanup(ni);
+ #endif
+ 	}
+-	del_timer(&nt->nt_wds_aging_timer);
+ 	IEEE80211_SCAN_LOCK_DESTROY(nt);
+ 	IEEE80211_NODE_TABLE_LOCK_DESTROY(nt);
+ }
+@@ -2402,8 +2194,6 @@ ieee80211_node_leave(struct ieee80211_no
+ 	 * so no more references are generated
+ 	 */
+ 	if (nt) {
+-		ieee80211_remove_wds_addr(nt, ni->ni_macaddr);
+-		ieee80211_del_wds_node(nt, ni);
+ 		IEEE80211_NODE_TABLE_LOCK_IRQ(nt);
+ 		node_table_leave_locked(nt, ni);
+ 		IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt);
+--- a/net80211/ieee80211_node.h
++++ b/net80211/ieee80211_node.h
+@@ -231,13 +231,6 @@ void ieee80211_sta_leave(struct ieee8021
+ #define WDS_AGING_STATIC 	0xffff
+ #define WDS_AGING_TIMER_VAL 	(WDS_AGING_TIME / 2)
+ 
+-struct ieee80211_wds_addr {
+-	LIST_ENTRY(ieee80211_wds_addr) wds_hash;
+-	u_int8_t	wds_macaddr[IEEE80211_ADDR_LEN];
+-	struct ieee80211_node *wds_ni;
+-	u_int16_t wds_agingcount;
+-};
+-
+ /*
+  * Table of ieee80211_node instances.  Each ieee80211com
+  * has at least one for holding the scan candidates.
+@@ -250,11 +243,9 @@ struct ieee80211_node_table {
+ 	ieee80211_node_table_lock_t nt_nodelock;	/* on node table */
+ 	TAILQ_HEAD(, ieee80211_node) nt_node;	/* information of all nodes */
+ 	ATH_LIST_HEAD(, ieee80211_node) nt_hash[IEEE80211_NODE_HASHSIZE];
+-	ATH_LIST_HEAD(, ieee80211_wds_addr) nt_wds_hash[IEEE80211_NODE_HASHSIZE];
+ 	ieee80211_scan_lock_t nt_scanlock;	/* on nt_scangen */
+ 	u_int nt_scangen;			/* gen# for timeout scan */
+ 	int nt_inact_init;			/* initial node inact setting */
+-	struct timer_list nt_wds_aging_timer;	/* timer to age out wds entries */
+ };
+ 
+ /* Allocates a new ieee80211_node* that has a reference count of one, and 
+@@ -363,47 +354,6 @@ void
+ ieee80211_unref_node(struct ieee80211_node **pni);
+ #endif /* #ifdef IEEE80211_DEBUG_REFCNT */
+ 
+-/* Increments reference count of ieee80211_node *ni */
+-#ifdef IEEE80211_DEBUG_REFCNT
+-#define ieee80211_add_wds_addr(_table, _node, _mac, _static) \
+-	ieee80211_add_wds_addr_debug(_table, _node, _mac, _static, __func__, __LINE__)
+-int ieee80211_add_wds_addr_debug(struct ieee80211_node_table *, struct ieee80211_node *,
+-	const u_int8_t *, u_int8_t, const char* func, int line);
+-#else
+-int ieee80211_add_wds_addr(struct ieee80211_node_table *, struct ieee80211_node *,
+-	const u_int8_t *, u_int8_t);
+-#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
+-
+-/* Decrements reference count of ieee80211_node *ni */
+-#ifdef IEEE80211_DEBUG_REFCNT
+-#define ieee80211_remove_wds_addr(_table, _mac) \
+-	ieee80211_remove_wds_addr_debug(_table, _mac, __func__, __LINE__)
+-void ieee80211_remove_wds_addr_debug(struct ieee80211_node_table *, const u_int8_t *,
+-			       const char* func, int line);
+-#else
+-void ieee80211_remove_wds_addr(struct ieee80211_node_table *, const u_int8_t *);
+-#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
+-
+-/* Decrements reference count of node, if found */
+-#ifdef IEEE80211_DEBUG_REFCNT
+-#define ieee80211_del_wds_node(_table, _node) \
+-	ieee80211_del_wds_node_debug(_table, _node, __func__, __LINE__)
+-void ieee80211_del_wds_node_debug(struct ieee80211_node_table *, struct ieee80211_node *,
+-			    const char* func, int line);
+-#else
+-void ieee80211_del_wds_node(struct ieee80211_node_table *, struct ieee80211_node *);
+-#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
+-
+-/* Increments reference count of node, if found */
+-#ifdef IEEE80211_DEBUG_REFCNT
+-#define ieee80211_find_wds_node(_table, _mac) \
+-	ieee80211_find_wds_node_debug(_table, _mac, __func__, __LINE__)
+-struct ieee80211_node *ieee80211_find_wds_node_debug(struct ieee80211_node_table *,
+-	const u_int8_t *, const char* func, int line);
+-#else
+-struct ieee80211_node *ieee80211_find_wds_node(struct ieee80211_node_table *,
+-	const u_int8_t *);
+-#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
+ typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
+ void ieee80211_iterate_nodes(struct ieee80211_node_table *,
+ 	ieee80211_iter_func *, void *);
diff --git a/package/madwifi/patches/393-mbss_vap_auth.patch b/package/madwifi/patches/393-mbss_vap_auth.patch
new file mode 100644
index 000000000..48d3df3e1
--- /dev/null
+++ b/package/madwifi/patches/393-mbss_vap_auth.patch
@@ -0,0 +1,423 @@
+--- 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,
+@@ -1391,6 +1394,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 
+@@ -1400,10 +1450,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
+ {
+@@ -1411,9 +1461,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)*/)
+@@ -1426,32 +1475,21 @@ 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;
+ 	}
+ 
+-#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);
+@@ -1476,15 +1514,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
+@@ -1502,11 +1539,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) {
+@@ -1961,13 +2009,29 @@ 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;
++
++	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
+@@ -227,15 +227,22 @@ ieee80211_input(struct ieee80211vap * va
+ 	if (!dev)
+ 		goto out;
+ 
++	if ((vap->iv_dev->flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
++		goto out;
++
++	if (!vap->iv_bss)
++		goto out;
++
+ 	/* initialize ni as in the previous API */
+ 	if (ni_or_null == NULL) {
+                /* This function does not 'own' vap->iv_bss, so we cannot
+                 * 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));
+@@ -933,16 +940,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) {
+@@ -954,8 +968,10 @@ 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);
+ 	}
++
++out:
+ 	if (skb != NULL)		/* no vaps, reclaim skb */
+ 		ieee80211_dev_kfree_skb(&skb);
+ 	return type;
+@@ -1146,11 +1162,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) {
+ 
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -6577,9 +6577,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");
+@@ -6734,7 +6733,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;
+@@ -6889,12 +6890,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);
+@@ -6956,11 +6960,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);
+@@ -6969,24 +6986,35 @@ 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:
++			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 */
diff --git a/package/madwifi/patches/394-probereq.patch b/package/madwifi/patches/394-probereq.patch
index fc8cda0d5..1dd0bcd13 100644
--- a/package/madwifi/patches/394-probereq.patch
+++ b/package/madwifi/patches/394-probereq.patch
@@ -1,6 +1,6 @@
 --- a/net80211/ieee80211_input.c
 +++ b/net80211/ieee80211_input.c
-@@ -3604,6 +3604,8 @@ ieee80211_recv_mgmt(struct ieee80211vap 
+@@ -3618,6 +3618,8 @@ ieee80211_recv_mgmt(struct ieee80211vap 
  			vap->iv_stats.is_rx_mgtdiscard++;
  			return;
  		}
diff --git a/package/madwifi/patches/405-retransmit_check.patch b/package/madwifi/patches/405-retransmit_check.patch
index b03232ad5..dadb4239c 100644
--- a/package/madwifi/patches/405-retransmit_check.patch
+++ b/package/madwifi/patches/405-retransmit_check.patch
@@ -11,7 +11,7 @@
  #define	IEEE80211_QOS_TXOP			0x00ff
 --- a/net80211/ieee80211_input.c
 +++ b/net80211/ieee80211_input.c
-@@ -429,7 +429,7 @@ ieee80211_input(struct ieee80211vap * va
+@@ -436,7 +436,7 @@ ieee80211_input(struct ieee80211vap * va
  				tid = 0;
  			rxseq = le16toh(*(__le16 *)wh->i_seq);
  			if ((wh->i_fc[1] & IEEE80211_FC1_RETRY) &&
diff --git a/package/madwifi/patches/450-new_hal.patch b/package/madwifi/patches/450-new_hal.patch
index 324f7dd6b..7494f0d4a 100644
--- a/package/madwifi/patches/450-new_hal.patch
+++ b/package/madwifi/patches/450-new_hal.patch
@@ -108,7 +108,7 @@
  	/*
  	 * Check if the MAC has multi-rate retry support.
  	 * We do this by trying to setup a fake extended
-@@ -7524,7 +7532,7 @@ ath_txq_setup(struct ath_softc *sc, int 
+@@ -7552,7 +7560,7 @@ ath_txq_setup(struct ath_softc *sc, int 
  	if (qtype == HAL_TX_QUEUE_UAPSD)
  		qi.tqi_qflags = HAL_TXQ_TXDESCINT_ENABLE;
  	else
-- 
cgit v1.2.3