aboutsummaryrefslogtreecommitdiffstats
path: root/package/madwifi/patches/371-wds_sta_separation.patch
diff options
context:
space:
mode:
Diffstat (limited to 'package/madwifi/patches/371-wds_sta_separation.patch')
-rw-r--r--package/madwifi/patches/371-wds_sta_separation.patch421
1 files changed, 421 insertions, 0 deletions
diff --git a/package/madwifi/patches/371-wds_sta_separation.patch b/package/madwifi/patches/371-wds_sta_separation.patch
new file mode 100644
index 0000000000..c16145b0a9
--- /dev/null
+++ b/package/madwifi/patches/371-wds_sta_separation.patch
@@ -0,0 +1,421 @@
+--- a/net80211/ieee80211_input.c
++++ b/net80211/ieee80211_input.c
+@@ -202,6 +202,7 @@
+ struct ieee80211com *ic = vap->iv_ic;
+ struct net_device *dev = vap->iv_dev;
+ struct ieee80211_node *ni_wds = NULL;
++ struct net_device_stats *stats;
+ struct ieee80211_frame *wh;
+ struct ieee80211_key *key;
+ struct ether_header *eh;
+@@ -562,11 +563,14 @@
+ 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)) {
++ ieee80211_wds_addif(ni);
+ }
+ }
+
+ /* XXX: Useless node mgmt API; make better */
+- if ((dir == IEEE80211_FC1_DIR_DSTODS) && !ni_wds) {
++ if ((dir == IEEE80211_FC1_DIR_DSTODS) && !ni_wds && !ni->ni_subif) {
+ struct ieee80211_node_table *nt = &ic->ic_sta;
+ struct ieee80211_frame_addr4 *wh4;
+
+@@ -698,8 +702,12 @@
+ if (! accept_data_frame(vap, ni, key, skb, eh))
+ goto out;
+
+- vap->iv_devstats.rx_packets++;
+- vap->iv_devstats.rx_bytes += skb->len;
++ if (ni->ni_subif && ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE)))
++ stats = &ni->ni_subif->iv_devstats;
++ else
++ stats = &vap->iv_devstats;
++ stats->rx_packets++;
++ stats->rx_bytes += skb->len;
+ IEEE80211_NODE_STAT(ni, rx_data);
+ IEEE80211_NODE_STAT_ADD(ni, rx_bytes, skb->len);
+ ic->ic_lastdata = jiffies;
+@@ -1132,6 +1140,11 @@
+ dev = vap->iv_xrvap->iv_dev;
+ #endif
+
++ /* if the node has a wds subif, move data frames there,
++ * but keep EAP traffic on the master */
++ if (ni->ni_subif && ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE)))
++ dev = ni->ni_subif->iv_dev;
++
+ /* perform as a bridge within the vap */
+ /* XXX intra-vap bridging only */
+ if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
+@@ -1157,6 +1170,7 @@
+ if (ni1 != NULL) {
+ if (ni1->ni_vap == vap &&
+ ieee80211_node_is_authorized(ni1) &&
++ !ni->ni_subif &&
+ ni1 != vap->iv_bss) {
+ skb1 = skb;
+ skb = NULL;
+--- a/net80211/ieee80211_ioctl.h
++++ b/net80211/ieee80211_ioctl.h
+@@ -649,6 +649,7 @@
+ IEEE80211_PARAM_BGSCAN_THRESH = 79, /* bg scan rssi threshold */
+ IEEE80211_PARAM_RSSI_DIS_THR = 80, /* rssi threshold for disconnection */
+ IEEE80211_PARAM_RSSI_DIS_COUNT = 81, /* counter for rssi threshold */
++ IEEE80211_PARAM_WDS_SEP = 82, /* move wds stations into separate interfaces */
+ };
+
+ #define SIOCG80211STATS (SIOCDEVPRIVATE+2)
+--- a/net80211/ieee80211_node.h
++++ b/net80211/ieee80211_node.h
+@@ -92,11 +92,12 @@
+ * the ieee80211com structure.
+ */
+ struct ieee80211_node {
+- struct ieee80211vap *ni_vap;
++ struct ieee80211vap *ni_vap, *ni_subif;
+ struct ieee80211com *ni_ic;
+ struct ieee80211_node_table *ni_table;
+ TAILQ_ENTRY(ieee80211_node) ni_list;
+ LIST_ENTRY(ieee80211_node) ni_hash;
++ 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 @@
+ void ieee80211_node_leave(struct ieee80211_node *);
+ u_int8_t ieee80211_getrssi(struct ieee80211com *);
+ int32_t ieee80211_get_node_count(struct ieee80211com *);
++void ieee80211_wds_addif(struct ieee80211_node *ni);
+ #endif /* _NET80211_IEEE80211_NODE_H_ */
+
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -322,6 +322,7 @@
+ u_int8_t ic_myaddr[IEEE80211_ADDR_LEN];
+ struct timer_list ic_inact; /* mgmt/inactivity timer */
+
++ unsigned int ic_subifs;
+ u_int32_t ic_flags; /* state flags */
+ u_int32_t ic_flags_ext; /* extension of state flags */
+ u_int32_t ic_caps; /* capabilities */
+@@ -625,6 +626,7 @@
+ #define IEEE80211_FEXT_DROPUNENC_EAPOL 0x00000800 /* CONF: drop unencrypted eapol frames */
+ #define IEEE80211_FEXT_APPIE_UPDATE 0x00001000 /* STATE: beacon APP IE updated */
+ #define IEEE80211_FEXT_BGSCAN_THR 0x00002000 /* bgscan due to low rssi */
++#define IEEE80211_FEXT_WDSSEP 0x00004000 /* move wds clients into separate interfaces */
+
+ #define IEEE80211_COM_UAPSD_ENABLE(_ic) ((_ic)->ic_flags_ext |= IEEE80211_FEXT_UAPSD)
+ #define IEEE80211_COM_UAPSD_DISABLE(_ic) ((_ic)->ic_flags_ext &= ~IEEE80211_FEXT_UAPSD)
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -2867,6 +2867,14 @@
+ else
+ vap->iv_minrateindex = 0;
+ break;
++ case IEEE80211_PARAM_WDS_SEP:
++ if (vap->iv_opmode != IEEE80211_M_HOSTAP)
++ retv = -EINVAL;
++ else if (value)
++ vap->iv_flags_ext |= IEEE80211_FEXT_WDSSEP;
++ else
++ vap->iv_flags_ext &= ~IEEE80211_FEXT_WDSSEP;
++ break;
+ #ifdef ATH_REVERSE_ENGINEERING
+ case IEEE80211_PARAM_DUMPREGS:
+ ieee80211_dump_registers(dev, info, w, extra);
+@@ -3223,6 +3231,9 @@
+ case IEEE80211_PARAM_MINRATE:
+ param[0] = vap->iv_minrateindex;
+ break;
++ case IEEE80211_PARAM_WDS_SEP:
++ param[0] = !!(vap->iv_flags_ext & IEEE80211_FEXT_WDSSEP);
++ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+@@ -5767,6 +5778,10 @@
+ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_minrate"},
+ { IEEE80211_IOCTL_SETSCANLIST,
+ IW_PRIV_TYPE_CHAR | 255, 0, "setscanlist"},
++ { IEEE80211_PARAM_WDS_SEP,
++ IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wdssep"},
++ { IEEE80211_PARAM_WDS_SEP,
++ 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_wdssep"},
+
+ #ifdef ATH_REVERSE_ENGINEERING
+ /*
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -47,6 +47,7 @@
+ #include <linux/netdevice.h>
+ #include <linux/etherdevice.h>
+ #include <linux/random.h>
++#include <linux/rtnetlink.h>
+
+ #include "if_media.h"
+
+@@ -236,7 +237,11 @@
+ ieee80211_node_vdetach(struct ieee80211vap *vap)
+ {
+ struct ieee80211com *ic = vap->iv_ic;
++ struct ieee80211_node *ni;
+
++ ni = vap->iv_wdsnode;
++ if (ni)
++ ni->ni_subif = NULL;
+ ieee80211_node_table_reset(&ic->ic_sta, vap);
+ if (vap->iv_bss != NULL) {
+ ieee80211_unref_node(&vap->iv_bss);
+@@ -1134,6 +1139,40 @@
+ return ni;
+ }
+
++#define WDSIFNAME ".sta%d"
++void ieee80211_wds_addif(struct ieee80211_node *ni)
++{
++ 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;
++
++ name = kmalloc(strlen(vap->iv_dev->name) + sizeof(WDSIFNAME) + 1, GFP_KERNEL);
++ if (!name)
++ return;
++
++ 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)
++ goto done;
++
++ memcpy(avp->wds_mac, ni->ni_bssid, IEEE80211_ADDR_LEN);
++ avp->iv_wdsnode = ieee80211_ref_node(ni);
++ ni->ni_subif = avp;
++ ic->ic_subifs++;
++
++done:
++ rtnl_unlock();
++}
++#undef WDSIFNAME
++
+ /* Add wds address to the node table */
+ int
+ #ifdef IEEE80211_DEBUG_REFCNT
+@@ -2254,6 +2293,28 @@
+ }
+ }
+
++static void
++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 ieee80211com *ic;
++
++ 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);
++}
++
+ /*
+ * Handle bookkeeping for a station/neighbor leaving
+ * the bss when operating in ap or adhoc modes.
+@@ -2270,6 +2331,12 @@
+ ni, "station with aid %d leaves (refcnt %u)",
+ IEEE80211_NODE_AID(ni), atomic_read(&ni->ni_refcnt));
+
++ if (ni->ni_subif) {
++ ieee80211_ref_node(ni);
++ IEEE80211_INIT_WORK(&ni->ni_destroy, ieee80211_subif_destroy);
++ schedule_work(&ni->ni_destroy);
++ }
++
+ /* From this point onwards we can no longer find the node,
+ * so no more references are generated
+ */
+--- a/net80211/ieee80211_linux.h
++++ b/net80211/ieee80211_linux.h
+@@ -81,6 +81,12 @@
+ #endif
+ }
+
++#ifndef container_of
++#define container_of(ptr, type, member) ({ \
++ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
++ (type *)( (char *)__mptr - offsetof(type,member) );})
++#endif
++
+ /*
+ * Task deferral
+ *
+@@ -113,6 +119,29 @@
+
+ #define IEEE80211_RESCHEDULE schedule
+
++#include <linux/sched.h>
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
++#include <linux/tqueue.h>
++#define work_struct tq_struct
++#define schedule_work(t) schedule_task((t))
++#define flush_scheduled_work() flush_scheduled_tasks()
++#define IEEE80211_INIT_WORK(t, f) do { \
++ memset((t), 0, sizeof(struct tq_struct)); \
++ (t)->routine = (void (*)(void*)) (f); \
++ (t)->data=(void *) (t); \
++} while (0)
++#else
++#include <linux/workqueue.h>
++
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
++#define IEEE80211_INIT_WORK(_t, _f) INIT_WORK((_t), (void (*)(void *))(_f), (_t));
++#else
++#define IEEE80211_INIT_WORK(_t, _f) INIT_WORK((_t), (_f));
++#endif
++
++#endif /* KERNEL_VERSION < 2.5.41 */
++
++
+ /* Locking */
+ /* NB: beware, spin_is_locked() is not usefully defined for !(DEBUG || SMP)
+ * because spinlocks do not exist in this configuration. Instead IRQs
+@@ -167,6 +196,18 @@
+ IEEE80211_VAPS_LOCK_ASSERT(_ic); \
+ spin_unlock_bh(&(_ic)->ic_vapslock); \
+ } while (0)
++#define IEEE80211_VAPS_LOCK_IRQ(_ic) do { \
++ unsigned long __vlockflags; \
++ IEEE80211_VAPS_LOCK_CHECK(_ic); \
++ spin_lock_irqsave(&(_ic)->ic_vapslock, __vlockflags);
++#define IEEE80211_VAPS_UNLOCK_IRQ(_ic) \
++ IEEE80211_VAPS_LOCK_ASSERT(_ic); \
++ spin_unlock_irqrestore(&(_ic)->ic_vapslock, __vlockflags); \
++} while (0)
++#define IEEE80211_VAPS_UNLOCK_IRQ_EARLY(_ic) \
++ IEEE80211_VAPS_LOCK_ASSERT(_ic); \
++ spin_unlock_irqrestore(&(_ic)->ic_vapslock, __vlockflags);
++
+
+ #if (defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)) && defined(spin_is_locked)
+ #define IEEE80211_VAPS_LOCK_ASSERT(_ic) \
+--- a/net80211/ieee80211_proto.c
++++ b/net80211/ieee80211_proto.c
+@@ -1081,6 +1081,8 @@
+ int
+ ieee80211_open(struct net_device *dev)
+ {
++ struct ieee80211vap *vap = dev->priv;
++
+ return ieee80211_init(dev, 0);
+ }
+
+@@ -1116,11 +1118,33 @@
+ struct ieee80211vap *vap = dev->priv;
+ struct ieee80211com *ic = vap->iv_ic;
+ struct net_device *parent = ic->ic_dev;
++ struct ieee80211_node *tni, *ni;
+
+ IEEE80211_DPRINTF(vap,
+ IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
+ "%s\n", "stop running");
+
++ /* get rid of all wds nodes while we're still locked */
++ do {
++ ni = NULL;
++
++ IEEE80211_NODE_TABLE_LOCK_IRQ(&ic->ic_sta);
++ TAILQ_FOREACH(tni, &ic->ic_sta.nt_node, ni_list) {
++ if (tni->ni_vap != vap)
++ continue;
++ if (!tni->ni_subif)
++ continue;
++ ni = tni;
++ break;
++ }
++ IEEE80211_NODE_TABLE_UNLOCK_IRQ(&ic->ic_sta);
++
++ if (!ni)
++ break;
++
++ ieee80211_node_leave(ni);
++ } while (1);
++
+ ieee80211_new_state(vap, IEEE80211_S_INIT, -1);
+ if (dev->flags & IFF_RUNNING) {
+ dev->flags &= ~IFF_RUNNING; /* mark us stopped */
+@@ -1342,9 +1366,9 @@
+ struct ieee80211com *ic = vap->iv_ic;
+ int rc;
+
+- IEEE80211_VAPS_LOCK_BH(ic);
++ IEEE80211_VAPS_LOCK_IRQ(ic);
+ rc = vap->iv_newstate(vap, nstate, arg);
+- IEEE80211_VAPS_UNLOCK_BH(ic);
++ IEEE80211_VAPS_UNLOCK_IRQ(ic);
+ return rc;
+ }
+
+--- a/net80211/ieee80211.c
++++ b/net80211/ieee80211.c
+@@ -599,8 +599,10 @@
+
+ IEEE80211_CANCEL_TQUEUE(&vap->iv_stajoin1tq);
+ IEEE80211_LOCK_IRQ(ic);
+- if (vap->iv_wdsnode)
++ if (vap->iv_wdsnode) {
++ vap->iv_wdsnode->ni_subif = NULL;
+ ieee80211_unref_node(&vap->iv_wdsnode);
++ }
+ if ((vap->iv_opmode == IEEE80211_M_WDS) &&
+ (vap->iv_master != NULL))
+ TAILQ_REMOVE(&vap->iv_master->iv_wdslinks, vap, iv_wdsnext);
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -79,28 +79,6 @@
+ #define tasklet_enable(t) do { (void) t; local_bh_enable(); } while (0)
+ #endif /* !DECLARE_TASKLET */
+
+-#include <linux/sched.h>
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
+-#include <linux/tqueue.h>
+-#define work_struct tq_struct
+-#define schedule_work(t) schedule_task((t))
+-#define flush_scheduled_work() flush_scheduled_tasks()
+-#define ATH_INIT_WORK(t, f) do { \
+- memset((t), 0, sizeof(struct tq_struct)); \
+- (t)->routine = (void (*)(void*)) (f); \
+- (t)->data=(void *) (t); \
+-} while (0)
+-#else
+-#include <linux/workqueue.h>
+-
+-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
+-#define ATH_INIT_WORK(_t, _f) INIT_WORK((_t), (void (*)(void *))(_f), (_t));
+-#else
+-#define ATH_INIT_WORK(_t, _f) INIT_WORK((_t), (_f));
+-#endif
+-
+-#endif /* KERNEL_VERSION < 2.5.41 */
+-
+ /*
+ * Guess how the interrupt handler should work.
+ */