1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2024-11-30 21:58:07 +02:00
openwrt-xburst/package/madwifi/patches/393-mbss_vap_auth.patch
nbd c941f1cfbc madwifi: fix some potential null pointer derefs with wds
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@19322 3c298f89-4303-0410-b956-a3cf2f4a3e73
2010-01-24 23:39:00 +00:00

492 lines
15 KiB
Diff

--- 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;
@@ -1147,11 +1158,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) {
@@ -3520,6 +3529,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);
@@ -3553,10 +3563,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.
@@ -3595,6 +3607,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 */