some more fixes for wds sta separation
[openwrt.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 @@ -435,7 +436,7 @@
12  
13         switch (type) {
14         case IEEE80211_FC0_TYPE_DATA:
15 -               hdrspace = ieee80211_hdrspace(ic, wh);
16 +               hdrspace = ieee80211_hdrsize(wh);
17                 if (skb->len < hdrspace) {
18                         IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
19                                 wh, "data", "too short: len %u, expecting %u",
20 @@ -446,15 +447,20 @@
21                 switch (vap->iv_opmode) {
22                 case IEEE80211_M_STA:
23                         if ((dir != IEEE80211_FC1_DIR_FROMDS) &&
24 -                           (!((vap->iv_flags_ext & IEEE80211_FEXT_WDS) &&
25 -                           (dir == IEEE80211_FC1_DIR_DSTODS)))) {
26 +                           (!(vap->iv_flags_ext & IEEE80211_FEXT_WDS) &&
27 +                            (dir == IEEE80211_FC1_DIR_DSTODS))) {
28                                 IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY,
29                                         wh, "data", "invalid dir 0x%x", dir);
30                                 vap->iv_stats.is_rx_wrongdir++;
31                                 goto out;
32                         }
33  
34 -                       if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
35 +                       if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
36 +                               /* ignore 3-addr mcast if we're WDS STA */
37 +                               if ((vap->iv_flags_ext & IEEE80211_FEXT_WDS) &&
38 +                                       (dir != IEEE80211_FC1_DIR_DSTODS))
39 +                                       goto out;
40 +
41                                 /* Discard multicast if IFF_MULTICAST not set */
42                                 if ((0 != memcmp(wh->i_addr3, dev->broadcast, ETH_ALEN)) && 
43                                         (0 == (dev->flags & IFF_MULTICAST))) {
44 @@ -482,24 +488,6 @@
45                                         vap->iv_stats.is_rx_mcastecho++;
46                                         goto out;
47                                 }
48 -                               /* 
49 -                                * if it is brodcasted by me on behalf of
50 -                                * a station behind me, drop it.
51 -                                */
52 -                               if (vap->iv_flags_ext & IEEE80211_FEXT_WDS) {
53 -                                       struct ieee80211_node_table *nt;
54 -                                       struct ieee80211_node *ni_wds;
55 -                                       nt = &ic->ic_sta;
56 -                                       ni_wds = ieee80211_find_wds_node(nt, wh->i_addr3);
57 -                                       if (ni_wds) {
58 -                                               ieee80211_unref_node(&ni_wds);
59 -                                               IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT,
60 -                                                       wh, NULL, "%s",
61 -                                                       "multicast echo originated from node behind me");
62 -                                               vap->iv_stats.is_rx_mcastecho++;
63 -                                               goto out;
64 -                                       }
65 -                               }
66                         }
67                         break;
68                 case IEEE80211_M_IBSS:
69 @@ -541,6 +529,11 @@
70                                 vap->iv_stats.is_rx_notassoc++;
71                                 goto err;
72                         }
73 +
74 +                       /* subif isn't fully set up yet, drop the frame */
75 +                       if (ni->ni_subif == ni->ni_vap)
76 +                               goto err;
77 +
78                         /*
79                          * If we're a 4 address packet, make sure we have an entry in
80                          * the node table for the packet source address (addr4).
81 @@ -548,9 +541,16 @@
82                          */
83  
84                         /* check for wds link first */
85 -                       if (dir == IEEE80211_FC1_DIR_DSTODS) {
86 +                       if ((dir == IEEE80211_FC1_DIR_DSTODS) && !ni->ni_subif) {
87                                 struct ieee80211vap *avp;
88  
89 +                               if (vap->iv_flags_ext & IEEE80211_FEXT_WDSSEP) {
90 +                                       ieee80211_wds_addif(ni);
91 +                                       /* we must drop frames here until the interface has
92 +                                        * been fully separated, otherwise a bridge might get
93 +                                        * confused */
94 +                                       goto err;
95 +                               }
96                                 TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) {
97                                         if (!memcmp(avp->wds_mac, wh->i_addr2, IEEE80211_ADDR_LEN)) {
98                                                 IEEE80211_LOCK_IRQ(ni->ni_ic);
99 @@ -566,7 +566,7 @@
100                         }
101  
102                         /* XXX: Useless node mgmt API; make better */
103 -                       if ((dir == IEEE80211_FC1_DIR_DSTODS) && !ni_wds) {
104 +                       if ((dir == IEEE80211_FC1_DIR_DSTODS) && !ni_wds && !ni->ni_subif) {
105                                 struct ieee80211_node_table *nt = &ic->ic_sta;
106                                 struct ieee80211_frame_addr4 *wh4;
107  
108 @@ -626,6 +626,11 @@
109                         goto out;
110                 }
111  
112 +               /* check if there is any data left */
113 +               hdrspace = ieee80211_hdrspace(ic, wh);
114 +               if (skb->len < hdrspace)
115 +                       goto out;
116 +
117                 /*
118                  * Handle privacy requirements.  Note that we
119                  * must not be preempted from here until after
120 @@ -698,8 +703,12 @@
121                 if (! accept_data_frame(vap, ni, key, skb, eh))
122                         goto out;
123  
124 -               vap->iv_devstats.rx_packets++;
125 -               vap->iv_devstats.rx_bytes += skb->len;
126 +               if (ni->ni_subif && ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE)))
127 +                       stats = &ni->ni_subif->iv_devstats;
128 +               else
129 +                       stats = &vap->iv_devstats;
130 +               stats->rx_packets++;
131 +               stats->rx_bytes += skb->len;
132                 IEEE80211_NODE_STAT(ni, rx_data);
133                 IEEE80211_NODE_STAT_ADD(ni, rx_bytes, skb->len);
134                 ic->ic_lastdata = jiffies;
135 @@ -1132,6 +1141,13 @@
136                 dev = vap->iv_xrvap->iv_dev;
137  #endif
138  
139 +       /* if the node has a wds subif, move data frames there,
140 +        * but keep EAP traffic on the master */
141 +       if (ni->ni_subif && ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE))) {
142 +               vap = ni->ni_subif;
143 +               dev = vap->iv_dev;
144 +       }
145 +
146         /* perform as a bridge within the vap */
147         /* XXX intra-vap bridging only */
148         if (vap->iv_opmode == IEEE80211_M_HOSTAP &&
149 @@ -1157,7 +1173,15 @@
150                         if (ni1 != NULL) {
151                                 if (ni1->ni_vap == vap &&
152                                     ieee80211_node_is_authorized(ni1) &&
153 +                                       !ni1->ni_subif &&
154                                     ni1 != vap->iv_bss) {
155 +
156 +                                       /* tried to bridge to a subif, drop the packet */
157 +                                       if (ni->ni_subif) {
158 +                                               ieee80211_dev_kfree_skb(&skb);
159 +                                               return;
160 +                                       }
161 +
162                                         skb1 = skb;
163                                         skb = NULL;
164                                 }
165 --- a/net80211/ieee80211_ioctl.h
166 +++ b/net80211/ieee80211_ioctl.h
167 @@ -649,6 +649,7 @@
168         IEEE80211_PARAM_BGSCAN_THRESH           = 79,   /* bg scan rssi threshold */
169         IEEE80211_PARAM_RSSI_DIS_THR    = 80,   /* rssi threshold for disconnection */
170         IEEE80211_PARAM_RSSI_DIS_COUNT  = 81,   /* counter for rssi threshold */
171 +       IEEE80211_PARAM_WDS_SEP                 = 82,   /* move wds stations into separate interfaces */
172  };
173  
174  #define        SIOCG80211STATS                 (SIOCDEVPRIVATE+2)
175 --- a/net80211/ieee80211_node.h
176 +++ b/net80211/ieee80211_node.h
177 @@ -92,11 +92,13 @@
178   * the ieee80211com structure.
179   */
180  struct ieee80211_node {
181 -       struct ieee80211vap *ni_vap;
182 +       struct ieee80211vap *ni_vap, *ni_subif;
183         struct ieee80211com *ni_ic;
184         struct ieee80211_node_table *ni_table;
185         TAILQ_ENTRY(ieee80211_node) ni_list;
186         LIST_ENTRY(ieee80211_node) ni_hash;
187 +       struct work_struct ni_create;   /* task for creating a subif */
188 +       struct work_struct ni_destroy;  /* task for destroying a subif */
189         atomic_t ni_refcnt;
190         u_int ni_scangen;                       /* gen# for timeout scan */
191         u_int8_t ni_authmode;                   /* authentication algorithm */
192 @@ -430,5 +432,6 @@
193  void ieee80211_node_leave(struct ieee80211_node *);
194  u_int8_t ieee80211_getrssi(struct ieee80211com *);
195  int32_t ieee80211_get_node_count(struct ieee80211com *);
196 +void ieee80211_wds_addif(struct ieee80211_node *ni);
197  #endif /* _NET80211_IEEE80211_NODE_H_ */
198  
199 --- a/net80211/ieee80211_var.h
200 +++ b/net80211/ieee80211_var.h
201 @@ -322,6 +322,7 @@
202         u_int8_t ic_myaddr[IEEE80211_ADDR_LEN];
203         struct timer_list ic_inact;             /* mgmt/inactivity timer */
204  
205 +       unsigned int ic_subifs;
206         u_int32_t ic_flags;                     /* state flags */
207         u_int32_t ic_flags_ext;                 /* extension of state flags */
208         u_int32_t ic_caps;                      /* capabilities */
209 @@ -625,6 +626,7 @@
210  #define IEEE80211_FEXT_DROPUNENC_EAPOL 0x00000800      /* CONF: drop unencrypted eapol frames */
211  #define IEEE80211_FEXT_APPIE_UPDATE    0x00001000      /* STATE: beacon APP IE updated */
212  #define IEEE80211_FEXT_BGSCAN_THR      0x00002000      /* bgscan due to low rssi */
213 +#define IEEE80211_FEXT_WDSSEP          0x00004000      /* move wds clients into separate interfaces */
214  
215  #define IEEE80211_COM_UAPSD_ENABLE(_ic)                ((_ic)->ic_flags_ext |= IEEE80211_FEXT_UAPSD)
216  #define IEEE80211_COM_UAPSD_DISABLE(_ic)       ((_ic)->ic_flags_ext &= ~IEEE80211_FEXT_UAPSD)
217 --- a/net80211/ieee80211_wireless.c
218 +++ b/net80211/ieee80211_wireless.c
219 @@ -2867,6 +2867,14 @@
220                 else
221                         vap->iv_minrateindex = 0;
222                 break;
223 +       case IEEE80211_PARAM_WDS_SEP:
224 +               if (vap->iv_opmode != IEEE80211_M_HOSTAP)
225 +                       retv = -EINVAL;
226 +               else if (value)
227 +                       vap->iv_flags_ext |= IEEE80211_FEXT_WDSSEP;
228 +               else
229 +                       vap->iv_flags_ext &= ~IEEE80211_FEXT_WDSSEP;
230 +               break;
231  #ifdef ATH_REVERSE_ENGINEERING
232         case IEEE80211_PARAM_DUMPREGS:
233                 ieee80211_dump_registers(dev, info, w, extra);
234 @@ -3223,6 +3231,9 @@
235         case IEEE80211_PARAM_MINRATE:
236                 param[0] = vap->iv_minrateindex;
237                 break;
238 +       case IEEE80211_PARAM_WDS_SEP:
239 +               param[0] = !!(vap->iv_flags_ext & IEEE80211_FEXT_WDSSEP);
240 +               break;
241         default:
242                 return -EOPNOTSUPP;
243         }
244 @@ -4447,6 +4458,8 @@
245         struct ieee80211vap *vap = ni->ni_vap;
246         size_t ielen;
247  
248 +       if (req->vap->iv_wdsnode && ni->ni_subif)
249 +               vap = ni->ni_subif;
250         if (vap != req->vap && vap != req->vap->iv_xrvap)       /* only entries for this vap */
251                 return;
252         if ((vap->iv_opmode == IEEE80211_M_HOSTAP ||
253 @@ -4466,6 +4479,8 @@
254         size_t ielen, len;
255         u_int8_t *cp;
256  
257 +       if (req->vap->iv_wdsnode && ni->ni_subif)
258 +               vap = ni->ni_subif;
259         if (vap != req->vap && vap != req->vap->iv_xrvap)       /* only entries for this vap (or) xrvap */
260                 return;
261         if ((vap->iv_opmode == IEEE80211_M_HOSTAP ||
262 @@ -5767,6 +5782,10 @@
263          0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_minrate"},
264         { IEEE80211_IOCTL_SETSCANLIST,
265          IW_PRIV_TYPE_CHAR | 255, 0, "setscanlist"},
266 +       { IEEE80211_PARAM_WDS_SEP,
267 +        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wdssep"},
268 +       { IEEE80211_PARAM_WDS_SEP,
269 +        0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_wdssep"},
270  
271  #ifdef ATH_REVERSE_ENGINEERING
272         /*
273 @@ -5890,6 +5909,8 @@
274  ieee80211_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
275  {
276         struct ieee80211vap *vap = dev->priv;
277 +       struct ieee80211com *ic = vap->iv_ic;
278 +       struct ieee80211_node *ni;
279  
280         switch (cmd) {
281         case SIOCG80211STATS:
282 @@ -5898,8 +5919,20 @@
283         case SIOC80211IFDESTROY:
284                 if (!capable(CAP_NET_ADMIN))
285                         return -EPERM;
286 +               /* drop all node subifs */
287 +               TAILQ_FOREACH(ni, &ic->ic_sta.nt_node, ni_list) {
288 +                       struct ieee80211vap *avp = ni->ni_subif;
289 +
290 +                       if (ni->ni_vap != vap)
291 +                               continue;
292 +                       if (!avp)
293 +                               continue;
294 +                       ni->ni_subif = NULL;
295 +                       ieee80211_stop(avp->iv_dev);
296 +                       ic->ic_vap_delete(avp);
297 +               }
298                 ieee80211_stop(vap->iv_dev);    /* force state before cleanup */
299 -               vap->iv_ic->ic_vap_delete(vap);
300 +               ic->ic_vap_delete(vap);
301                 return 0;
302         case IEEE80211_IOCTL_GETKEY:
303                 return ieee80211_ioctl_getkey(dev, (struct iwreq *) ifr);
304 --- a/net80211/ieee80211_node.c
305 +++ b/net80211/ieee80211_node.c
306 @@ -47,6 +47,7 @@
307  #include <linux/netdevice.h>
308  #include <linux/etherdevice.h>
309  #include <linux/random.h>
310 +#include <linux/rtnetlink.h>
311  
312  #include "if_media.h"
313  
314 @@ -236,7 +237,11 @@
315  ieee80211_node_vdetach(struct ieee80211vap *vap)
316  {
317         struct ieee80211com *ic = vap->iv_ic;
318 +       struct ieee80211_node *ni;
319  
320 +       ni = vap->iv_wdsnode;
321 +       if (ni)
322 +               ni->ni_subif = NULL;
323         ieee80211_node_table_reset(&ic->ic_sta, vap);
324         if (vap->iv_bss != NULL) {
325                 ieee80211_unref_node(&vap->iv_bss);
326 @@ -1134,6 +1139,57 @@
327         return ni;
328  }
329  
330 +#define WDSIFNAME ".sta%d"
331 +static void
332 +ieee80211_wds_do_addif(struct work_struct *work)
333 +{
334 +       struct ieee80211_node *ni = container_of(work, struct ieee80211_node, ni_create);
335 +       struct ieee80211vap *vap = ni->ni_vap;
336 +       struct ieee80211com *ic = vap->iv_ic;
337 +       struct ieee80211vap *avp;
338 +       char *name;
339 +
340 +       rtnl_lock();
341 +       /* did we get cancelled by the destroy call? */
342 +       if (!ni->ni_subif)
343 +               goto done;
344 +
345 +       ni->ni_subif = NULL;
346 +       name = kmalloc(strlen(vap->iv_dev->name) + sizeof(WDSIFNAME) + 1, GFP_KERNEL);
347 +       if (!name)
348 +               goto done;
349 +
350 +       strcpy(name, vap->iv_dev->name);
351 +       strcat(name, WDSIFNAME);
352 +       avp = ieee80211_create_vap(ic, name, ic->ic_dev, IEEE80211_M_WDS, 0, vap);
353 +       kfree(name);
354 +       if (!avp)
355 +               goto done;
356 +
357 +       memcpy(avp->wds_mac, ni->ni_bssid, IEEE80211_ADDR_LEN);
358 +       avp->iv_wdsnode = ieee80211_ref_node(ni);
359 +       ni->ni_subif = avp;
360 +       ic->ic_subifs++;
361 +
362 +done:
363 +       rtnl_unlock();
364 +       ieee80211_unref_node(&ni);
365 +}
366 +#undef WDSIFNAME
367 +
368 +void ieee80211_wds_addif(struct ieee80211_node *ni)
369 +{
370 +       /* check if the node is split out already,
371 +        * or if we're in progress of setting up a new interface already */
372 +       if (ni->ni_subif)
373 +               return;
374 +
375 +       ieee80211_ref_node(ni);
376 +       ni->ni_subif = ni->ni_vap;
377 +       IEEE80211_INIT_WORK(&ni->ni_create, ieee80211_wds_do_addif);
378 +       schedule_work(&ni->ni_create);
379 +}
380 +
381  /* Add wds address to the node table */
382  int
383  #ifdef IEEE80211_DEBUG_REFCNT
384 @@ -2254,6 +2310,36 @@
385         }
386  }
387  
388 +static void
389 +ieee80211_subif_destroy(struct work_struct *work)
390 +{
391 +       struct ieee80211_node *ni = container_of(work, struct ieee80211_node, ni_destroy);
392 +       struct ieee80211vap *vap;
393 +       struct ieee80211com *ic;
394 +
395 +       rtnl_lock();
396 +       vap = ni->ni_subif;
397 +
398 +       /* if addif is waiting for the timer to fire, cancel! */
399 +       if (vap == ni->ni_vap) {
400 +               ni->ni_subif = NULL;
401 +               goto done;
402 +       }
403 +
404 +       if (!vap)
405 +               goto done;
406 +
407 +       ic = vap->iv_ic;
408 +       ni->ni_subif = NULL;
409 +       ieee80211_stop(vap->iv_dev);
410 +       ic->ic_vap_delete(vap);
411 +       ic->ic_subifs--;
412 +
413 +done:
414 +       ieee80211_unref_node(&ni);
415 +       rtnl_unlock();
416 +}
417 +
418  /*
419   * Handle bookkeeping for a station/neighbor leaving
420   * the bss when operating in ap or adhoc modes.
421 @@ -2270,6 +2356,12 @@
422                         ni, "station with aid %d leaves (refcnt %u)",
423                         IEEE80211_NODE_AID(ni), atomic_read(&ni->ni_refcnt));
424  
425 +       if (ni->ni_subif) {
426 +               ieee80211_ref_node(ni);
427 +               IEEE80211_INIT_WORK(&ni->ni_destroy, ieee80211_subif_destroy);
428 +               schedule_work(&ni->ni_destroy);
429 +       }
430 +
431         /* From this point onwards we can no longer find the node,
432          * so no more references are generated
433          */
434 --- a/net80211/ieee80211_linux.h
435 +++ b/net80211/ieee80211_linux.h
436 @@ -81,6 +81,12 @@
437  #endif
438  }
439  
440 +#ifndef container_of
441 +#define container_of(ptr, type, member) ({          \
442 +    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
443 +           (type *)( (char *)__mptr - offsetof(type,member) );})
444 +#endif
445 +
446  /*
447   * Task deferral
448   *
449 @@ -113,6 +119,29 @@
450  
451  #define        IEEE80211_RESCHEDULE    schedule
452  
453 +#include <linux/sched.h>
454 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
455 +#include <linux/tqueue.h>
456 +#define work_struct                    tq_struct
457 +#define schedule_work(t)               schedule_task((t))
458 +#define flush_scheduled_work()         flush_scheduled_tasks()
459 +#define IEEE80211_INIT_WORK(t, f) do {                         \
460 +       memset((t), 0, sizeof(struct tq_struct)); \
461 +       (t)->routine = (void (*)(void*)) (f);   \
462 +       (t)->data=(void *) (t);                 \
463 +} while (0)
464 +#else
465 +#include <linux/workqueue.h>
466 +
467 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
468 +#define IEEE80211_INIT_WORK(_t, _f)    INIT_WORK((_t), (void (*)(void *))(_f), (_t));
469 +#else
470 +#define IEEE80211_INIT_WORK(_t, _f)    INIT_WORK((_t), (_f));
471 +#endif
472 +
473 +#endif /* KERNEL_VERSION < 2.5.41 */
474 +
475 +
476  /* Locking */
477  /* NB: beware, spin_is_locked() is not usefully defined for !(DEBUG || SMP)
478   * because spinlocks do not exist in this configuration. Instead IRQs 
479 @@ -167,6 +196,18 @@
480         IEEE80211_VAPS_LOCK_ASSERT(_ic);                \
481         spin_unlock_bh(&(_ic)->ic_vapslock);            \
482  } while (0)
483 +#define        IEEE80211_VAPS_LOCK_IRQ(_ic) do {                                       \
484 +       unsigned long __vlockflags;                                     \
485 +       IEEE80211_VAPS_LOCK_CHECK(_ic);                                 \
486 +       spin_lock_irqsave(&(_ic)->ic_vapslock, __vlockflags);
487 +#define        IEEE80211_VAPS_UNLOCK_IRQ(_ic)                                  \
488 +       IEEE80211_VAPS_LOCK_ASSERT(_ic);                                        \
489 +       spin_unlock_irqrestore(&(_ic)->ic_vapslock, __vlockflags);      \
490 +} while (0)
491 +#define        IEEE80211_VAPS_UNLOCK_IRQ_EARLY(_ic)                                    \
492 +       IEEE80211_VAPS_LOCK_ASSERT(_ic);                                        \
493 +       spin_unlock_irqrestore(&(_ic)->ic_vapslock, __vlockflags);
494 +
495  
496  #if (defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)) && defined(spin_is_locked)
497  #define IEEE80211_VAPS_LOCK_ASSERT(_ic) \
498 --- a/net80211/ieee80211_proto.c
499 +++ b/net80211/ieee80211_proto.c
500 @@ -1081,6 +1081,8 @@
501  int
502  ieee80211_open(struct net_device *dev)
503  {
504 +       struct ieee80211vap *vap = dev->priv;
505 +
506         return ieee80211_init(dev, 0);
507  }
508  
509 @@ -1116,11 +1118,33 @@
510         struct ieee80211vap *vap = dev->priv;
511         struct ieee80211com *ic = vap->iv_ic;
512         struct net_device *parent = ic->ic_dev;
513 +       struct ieee80211_node *tni, *ni;
514  
515         IEEE80211_DPRINTF(vap,
516                 IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG,
517                 "%s\n", "stop running");
518  
519 +       /* get rid of all wds nodes while we're still locked */
520 +       do {
521 +               ni = NULL;
522 +
523 +               IEEE80211_NODE_TABLE_LOCK_IRQ(&ic->ic_sta);
524 +               TAILQ_FOREACH(tni, &ic->ic_sta.nt_node, ni_list) {
525 +                       if (tni->ni_vap != vap)
526 +                               continue;
527 +                       if (!tni->ni_subif)
528 +                               continue;
529 +                       ni = tni;
530 +                       break;
531 +               }
532 +               IEEE80211_NODE_TABLE_UNLOCK_IRQ(&ic->ic_sta);
533 +
534 +               if (!ni)
535 +                       break;
536 +
537 +               ieee80211_node_leave(ni);
538 +       } while (1);
539 +
540         ieee80211_new_state(vap, IEEE80211_S_INIT, -1);
541         if (dev->flags & IFF_RUNNING) {
542                 dev->flags &= ~IFF_RUNNING;             /* mark us stopped */
543 @@ -1342,9 +1366,9 @@
544         struct ieee80211com *ic = vap->iv_ic;
545         int rc;
546  
547 -       IEEE80211_VAPS_LOCK_BH(ic);
548 +       IEEE80211_VAPS_LOCK_IRQ(ic);
549         rc = vap->iv_newstate(vap, nstate, arg);
550 -       IEEE80211_VAPS_UNLOCK_BH(ic);
551 +       IEEE80211_VAPS_UNLOCK_IRQ(ic);
552         return rc;
553  }
554  
555 @@ -1630,6 +1654,7 @@
556                  */
557                 if (ni->ni_authmode != IEEE80211_AUTH_8021X)
558                         ieee80211_node_authorize(ni);
559 +
560  #ifdef ATH_SUPERG_XR
561                 /*
562                  * fire a timer to bring up XR vap if configured.
563 @@ -1885,8 +1910,15 @@
564                 if (ostate == IEEE80211_S_SCAN || 
565                     ostate == IEEE80211_S_AUTH ||
566                     ostate == IEEE80211_S_ASSOC) {
567 +
568                         /* Transition (S_SCAN|S_AUTH|S_ASSOC) -> S_RUN */
569                         __ieee80211_newstate(vap, nstate, arg);
570 +
571 +                       /* if we're in wds, let the ap know that we're doing this */
572 +                       if ((vap->iv_opmode == IEEE80211_M_STA) &&
573 +                               (vap->iv_flags_ext & IEEE80211_FEXT_WDS))
574 +                                       ieee80211_send_nulldata(ieee80211_ref_node(vap->iv_bss));
575 +
576                         /* Then bring up all other vaps pending on the scan */
577                         dstate = get_dominant_state(ic);
578                         if (dstate == IEEE80211_S_RUN) {
579 --- a/net80211/ieee80211.c
580 +++ b/net80211/ieee80211.c
581 @@ -373,10 +373,25 @@
582  ieee80211_ifdetach(struct ieee80211com *ic)
583  {
584         struct ieee80211vap *vap;
585 +       int count;
586 +
587 +       /* bring down all vaps */
588 +       TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
589 +               ieee80211_stop(vap->iv_dev);
590 +       }
591 +
592 +       /* wait for all subifs to disappear */
593 +       do {
594 +               schedule();
595 +               rtnl_lock();
596 +               count = ic->ic_subifs;
597 +               rtnl_unlock();
598 +       } while (count > 0);
599  
600         rtnl_lock();
601 -       while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL)
602 +       while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) {
603                 ic->ic_vap_delete(vap);
604 +       }
605         rtnl_unlock();
606  
607         del_timer(&ic->ic_dfs_excl_timer);
608 @@ -599,8 +614,10 @@
609  
610         IEEE80211_CANCEL_TQUEUE(&vap->iv_stajoin1tq);
611         IEEE80211_LOCK_IRQ(ic);
612 -       if (vap->iv_wdsnode)
613 +       if (vap->iv_wdsnode) {
614 +               vap->iv_wdsnode->ni_subif = NULL;
615                 ieee80211_unref_node(&vap->iv_wdsnode);
616 +       }
617         if ((vap->iv_opmode == IEEE80211_M_WDS) &&
618                 (vap->iv_master != NULL))
619                 TAILQ_REMOVE(&vap->iv_master->iv_wdslinks, vap, iv_wdsnext);
620 --- a/ath/if_athvar.h
621 +++ b/ath/if_athvar.h
622 @@ -79,28 +79,6 @@
623  #define        tasklet_enable(t)       do { (void) t; local_bh_enable(); } while (0)
624  #endif /* !DECLARE_TASKLET */
625  
626 -#include <linux/sched.h>
627 -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
628 -#include <linux/tqueue.h>
629 -#define work_struct                    tq_struct
630 -#define schedule_work(t)               schedule_task((t))
631 -#define flush_scheduled_work()         flush_scheduled_tasks()
632 -#define ATH_INIT_WORK(t, f) do {                       \
633 -       memset((t), 0, sizeof(struct tq_struct)); \
634 -       (t)->routine = (void (*)(void*)) (f);   \
635 -       (t)->data=(void *) (t);                 \
636 -} while (0)
637 -#else
638 -#include <linux/workqueue.h>
639 -
640 -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
641 -#define ATH_INIT_WORK(_t, _f)  INIT_WORK((_t), (void (*)(void *))(_f), (_t));
642 -#else
643 -#define ATH_INIT_WORK(_t, _f)  INIT_WORK((_t), (_f));
644 -#endif
645 -
646 -#endif /* KERNEL_VERSION < 2.5.41 */
647 -
648  /*
649   * Guess how the interrupt handler should work.
650   */
651 --- a/net80211/ieee80211_output.c
652 +++ b/net80211/ieee80211_output.c
653 @@ -261,6 +261,10 @@
654                 goto bad;
655         }
656  
657 +       if (ni->ni_subif && (vap != ni->ni_subif) &&
658 +               ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE)))
659 +               goto bad;
660 +
661         /* calculate priority so drivers can find the TX queue */
662         if (ieee80211_classify(ni, skb)) {
663                 IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni,
664 @@ -340,20 +344,33 @@
665   * constructing a frame as it sets i_fc[1]; other bits can
666   * then be or'd in.
667   */
668 -static void
669 +static struct ieee80211_frame *
670  ieee80211_send_setup(struct ieee80211vap *vap,
671         struct ieee80211_node *ni,
672 -       struct ieee80211_frame *wh,
673 +       struct sk_buff *skb,
674         int type,
675         const u_int8_t sa[IEEE80211_ADDR_LEN],
676         const u_int8_t da[IEEE80211_ADDR_LEN],
677         const u_int8_t bssid[IEEE80211_ADDR_LEN])
678  {
679  #define        WH4(wh) ((struct ieee80211_frame_addr4 *)wh)
680 +       struct ieee80211_frame *wh;
681 +       int len = sizeof(struct ieee80211_frame);
682 +       int opmode = vap->iv_opmode;
683 +
684 +       if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) {
685 +               if ((opmode == IEEE80211_M_STA) &&
686 +                       (vap->iv_flags_ext & IEEE80211_FEXT_WDS))
687 +                       opmode = IEEE80211_M_WDS;
688 +
689 +               if (opmode == IEEE80211_M_WDS)
690 +                       len = sizeof(struct ieee80211_frame_addr4);
691 +       }
692  
693 +       wh = (struct ieee80211_frame *)skb_push(skb, len);
694         wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type;
695         if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) {
696 -               switch (vap->iv_opmode) {
697 +               switch (opmode) {
698                 case IEEE80211_M_STA:
699                         wh->i_fc[1] = IEEE80211_FC1_DIR_TODS;
700                         IEEE80211_ADDR_COPY(wh->i_addr1, bssid);
701 @@ -395,6 +412,8 @@
702         *(__le16 *)&wh->i_seq[0] =
703             htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT);
704         ni->ni_txseqs[0]++;
705 +
706 +       return wh;
707  #undef WH4
708  }
709  
710 @@ -416,9 +435,7 @@
711  
712         SKB_CB(skb)->ni = ni;
713  
714 -       wh = (struct ieee80211_frame *)
715 -               skb_push(skb, sizeof(struct ieee80211_frame));
716 -       ieee80211_send_setup(vap, ni, wh,
717 +       wh = ieee80211_send_setup(vap, ni, skb,
718                 IEEE80211_FC0_TYPE_MGT | type,
719                 vap->iv_myaddr, ni->ni_macaddr, vap->iv_bssid);
720         /* XXX power management */
721 @@ -464,6 +481,9 @@
722         struct ieee80211_frame *wh;
723         u_int8_t *frm;
724  
725 +       if (ni->ni_subif)
726 +               vap = ni->ni_subif;
727 +
728         skb = ieee80211_getmgtframe(&frm, 0);
729         if (skb == NULL) {
730                 /* XXX debug msg */
731 @@ -472,9 +492,7 @@
732                 return -ENOMEM;
733         }
734  
735 -       wh = (struct ieee80211_frame *)
736 -               skb_push(skb, sizeof(struct ieee80211_frame));
737 -       ieee80211_send_setup(vap, ni, wh,
738 +       wh = ieee80211_send_setup(vap, ni, skb,
739                 IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA,
740                 vap->iv_myaddr, ni->ni_macaddr, vap->iv_bssid);
741         /* NB: power management bit is never sent by an AP */
742 @@ -512,6 +530,7 @@
743         struct sk_buff *skb;
744         struct ieee80211_qosframe *qwh;
745         u_int8_t *frm;
746 +       u_int8_t *i_qos;
747         int tid;
748  
749         skb = ieee80211_getmgtframe(&frm, 2);
750 @@ -523,11 +542,12 @@
751         SKB_CB(skb)->ni = ieee80211_ref_node(ni);
752  
753         skb->priority = ac;
754 -       qwh = (struct ieee80211_qosframe *)skb_push(skb, sizeof(struct ieee80211_qosframe));
755  
756 -       qwh = (struct ieee80211_qosframe *)skb->data;
757 +       /* grab a pointer to QoS control and also compensate for the header length
758 +        * difference between QoS and non-QoS frame */
759 +       i_qos = skb_push(skb, sizeof(struct ieee80211_qosframe) - sizeof(struct ieee80211_frame));
760  
761 -       ieee80211_send_setup(vap, ni, (struct ieee80211_frame *)qwh,
762 +       qwh = (struct ieee80211_qosframe *) ieee80211_send_setup(vap, ni, skb,
763                 IEEE80211_FC0_TYPE_DATA,
764                 vap->iv_myaddr, /* SA */
765                 ni->ni_macaddr, /* DA */
766 @@ -541,10 +561,10 @@
767  
768         /* map from access class/queue to 11e header priority value */
769         tid = WME_AC_TO_TID(ac);
770 -       qwh->i_qos[0] = tid & IEEE80211_QOS_TID;
771 +       i_qos[0] = tid & IEEE80211_QOS_TID;
772         if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy)
773                 qwh->i_qos[0] |= (1 << IEEE80211_QOS_ACKPOLICY_S) & IEEE80211_QOS_ACKPOLICY;
774 -       qwh->i_qos[1] = 0;
775 +       i_qos[1] = 0;
776  
777         IEEE80211_NODE_STAT(ni, tx_data);
778  
779 @@ -786,6 +806,8 @@
780                 hdrsize = sizeof(struct ieee80211_frame);
781  
782         SKB_CB(skb)->auth_pkt = (eh.ether_type == __constant_htons(ETHERTYPE_PAE));
783 +       if (!SKB_CB(skb)->auth_pkt && ni->ni_subif)
784 +               vap = ni->ni_subif;
785  
786         switch (vap->iv_opmode) {
787         case IEEE80211_M_IBSS:
788 @@ -805,20 +827,9 @@
789                         ismulticast = IEEE80211_IS_MULTICAST(eh.ether_dhost);
790                 break;
791         case IEEE80211_M_STA:
792 -               if ((vap->iv_flags_ext & IEEE80211_FEXT_WDS) &&
793 -                   !IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)) {
794 +               if (vap->iv_flags_ext & IEEE80211_FEXT_WDS) {
795                         use4addr = 1;
796 -                       ismulticast = IEEE80211_IS_MULTICAST(ni->ni_macaddr);
797 -                       /* Add a WDS entry to the station VAP */
798 -                       if (IEEE80211_IS_MULTICAST(eh.ether_dhost)) {
799 -                               struct ieee80211_node_table *nt = &ic->ic_sta;
800 -                               struct ieee80211_node *ni_wds 
801 -                                       = ieee80211_find_wds_node(nt, eh.ether_shost);
802 -                               if (ni_wds)
803 -                                       ieee80211_unref_node(&ni_wds);
804 -                               else
805 -                                       ieee80211_add_wds_addr(nt, ni, eh.ether_shost, 0);
806 -                       }
807 +                       ismulticast = 0;
808                 } else
809                         ismulticast = IEEE80211_IS_MULTICAST(vap->iv_bssid);
810                 break;
811 @@ -1689,9 +1700,7 @@
812  
813         SKB_CB(skb)->ni = ieee80211_ref_node(ni);
814  
815 -       wh = (struct ieee80211_frame *)
816 -               skb_push(skb, sizeof(struct ieee80211_frame));
817 -       ieee80211_send_setup(vap, ni, wh,
818 +       wh = ieee80211_send_setup(vap, ni, skb,
819                 IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ,
820                 sa, da, bssid);
821         /* XXX power management? */
822 --- a/net80211/ieee80211_linux.c
823 +++ b/net80211/ieee80211_linux.c
824 @@ -145,7 +145,7 @@
825         struct sk_buff *skb;
826         u_int len;
827  
828 -       len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4);
829 +       len = roundup(sizeof(struct ieee80211_frame_addr4) + pktlen, 4);
830  #ifdef IEEE80211_DEBUG_REFCNT
831         skb = ieee80211_dev_alloc_skb_debug(len + align - 1, func, line);
832  #else
833 @@ -161,7 +161,7 @@
834                 SKB_CB(skb)->flags = 0;
835                 SKB_CB(skb)->next = NULL;
836  
837 -               skb_reserve(skb, sizeof(struct ieee80211_frame));
838 +               skb_reserve(skb, sizeof(struct ieee80211_frame_addr4));
839                 *frm = skb_put(skb, pktlen);
840         }
841         return skb;