madwifi: fix race condition triggered by active split wds sta interfaces during vap...
[librecmc/librecmc.git] / package / madwifi / patches / 371-wds_sta_separation.patch
1 --- a/net80211/ieee80211_input.c
2 +++ b/net80211/ieee80211_input.c
3 @@ -202,6 +202,7 @@
4         struct ieee80211com *ic = vap->iv_ic;
5         struct net_device *dev = vap->iv_dev;
6         struct ieee80211_node *ni_wds = NULL;
7 +       struct net_device_stats *stats;
8         struct ieee80211_frame *wh;
9         struct ieee80211_key *key;
10         struct ether_header *eh;
11 @@ -562,11 +563,14 @@
12                                 if (ni_wds != NULL) {
13                                         ieee80211_unref_node(&ni);
14                                         ni = ieee80211_ref_node(ni_wds);
15 +                               } else if (!ni->ni_subif &&
16 +                                               (vap->iv_flags_ext & IEEE80211_FEXT_WDSSEP)) {
17 +                                       ieee80211_wds_addif(ni);
18                                 }
19                         }
20  
21                         /* XXX: Useless node mgmt API; make better */
22 -                       if ((dir == IEEE80211_FC1_DIR_DSTODS) && !ni_wds) {
23 +                       if ((dir == IEEE80211_FC1_DIR_DSTODS) && !ni_wds && !ni->ni_subif) {
24                                 struct ieee80211_node_table *nt = &ic->ic_sta;
25                                 struct ieee80211_frame_addr4 *wh4;
26  
27 @@ -698,8 +702,12 @@
28                 if (! accept_data_frame(vap, ni, key, skb, eh))
29                         goto out;
30  
31 -               vap->iv_devstats.rx_packets++;
32 -               vap->iv_devstats.rx_bytes += skb->len;
33 +               if (ni->ni_subif && ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE)))
34 +                       stats = &ni->ni_subif->iv_devstats;
35 +               else
36 +                       stats = &vap->iv_devstats;
37 +               stats->rx_packets++;
38 +               stats->rx_bytes += skb->len;
39                 IEEE80211_NODE_STAT(ni, rx_data);
40                 IEEE80211_NODE_STAT_ADD(ni, rx_bytes, skb->len);
41                 ic->ic_lastdata = jiffies;
42 @@ -1132,6 +1140,13 @@
43                 dev = vap->iv_xrvap->iv_dev;
44  #endif
45  
46 +       /* if the node has a wds subif, move data frames there,
47 +        * but keep EAP traffic on the master */
48 +       if (ni->ni_subif && ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE))) {
49 +               vap = ni->ni_subif;
50 +               dev = vap->iv_dev;
51 +       }
52 +
53         /* perform as a bridge within the vap */
54         /* XXX intra-vap bridging only */
55         if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
56 @@ -1157,6 +1172,7 @@
57                         if (ni1 != NULL) {
58                                 if (ni1->ni_vap == vap &&
59                                     ieee80211_node_is_authorized(ni1) &&
60 +                                       !ni->ni_subif &&
61                                     ni1 != vap->iv_bss) {
62                                         skb1 = skb;
63                                         skb = NULL;
64 --- a/net80211/ieee80211_ioctl.h
65 +++ b/net80211/ieee80211_ioctl.h
66 @@ -649,6 +649,7 @@
67         IEEE80211_PARAM_BGSCAN_THRESH           = 79,   /* bg scan rssi threshold */
68         IEEE80211_PARAM_RSSI_DIS_THR    = 80,   /* rssi threshold for disconnection */
69         IEEE80211_PARAM_RSSI_DIS_COUNT  = 81,   /* counter for rssi threshold */
70 +       IEEE80211_PARAM_WDS_SEP                 = 82,   /* move wds stations into separate interfaces */
71  };
72  
73  #define        SIOCG80211STATS                 (SIOCDEVPRIVATE+2)
74 --- a/net80211/ieee80211_node.h
75 +++ b/net80211/ieee80211_node.h
76 @@ -92,11 +92,12 @@
77   * the ieee80211com structure.
78   */
79  struct ieee80211_node {
80 -       struct ieee80211vap *ni_vap;
81 +       struct ieee80211vap *ni_vap, *ni_subif;
82         struct ieee80211com *ni_ic;
83         struct ieee80211_node_table *ni_table;
84         TAILQ_ENTRY(ieee80211_node) ni_list;
85         LIST_ENTRY(ieee80211_node) ni_hash;
86 +       struct work_struct ni_destroy;  /* task for destroying a subif */
87         atomic_t ni_refcnt;
88         u_int ni_scangen;                       /* gen# for timeout scan */
89         u_int8_t ni_authmode;                   /* authentication algorithm */
90 @@ -430,5 +431,6 @@
91  void ieee80211_node_leave(struct ieee80211_node *);
92  u_int8_t ieee80211_getrssi(struct ieee80211com *);
93  int32_t ieee80211_get_node_count(struct ieee80211com *);
94 +void ieee80211_wds_addif(struct ieee80211_node *ni);
95  #endif /* _NET80211_IEEE80211_NODE_H_ */
96  
97 --- a/net80211/ieee80211_var.h
98 +++ b/net80211/ieee80211_var.h
99 @@ -322,6 +322,7 @@
100         u_int8_t ic_myaddr[IEEE80211_ADDR_LEN];
101         struct timer_list ic_inact;             /* mgmt/inactivity timer */
102  
103 +       unsigned int ic_subifs;
104         u_int32_t ic_flags;                     /* state flags */
105         u_int32_t ic_flags_ext;                 /* extension of state flags */
106         u_int32_t ic_caps;                      /* capabilities */
107 @@ -625,6 +626,7 @@
108  #define IEEE80211_FEXT_DROPUNENC_EAPOL 0x00000800      /* CONF: drop unencrypted eapol frames */
109  #define IEEE80211_FEXT_APPIE_UPDATE    0x00001000      /* STATE: beacon APP IE updated */
110  #define IEEE80211_FEXT_BGSCAN_THR      0x00002000      /* bgscan due to low rssi */
111 +#define IEEE80211_FEXT_WDSSEP          0x00004000      /* move wds clients into separate interfaces */
112  
113  #define IEEE80211_COM_UAPSD_ENABLE(_ic)                ((_ic)->ic_flags_ext |= IEEE80211_FEXT_UAPSD)
114  #define IEEE80211_COM_UAPSD_DISABLE(_ic)       ((_ic)->ic_flags_ext &= ~IEEE80211_FEXT_UAPSD)
115 --- a/net80211/ieee80211_wireless.c
116 +++ b/net80211/ieee80211_wireless.c
117 @@ -2867,6 +2867,14 @@
118                 else
119                         vap->iv_minrateindex = 0;
120                 break;
121 +       case IEEE80211_PARAM_WDS_SEP:
122 +               if (vap->iv_opmode != IEEE80211_M_HOSTAP)
123 +                       retv = -EINVAL;
124 +               else if (value)
125 +                       vap->iv_flags_ext |= IEEE80211_FEXT_WDSSEP;
126 +               else
127 +                       vap->iv_flags_ext &= ~IEEE80211_FEXT_WDSSEP;
128 +               break;
129  #ifdef ATH_REVERSE_ENGINEERING
130         case IEEE80211_PARAM_DUMPREGS:
131                 ieee80211_dump_registers(dev, info, w, extra);
132 @@ -3223,6 +3231,9 @@
133         case IEEE80211_PARAM_MINRATE:
134                 param[0] = vap->iv_minrateindex;
135                 break;
136 +       case IEEE80211_PARAM_WDS_SEP:
137 +               param[0] = !!(vap->iv_flags_ext & IEEE80211_FEXT_WDSSEP);
138 +               break;
139         default:
140                 return -EOPNOTSUPP;
141         }
142 @@ -5767,6 +5778,10 @@
143          0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_minrate"},
144         { IEEE80211_IOCTL_SETSCANLIST,
145          IW_PRIV_TYPE_CHAR | 255, 0, "setscanlist"},
146 +       { IEEE80211_PARAM_WDS_SEP,
147 +        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wdssep"},
148 +       { IEEE80211_PARAM_WDS_SEP,
149 +        0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_wdssep"},
150  
151  #ifdef ATH_REVERSE_ENGINEERING
152         /*
153 @@ -5890,6 +5905,8 @@
154  ieee80211_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
155  {
156         struct ieee80211vap *vap = dev->priv;
157 +       struct ieee80211com *ic = vap->iv_ic;
158 +       struct ieee80211_node *ni;
159  
160         switch (cmd) {
161         case SIOCG80211STATS:
162 @@ -5898,8 +5915,20 @@
163         case SIOC80211IFDESTROY:
164                 if (!capable(CAP_NET_ADMIN))
165                         return -EPERM;
166 +               /* drop all node subifs */
167 +               TAILQ_FOREACH(ni, &ic->ic_sta.nt_node, ni_list) {
168 +                       struct ieee80211vap *avp = ni->ni_subif;
169 +
170 +                       if (ni->ni_vap != vap)
171 +                               continue;
172 +                       if (!avp)
173 +                               continue;
174 +                       ni->ni_subif = NULL;
175 +                       ieee80211_stop(avp->iv_dev);
176 +                       ic->ic_vap_delete(avp);
177 +               }
178                 ieee80211_stop(vap->iv_dev);    /* force state before cleanup */
179 -               vap->iv_ic->ic_vap_delete(vap);
180 +               ic->ic_vap_delete(vap);
181                 return 0;
182         case IEEE80211_IOCTL_GETKEY:
183                 return ieee80211_ioctl_getkey(dev, (struct iwreq *) ifr);
184 --- a/net80211/ieee80211_node.c
185 +++ b/net80211/ieee80211_node.c
186 @@ -47,6 +47,7 @@
187  #include <linux/netdevice.h>
188  #include <linux/etherdevice.h>
189  #include <linux/random.h>
190 +#include <linux/rtnetlink.h>
191  
192  #include "if_media.h"
193  
194 @@ -236,7 +237,11 @@
195  ieee80211_node_vdetach(struct ieee80211vap *vap)
196  {
197         struct ieee80211com *ic = vap->iv_ic;
198 +       struct ieee80211_node *ni;
199  
200 +       ni = vap->iv_wdsnode;
201 +       if (ni)
202 +               ni->ni_subif = NULL;
203         ieee80211_node_table_reset(&ic->ic_sta, vap);
204         if (vap->iv_bss != NULL) {
205                 ieee80211_unref_node(&vap->iv_bss);
206 @@ -1134,6 +1139,40 @@
207         return ni;
208  }
209  
210 +#define WDSIFNAME ".sta%d"
211 +void ieee80211_wds_addif(struct ieee80211_node *ni)
212 +{
213 +       struct ieee80211vap *vap = ni->ni_vap;
214 +       struct ieee80211com *ic = vap->iv_ic;
215 +       struct ieee80211vap *avp;
216 +       char *name;
217 +
218 +       /* check if the node is split out already */
219 +       if (ni->ni_subif)
220 +               return;
221 +
222 +       name = kmalloc(strlen(vap->iv_dev->name) + sizeof(WDSIFNAME) + 1, GFP_KERNEL);
223 +       if (!name)
224 +               return;
225 +
226 +       strcpy(name, vap->iv_dev->name);
227 +       strcat(name, WDSIFNAME);
228 +       rtnl_lock();
229 +       avp = ieee80211_create_vap(ic, name, ic->ic_dev, IEEE80211_M_WDS, 0, vap);
230 +       kfree(name);
231 +       if (!avp)
232 +               goto done;
233 +
234 +       memcpy(avp->wds_mac, ni->ni_bssid, IEEE80211_ADDR_LEN);
235 +       avp->iv_wdsnode = ieee80211_ref_node(ni);
236 +       ni->ni_subif = avp;
237 +       ic->ic_subifs++;
238 +
239 +done:
240 +       rtnl_unlock();
241 +}
242 +#undef WDSIFNAME
243 +
244  /* Add wds address to the node table */
245  int
246  #ifdef IEEE80211_DEBUG_REFCNT
247 @@ -2254,6 +2293,28 @@
248         }
249  }
250  
251 +static void
252 +ieee80211_subif_destroy(struct work_struct *work)
253 +{
254 +       struct ieee80211_node *ni = container_of(work, struct ieee80211_node, ni_destroy);
255 +       struct ieee80211vap *vap = ni->ni_subif;
256 +       struct ieee80211com *ic;
257 +
258 +       if (!vap)
259 +               goto done;
260 +
261 +       rtnl_lock();
262 +       ic = vap->iv_ic;
263 +       ni->ni_subif = NULL;
264 +       ieee80211_stop(vap->iv_dev);
265 +       ic->ic_vap_delete(vap);
266 +       ic->ic_subifs--;
267 +       rtnl_unlock();
268 +
269 +done:
270 +       ieee80211_unref_node(&ni);
271 +}
272 +
273  /*
274   * Handle bookkeeping for a station/neighbor leaving
275   * the bss when operating in ap or adhoc modes.
276 @@ -2270,6 +2331,12 @@
277                         ni, "station with aid %d leaves (refcnt %u)",
278                         IEEE80211_NODE_AID(ni), atomic_read(&ni->ni_refcnt));
279  
280 +       if (ni->ni_subif) {
281 +               ieee80211_ref_node(ni);
282 +               IEEE80211_INIT_WORK(&ni->ni_destroy, ieee80211_subif_destroy);
283 +               schedule_work(&ni->ni_destroy);
284 +       }
285 +
286         /* From this point onwards we can no longer find the node,
287          * so no more references are generated
288          */
289 --- a/net80211/ieee80211_linux.h
290 +++ b/net80211/ieee80211_linux.h
291 @@ -81,6 +81,12 @@
292  #endif
293  }
294  
295 +#ifndef container_of
296 +#define container_of(ptr, type, member) ({          \
297 +    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
298 +           (type *)( (char *)__mptr - offsetof(type,member) );})
299 +#endif
300 +
301  /*
302   * Task deferral
303   *
304 @@ -113,6 +119,29 @@
305  
306  #define        IEEE80211_RESCHEDULE    schedule
307  
308 +#include <linux/sched.h>
309 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
310 +#include <linux/tqueue.h>
311 +#define work_struct                    tq_struct
312 +#define schedule_work(t)               schedule_task((t))
313 +#define flush_scheduled_work()         flush_scheduled_tasks()
314 +#define IEEE80211_INIT_WORK(t, f) do {                         \
315 +       memset((t), 0, sizeof(struct tq_struct)); \
316 +       (t)->routine = (void (*)(void*)) (f);   \
317 +       (t)->data=(void *) (t);                 \
318 +} while (0)
319 +#else
320 +#include <linux/workqueue.h>
321 +
322 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
323 +#define IEEE80211_INIT_WORK(_t, _f)    INIT_WORK((_t), (void (*)(void *))(_f), (_t));
324 +#else
325 +#define IEEE80211_INIT_WORK(_t, _f)    INIT_WORK((_t), (_f));
326 +#endif
327 +
328 +#endif /* KERNEL_VERSION < 2.5.41 */
329 +
330 +
331  /* Locking */
332  /* NB: beware, spin_is_locked() is not usefully defined for !(DEBUG || SMP)
333   * because spinlocks do not exist in this configuration. Instead IRQs 
334 @@ -167,6 +196,18 @@
335         IEEE80211_VAPS_LOCK_ASSERT(_ic);                \
336         spin_unlock_bh(&(_ic)->ic_vapslock);            \
337  } while (0)
338 +#define        IEEE80211_VAPS_LOCK_IRQ(_ic) do {                                       \
339 +       unsigned long __vlockflags;                                     \
340 +       IEEE80211_VAPS_LOCK_CHECK(_ic);                                 \
341 +       spin_lock_irqsave(&(_ic)->ic_vapslock, __vlockflags);
342 +#define        IEEE80211_VAPS_UNLOCK_IRQ(_ic)                                  \
343 +       IEEE80211_VAPS_LOCK_ASSERT(_ic);                                        \
344 +       spin_unlock_irqrestore(&(_ic)->ic_vapslock, __vlockflags);      \
345 +} while (0)
346 +#define        IEEE80211_VAPS_UNLOCK_IRQ_EARLY(_ic)                                    \
347 +       IEEE80211_VAPS_LOCK_ASSERT(_ic);                                        \
348 +       spin_unlock_irqrestore(&(_ic)->ic_vapslock, __vlockflags);
349 +
350  
351  #if (defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)) && defined(spin_is_locked)
352  #define IEEE80211_VAPS_LOCK_ASSERT(_ic) \
353 --- a/net80211/ieee80211_proto.c
354 +++ b/net80211/ieee80211_proto.c
355 @@ -1081,6 +1081,8 @@
356  int
357  ieee80211_open(struct net_device *dev)
358  {
359 +       struct ieee80211vap *vap = dev->priv;
360 +
361         return ieee80211_init(dev, 0);
362  }
363  
364 @@ -1116,11 +1118,33 @@
365         struct ieee80211vap *vap = dev->priv;
366         struct ieee80211com *ic = vap->iv_ic;
367         struct net_device *parent = ic->ic_dev;
368 +       struct ieee80211_node *tni, *ni;
369  
370         IEEE80211_DPRINTF(vap,
371                 IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
372                 "%s\n", "stop running");
373  
374 +       /* get rid of all wds nodes while we're still locked */
375 +       do {
376 +               ni = NULL;
377 +
378 +               IEEE80211_NODE_TABLE_LOCK_IRQ(&ic->ic_sta);
379 +               TAILQ_FOREACH(tni, &ic->ic_sta.nt_node, ni_list) {
380 +                       if (tni->ni_vap != vap)
381 +                               continue;
382 +                       if (!tni->ni_subif)
383 +                               continue;
384 +                       ni = tni;
385 +                       break;
386 +               }
387 +               IEEE80211_NODE_TABLE_UNLOCK_IRQ(&ic->ic_sta);
388 +
389 +               if (!ni)
390 +                       break;
391 +
392 +               ieee80211_node_leave(ni);
393 +       } while (1);
394 +
395         ieee80211_new_state(vap, IEEE80211_S_INIT, -1);
396         if (dev->flags & IFF_RUNNING) {
397                 dev->flags &= ~IFF_RUNNING;             /* mark us stopped */
398 @@ -1342,9 +1366,9 @@
399         struct ieee80211com *ic = vap->iv_ic;
400         int rc;
401  
402 -       IEEE80211_VAPS_LOCK_BH(ic);
403 +       IEEE80211_VAPS_LOCK_IRQ(ic);
404         rc = vap->iv_newstate(vap, nstate, arg);
405 -       IEEE80211_VAPS_UNLOCK_BH(ic);
406 +       IEEE80211_VAPS_UNLOCK_IRQ(ic);
407         return rc;
408  }
409  
410 --- a/net80211/ieee80211.c
411 +++ b/net80211/ieee80211.c
412 @@ -599,8 +599,10 @@
413  
414         IEEE80211_CANCEL_TQUEUE(&vap->iv_stajoin1tq);
415         IEEE80211_LOCK_IRQ(ic);
416 -       if (vap->iv_wdsnode)
417 +       if (vap->iv_wdsnode) {
418 +               vap->iv_wdsnode->ni_subif = NULL;
419                 ieee80211_unref_node(&vap->iv_wdsnode);
420 +       }
421         if ((vap->iv_opmode == IEEE80211_M_WDS) &&
422                 (vap->iv_master != NULL))
423                 TAILQ_REMOVE(&vap->iv_master->iv_wdslinks, vap, iv_wdsnext);
424 --- a/ath/if_athvar.h
425 +++ b/ath/if_athvar.h
426 @@ -79,28 +79,6 @@
427  #define        tasklet_enable(t)       do { (void) t; local_bh_enable(); } while (0)
428  #endif /* !DECLARE_TASKLET */
429  
430 -#include <linux/sched.h>
431 -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
432 -#include <linux/tqueue.h>
433 -#define work_struct                    tq_struct
434 -#define schedule_work(t)               schedule_task((t))
435 -#define flush_scheduled_work()         flush_scheduled_tasks()
436 -#define ATH_INIT_WORK(t, f) do {                       \
437 -       memset((t), 0, sizeof(struct tq_struct)); \
438 -       (t)->routine = (void (*)(void*)) (f);   \
439 -       (t)->data=(void *) (t);                 \
440 -} while (0)
441 -#else
442 -#include <linux/workqueue.h>
443 -
444 -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
445 -#define ATH_INIT_WORK(_t, _f)  INIT_WORK((_t), (void (*)(void *))(_f), (_t));
446 -#else
447 -#define ATH_INIT_WORK(_t, _f)  INIT_WORK((_t), (_f));
448 -#endif
449 -
450 -#endif /* KERNEL_VERSION < 2.5.41 */
451 -
452  /*
453   * Guess how the interrupt handler should work.
454   */
455 --- a/net80211/ieee80211_output.c
456 +++ b/net80211/ieee80211_output.c
457 @@ -786,6 +786,8 @@
458                 hdrsize = sizeof(struct ieee80211_frame);
459  
460         SKB_CB(skb)->auth_pkt = (eh.ether_type == __constant_htons(ETHERTYPE_PAE));
461 +       if (!SKB_CB(skb)->auth_pkt && ni->ni_subif)
462 +               vap = ni->ni_subif;
463  
464         switch (vap->iv_opmode) {
465         case IEEE80211_M_IBSS: