From f78a2712af346531cb3246884f692d884304286f Mon Sep 17 00:00:00 2001 From: nbd Date: Sun, 3 Aug 2008 21:00:34 +0000 Subject: madwifi: more fixes and cleanups for wds sta separation git-svn-id: svn://svn.openwrt.org/openwrt/trunk@12082 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- .../madwifi/patches/371-wds_sta_separation.patch | 379 +++++++++++++++++++-- 1 file changed, 357 insertions(+), 22 deletions(-) (limited to 'package/madwifi/patches/371-wds_sta_separation.patch') diff --git a/package/madwifi/patches/371-wds_sta_separation.patch b/package/madwifi/patches/371-wds_sta_separation.patch index e01b1502b5..57b62b089b 100644 --- a/package/madwifi/patches/371-wds_sta_separation.patch +++ b/package/madwifi/patches/371-wds_sta_separation.patch @@ -8,12 +8,78 @@ struct ieee80211_frame *wh; struct ieee80211_key *key; struct ether_header *eh; -@@ -562,11 +563,14 @@ +@@ -435,7 +436,7 @@ + + switch (type) { + case IEEE80211_FC0_TYPE_DATA: +- hdrspace = ieee80211_hdrspace(ic, wh); ++ hdrspace = ieee80211_hdrsize(wh); + if (skb->len < hdrspace) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, "data", "too short: len %u, expecting %u", +@@ -446,15 +447,20 @@ + switch (vap->iv_opmode) { + case IEEE80211_M_STA: + if ((dir != IEEE80211_FC1_DIR_FROMDS) && +- (!((vap->iv_flags_ext & IEEE80211_FEXT_WDS) && +- (dir == IEEE80211_FC1_DIR_DSTODS)))) { ++ (!(vap->iv_flags_ext & IEEE80211_FEXT_WDS) && ++ (dir == IEEE80211_FC1_DIR_DSTODS))) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, "data", "invalid dir 0x%x", dir); + vap->iv_stats.is_rx_wrongdir++; + goto out; + } + +- if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { ++ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { ++ /* ignore 3-addr mcast if we're WDS STA */ ++ if ((vap->iv_flags_ext & IEEE80211_FEXT_WDS) && ++ (dir != IEEE80211_FC1_DIR_DSTODS)) ++ goto out; ++ + /* Discard multicast if IFF_MULTICAST not set */ + if ((0 != memcmp(wh->i_addr3, dev->broadcast, ETH_ALEN)) && + (0 == (dev->flags & IFF_MULTICAST))) { +@@ -482,24 +488,6 @@ + vap->iv_stats.is_rx_mcastecho++; + goto out; + } +- /* +- * if it is brodcasted by me on behalf of +- * a station behind me, drop it. +- */ +- if (vap->iv_flags_ext & IEEE80211_FEXT_WDS) { +- struct ieee80211_node_table *nt; +- struct ieee80211_node *ni_wds; +- nt = &ic->ic_sta; +- ni_wds = ieee80211_find_wds_node(nt, wh->i_addr3); +- if (ni_wds) { +- ieee80211_unref_node(&ni_wds); +- IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, +- wh, NULL, "%s", +- "multicast echo originated from node behind me"); +- vap->iv_stats.is_rx_mcastecho++; +- goto out; +- } +- } + } + break; + case IEEE80211_M_IBSS: +@@ -548,7 +536,7 @@ + */ + + /* check for wds link first */ +- if (dir == IEEE80211_FC1_DIR_DSTODS) { ++ if ((dir == IEEE80211_FC1_DIR_DSTODS) && !ni->ni_subif) { + struct ieee80211vap *avp; + + TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) { +@@ -562,11 +550,13 @@ if (ni_wds != NULL) { ieee80211_unref_node(&ni); ni = ieee80211_ref_node(ni_wds); -+ } else if (!ni->ni_subif && -+ (vap->iv_flags_ext & IEEE80211_FEXT_WDSSEP)) { ++ } else if (vap->iv_flags_ext & IEEE80211_FEXT_WDSSEP) { + ieee80211_wds_addif(ni); } } @@ -24,7 +90,19 @@ struct ieee80211_node_table *nt = &ic->ic_sta; struct ieee80211_frame_addr4 *wh4; -@@ -698,8 +702,12 @@ +@@ -626,6 +616,11 @@ + goto out; + } + ++ /* check if there is any data left */ ++ hdrspace = ieee80211_hdrspace(ic, wh); ++ if (skb->len < hdrspace) ++ goto out; ++ + /* + * Handle privacy requirements. Note that we + * must not be preempted from here until after +@@ -698,8 +693,12 @@ if (! accept_data_frame(vap, ni, key, skb, eh)) goto out; @@ -39,7 +117,7 @@ IEEE80211_NODE_STAT(ni, rx_data); IEEE80211_NODE_STAT_ADD(ni, rx_bytes, skb->len); ic->ic_lastdata = jiffies; -@@ -1132,6 +1140,13 @@ +@@ -1132,6 +1131,13 @@ dev = vap->iv_xrvap->iv_dev; #endif @@ -53,7 +131,7 @@ /* perform as a bridge within the vap */ /* XXX intra-vap bridging only */ if (vap->iv_opmode == IEEE80211_M_HOSTAP && -@@ -1157,6 +1172,7 @@ +@@ -1157,6 +1163,7 @@ if (ni1 != NULL) { if (ni1->ni_vap == vap && ieee80211_node_is_authorized(ni1) && @@ -73,7 +151,7 @@ #define SIOCG80211STATS (SIOCDEVPRIVATE+2) --- a/net80211/ieee80211_node.h +++ b/net80211/ieee80211_node.h -@@ -92,11 +92,12 @@ +@@ -92,11 +92,13 @@ * the ieee80211com structure. */ struct ieee80211_node { @@ -83,11 +161,12 @@ struct ieee80211_node_table *ni_table; TAILQ_ENTRY(ieee80211_node) ni_list; LIST_ENTRY(ieee80211_node) ni_hash; ++ struct work_struct ni_create; /* task for creating a subif */ + struct work_struct ni_destroy; /* task for destroying a subif */ atomic_t ni_refcnt; u_int ni_scangen; /* gen# for timeout scan */ u_int8_t ni_authmode; /* authentication algorithm */ -@@ -430,5 +431,6 @@ +@@ -430,5 +432,6 @@ void ieee80211_node_leave(struct ieee80211_node *); u_int8_t ieee80211_getrssi(struct ieee80211com *); int32_t ieee80211_get_node_count(struct ieee80211com *); @@ -203,29 +282,32 @@ ieee80211_node_table_reset(&ic->ic_sta, vap); if (vap->iv_bss != NULL) { ieee80211_unref_node(&vap->iv_bss); -@@ -1134,6 +1139,40 @@ +@@ -1134,6 +1139,57 @@ return ni; } +#define WDSIFNAME ".sta%d" -+void ieee80211_wds_addif(struct ieee80211_node *ni) ++static void ++ieee80211_wds_do_addif(struct work_struct *work) +{ ++ struct ieee80211_node *ni = container_of(work, struct ieee80211_node, ni_create); + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211vap *avp; + char *name; + -+ /* check if the node is split out already */ -+ if (ni->ni_subif) -+ return; ++ rtnl_lock(); ++ /* did we get cancelled by the destroy call? */ ++ if (!ni->ni_subif) ++ goto done; + ++ ni->ni_subif = NULL; + name = kmalloc(strlen(vap->iv_dev->name) + sizeof(WDSIFNAME) + 1, GFP_KERNEL); + if (!name) -+ return; ++ goto done; + + strcpy(name, vap->iv_dev->name); + strcat(name, WDSIFNAME); -+ rtnl_lock(); + avp = ieee80211_create_vap(ic, name, ic->ic_dev, IEEE80211_M_WDS, 0, vap); + kfree(name); + if (!avp) @@ -238,13 +320,27 @@ + +done: + rtnl_unlock(); ++ ieee80211_unref_node(&ni); +} +#undef WDSIFNAME ++ ++void ieee80211_wds_addif(struct ieee80211_node *ni) ++{ ++ /* check if the node is split out already, ++ * or if we're in progress of setting up a new interface already */ ++ if (ni->ni_subif) ++ return; ++ ++ ieee80211_ref_node(ni); ++ ni->ni_subif = ni->ni_vap; ++ IEEE80211_INIT_WORK(&ni->ni_create, ieee80211_wds_do_addif); ++ schedule_work(&ni->ni_create); ++} + /* Add wds address to the node table */ int #ifdef IEEE80211_DEBUG_REFCNT -@@ -2254,6 +2293,28 @@ +@@ -2254,6 +2310,36 @@ } } @@ -252,28 +348,36 @@ +ieee80211_subif_destroy(struct work_struct *work) +{ + struct ieee80211_node *ni = container_of(work, struct ieee80211_node, ni_destroy); -+ struct ieee80211vap *vap = ni->ni_subif; ++ struct ieee80211vap *vap; + struct ieee80211com *ic; + ++ rtnl_lock(); ++ vap = ni->ni_subif; ++ ++ /* if addif is waiting for the timer to fire, cancel! */ ++ if (vap == ni->ni_vap) { ++ ni->ni_subif = NULL; ++ goto done; ++ } ++ + if (!vap) + goto done; + -+ rtnl_lock(); + ic = vap->iv_ic; + ni->ni_subif = NULL; + ieee80211_stop(vap->iv_dev); + ic->ic_vap_delete(vap); + ic->ic_subifs--; -+ rtnl_unlock(); + +done: + ieee80211_unref_node(&ni); ++ rtnl_unlock(); +} + /* * Handle bookkeeping for a station/neighbor leaving * the bss when operating in ap or adhoc modes. -@@ -2270,6 +2331,12 @@ +@@ -2270,6 +2356,12 @@ ni, "station with aid %d leaves (refcnt %u)", IEEE80211_NODE_AID(ni), atomic_read(&ni->ni_refcnt)); @@ -407,9 +511,60 @@ return rc; } +@@ -1630,6 +1654,7 @@ + */ + if (ni->ni_authmode != IEEE80211_AUTH_8021X) + ieee80211_node_authorize(ni); ++ + #ifdef ATH_SUPERG_XR + /* + * fire a timer to bring up XR vap if configured. +@@ -1885,8 +1910,15 @@ + if (ostate == IEEE80211_S_SCAN || + ostate == IEEE80211_S_AUTH || + ostate == IEEE80211_S_ASSOC) { ++ + /* Transition (S_SCAN|S_AUTH|S_ASSOC) -> S_RUN */ + __ieee80211_newstate(vap, nstate, arg); ++ ++ /* if we're in wds, let the ap know that we're doing this */ ++ if ((vap->iv_opmode == IEEE80211_M_STA) && ++ (vap->iv_flags_ext & IEEE80211_FEXT_WDS)) ++ ieee80211_send_nulldata(ieee80211_ref_node(vap->iv_bss)); ++ + /* Then bring up all other vaps pending on the scan */ + dstate = get_dominant_state(ic); + if (dstate == IEEE80211_S_RUN) { --- a/net80211/ieee80211.c +++ b/net80211/ieee80211.c -@@ -599,8 +599,10 @@ +@@ -373,10 +373,25 @@ + ieee80211_ifdetach(struct ieee80211com *ic) + { + struct ieee80211vap *vap; ++ int count; ++ ++ /* bring down all vaps */ ++ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { ++ ieee80211_stop(vap->iv_dev); ++ } ++ ++ /* wait for all subifs to disappear */ ++ do { ++ schedule(); ++ rtnl_lock(); ++ count = ic->ic_subifs; ++ rtnl_unlock(); ++ } while (count > 0); + + rtnl_lock(); +- while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) ++ while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) { + ic->ic_vap_delete(vap); ++ } + rtnl_unlock(); + + del_timer(&ic->ic_dfs_excl_timer); +@@ -599,8 +614,10 @@ IEEE80211_CANCEL_TQUEUE(&vap->iv_stajoin1tq); IEEE80211_LOCK_IRQ(ic); @@ -454,7 +609,133 @@ */ --- a/net80211/ieee80211_output.c +++ b/net80211/ieee80211_output.c -@@ -786,6 +786,8 @@ +@@ -261,6 +261,10 @@ + goto bad; + } + ++ if (ni->ni_subif && (vap != ni->ni_subif) && ++ ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE))) ++ goto bad; ++ + /* calculate priority so drivers can find the TX queue */ + if (ieee80211_classify(ni, skb)) { + IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, +@@ -340,20 +344,33 @@ + * constructing a frame as it sets i_fc[1]; other bits can + * then be or'd in. + */ +-static void ++static struct ieee80211_frame * + ieee80211_send_setup(struct ieee80211vap *vap, + struct ieee80211_node *ni, +- struct ieee80211_frame *wh, ++ struct sk_buff *skb, + int type, + const u_int8_t sa[IEEE80211_ADDR_LEN], + const u_int8_t da[IEEE80211_ADDR_LEN], + const u_int8_t bssid[IEEE80211_ADDR_LEN]) + { + #define WH4(wh) ((struct ieee80211_frame_addr4 *)wh) ++ struct ieee80211_frame *wh; ++ int len = sizeof(struct ieee80211_frame); ++ int opmode = vap->iv_opmode; ++ ++ if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { ++ if ((opmode == IEEE80211_M_STA) && ++ (vap->iv_flags_ext & IEEE80211_FEXT_WDS)) ++ opmode = IEEE80211_M_WDS; ++ ++ if (opmode == IEEE80211_M_WDS) ++ len = sizeof(struct ieee80211_frame_addr4); ++ } + ++ wh = (struct ieee80211_frame *)skb_push(skb, len); + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type; + if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { +- switch (vap->iv_opmode) { ++ switch (opmode) { + case IEEE80211_M_STA: + wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; + IEEE80211_ADDR_COPY(wh->i_addr1, bssid); +@@ -395,6 +412,8 @@ + *(__le16 *)&wh->i_seq[0] = + htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT); + ni->ni_txseqs[0]++; ++ ++ return wh; + #undef WH4 + } + +@@ -416,9 +435,7 @@ + + SKB_CB(skb)->ni = ni; + +- wh = (struct ieee80211_frame *) +- skb_push(skb, sizeof(struct ieee80211_frame)); +- ieee80211_send_setup(vap, ni, wh, ++ wh = ieee80211_send_setup(vap, ni, skb, + IEEE80211_FC0_TYPE_MGT | type, + vap->iv_myaddr, ni->ni_macaddr, vap->iv_bssid); + /* XXX power management */ +@@ -464,6 +481,9 @@ + struct ieee80211_frame *wh; + u_int8_t *frm; + ++ if (ni->ni_subif) ++ vap = ni->ni_subif; ++ + skb = ieee80211_getmgtframe(&frm, 0); + if (skb == NULL) { + /* XXX debug msg */ +@@ -472,9 +492,7 @@ + return -ENOMEM; + } + +- wh = (struct ieee80211_frame *) +- skb_push(skb, sizeof(struct ieee80211_frame)); +- ieee80211_send_setup(vap, ni, wh, ++ wh = ieee80211_send_setup(vap, ni, skb, + IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA, + vap->iv_myaddr, ni->ni_macaddr, vap->iv_bssid); + /* NB: power management bit is never sent by an AP */ +@@ -512,6 +530,7 @@ + struct sk_buff *skb; + struct ieee80211_qosframe *qwh; + u_int8_t *frm; ++ u_int8_t *i_qos; + int tid; + + skb = ieee80211_getmgtframe(&frm, 2); +@@ -523,11 +542,12 @@ + SKB_CB(skb)->ni = ieee80211_ref_node(ni); + + skb->priority = ac; +- qwh = (struct ieee80211_qosframe *)skb_push(skb, sizeof(struct ieee80211_qosframe)); + +- qwh = (struct ieee80211_qosframe *)skb->data; ++ /* grab a pointer to QoS control and also compensate for the header length ++ * difference between QoS and non-QoS frame */ ++ i_qos = skb_push(skb, sizeof(struct ieee80211_qosframe) - sizeof(struct ieee80211_frame)); + +- ieee80211_send_setup(vap, ni, (struct ieee80211_frame *)qwh, ++ qwh = (struct ieee80211_qosframe *) ieee80211_send_setup(vap, ni, skb, + IEEE80211_FC0_TYPE_DATA, + vap->iv_myaddr, /* SA */ + ni->ni_macaddr, /* DA */ +@@ -541,10 +561,10 @@ + + /* map from access class/queue to 11e header priority value */ + tid = WME_AC_TO_TID(ac); +- qwh->i_qos[0] = tid & IEEE80211_QOS_TID; ++ i_qos[0] = tid & IEEE80211_QOS_TID; + if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy) + qwh->i_qos[0] |= (1 << IEEE80211_QOS_ACKPOLICY_S) & IEEE80211_QOS_ACKPOLICY; +- qwh->i_qos[1] = 0; ++ i_qos[1] = 0; + + IEEE80211_NODE_STAT(ni, tx_data); + +@@ -786,6 +806,8 @@ hdrsize = sizeof(struct ieee80211_frame); SKB_CB(skb)->auth_pkt = (eh.ether_type == __constant_htons(ETHERTYPE_PAE)); @@ -463,3 +744,57 @@ switch (vap->iv_opmode) { case IEEE80211_M_IBSS: +@@ -805,20 +827,9 @@ + ismulticast = IEEE80211_IS_MULTICAST(eh.ether_dhost); + break; + case IEEE80211_M_STA: +- if ((vap->iv_flags_ext & IEEE80211_FEXT_WDS) && +- !IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)) { ++ if (vap->iv_flags_ext & IEEE80211_FEXT_WDS) { + use4addr = 1; +- ismulticast = IEEE80211_IS_MULTICAST(ni->ni_macaddr); +- /* Add a WDS entry to the station VAP */ +- if (IEEE80211_IS_MULTICAST(eh.ether_dhost)) { +- struct ieee80211_node_table *nt = &ic->ic_sta; +- struct ieee80211_node *ni_wds +- = ieee80211_find_wds_node(nt, eh.ether_shost); +- if (ni_wds) +- ieee80211_unref_node(&ni_wds); +- else +- ieee80211_add_wds_addr(nt, ni, eh.ether_shost, 0); +- } ++ ismulticast = 0; + } else + ismulticast = IEEE80211_IS_MULTICAST(vap->iv_bssid); + break; +@@ -1689,9 +1700,7 @@ + + SKB_CB(skb)->ni = ieee80211_ref_node(ni); + +- wh = (struct ieee80211_frame *) +- skb_push(skb, sizeof(struct ieee80211_frame)); +- ieee80211_send_setup(vap, ni, wh, ++ wh = ieee80211_send_setup(vap, ni, skb, + IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ, + sa, da, bssid); + /* XXX power management? */ +--- a/net80211/ieee80211_linux.c ++++ b/net80211/ieee80211_linux.c +@@ -145,7 +145,7 @@ + struct sk_buff *skb; + u_int len; + +- len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4); ++ len = roundup(sizeof(struct ieee80211_frame_addr4) + pktlen, 4); + #ifdef IEEE80211_DEBUG_REFCNT + skb = ieee80211_dev_alloc_skb_debug(len + align - 1, func, line); + #else +@@ -161,7 +161,7 @@ + SKB_CB(skb)->flags = 0; + SKB_CB(skb)->next = NULL; + +- skb_reserve(skb, sizeof(struct ieee80211_frame)); ++ skb_reserve(skb, sizeof(struct ieee80211_frame_addr4)); + *frm = skb_put(skb, pktlen); + } + return skb; -- cgit v1.2.3