| 1 | --- a/ath/if_ath.c |
| 2 | +++ b/ath/if_ath.c |
| 3 | @@ -124,7 +124,7 @@ enum { |
| 4 | }; |
| 5 | |
| 6 | static struct ieee80211vap *ath_vap_create(struct ieee80211com *, |
| 7 | - const char *, int, int, struct net_device *); |
| 8 | + const char *, int, int, struct net_device *, struct ieee80211vap *); |
| 9 | static void ath_vap_delete(struct ieee80211vap *); |
| 10 | static int ath_init(struct net_device *); |
| 11 | static int ath_set_ack_bitrate(struct ath_softc *, int); |
| 12 | @@ -1123,8 +1123,6 @@ ath_attach(u_int16_t devid, struct net_d |
| 13 | autocreatemode = IEEE80211_M_IBSS; |
| 14 | else if (!strcmp(autocreate, "ahdemo")) |
| 15 | autocreatemode = IEEE80211_M_AHDEMO; |
| 16 | - else if (!strcmp(autocreate, "wds")) |
| 17 | - autocreatemode = IEEE80211_M_WDS; |
| 18 | else if (!strcmp(autocreate, "monitor")) |
| 19 | autocreatemode = IEEE80211_M_MONITOR; |
| 20 | else { |
| 21 | @@ -1137,7 +1135,7 @@ ath_attach(u_int16_t devid, struct net_d |
| 22 | if (autocreatemode != -1) { |
| 23 | rtnl_lock(); |
| 24 | vap = ieee80211_create_vap(ic, "ath%d", dev, |
| 25 | - autocreatemode, 0); |
| 26 | + autocreatemode, 0, NULL); |
| 27 | rtnl_unlock(); |
| 28 | if (vap == NULL) |
| 29 | EPRINTF(sc, "Autocreation of %s VAP failed.", autocreate); |
| 30 | @@ -1230,14 +1228,14 @@ ath_detach(struct net_device *dev) |
| 31 | |
| 32 | static struct ieee80211vap * |
| 33 | ath_vap_create(struct ieee80211com *ic, const char *name, |
| 34 | - int opmode, int flags, struct net_device *mdev) |
| 35 | + int opmode, int flags, struct net_device *mdev, struct ieee80211vap *master) |
| 36 | { |
| 37 | struct ath_softc *sc = ic->ic_dev->priv; |
| 38 | struct ath_hal *ah = sc->sc_ah; |
| 39 | struct net_device *dev; |
| 40 | struct ath_vap *avp; |
| 41 | struct ieee80211vap *vap; |
| 42 | - int ic_opmode; |
| 43 | + int ic_opmode = IEEE80211_M_STA; |
| 44 | |
| 45 | if (ic->ic_dev->flags & IFF_RUNNING) { |
| 46 | /* needs to disable hardware too */ |
| 47 | @@ -1271,8 +1269,12 @@ ath_vap_create(struct ieee80211com *ic, |
| 48 | } else |
| 49 | ic_opmode = opmode; |
| 50 | break; |
| 51 | - case IEEE80211_M_HOSTAP: |
| 52 | case IEEE80211_M_WDS: |
| 53 | + ic_opmode = ic->ic_opmode; |
| 54 | + if (!master) |
| 55 | + return NULL; |
| 56 | + break; |
| 57 | + case IEEE80211_M_HOSTAP: |
| 58 | /* permit multiple APs and/or WDS links */ |
| 59 | /* XXX sta+ap for repeater/bridge application */ |
| 60 | if ((sc->sc_nvaps != 0) && (ic->ic_opmode == IEEE80211_M_STA)) |
| 61 | @@ -1304,7 +1306,7 @@ ath_vap_create(struct ieee80211com *ic, |
| 62 | } |
| 63 | |
| 64 | avp = dev->priv; |
| 65 | - ieee80211_vap_setup(ic, dev, name, opmode, flags); |
| 66 | + ieee80211_vap_setup(ic, dev, name, opmode, flags, master); |
| 67 | /* override with driver methods */ |
| 68 | vap = &avp->av_vap; |
| 69 | avp->av_newstate = vap->iv_newstate; |
| 70 | @@ -4209,8 +4211,7 @@ ath_calcrxfilter(struct ath_softc *sc) |
| 71 | if (ic->ic_opmode == IEEE80211_M_STA || |
| 72 | sc->sc_opmode == HAL_M_IBSS || /* NB: AHDEMO too */ |
| 73 | (sc->sc_nostabeacons) || sc->sc_scanning || |
| 74 | - ((ic->ic_opmode == IEEE80211_M_HOSTAP) && |
| 75 | - (ic->ic_protmode != IEEE80211_PROT_NONE))) |
| 76 | + (ic->ic_opmode == IEEE80211_M_HOSTAP)) |
| 77 | rfilt |= HAL_RX_FILTER_BEACON; |
| 78 | if (sc->sc_nmonvaps > 0) |
| 79 | rfilt |= (HAL_RX_FILTER_CONTROL | HAL_RX_FILTER_BEACON | |
| 80 | @@ -9032,8 +9033,6 @@ ath_calibrate(unsigned long arg) |
| 81 | * set sc->beacons if we might need to restart |
| 82 | * them after ath_reset. */ |
| 83 | if (!sc->sc_beacons && |
| 84 | - (TAILQ_FIRST(&ic->ic_vaps)->iv_opmode != |
| 85 | - IEEE80211_M_WDS) && |
| 86 | !txcont_was_active && |
| 87 | !sc->sc_dfs_cac) { |
| 88 | sc->sc_beacons = 1; |
| 89 | --- a/net80211/ieee80211.c |
| 90 | +++ b/net80211/ieee80211.c |
| 91 | @@ -373,10 +373,25 @@ void |
| 92 | ieee80211_ifdetach(struct ieee80211com *ic) |
| 93 | { |
| 94 | struct ieee80211vap *vap; |
| 95 | + int count; |
| 96 | + |
| 97 | + /* bring down all vaps */ |
| 98 | + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| 99 | + ieee80211_stop(vap->iv_dev); |
| 100 | + } |
| 101 | + |
| 102 | + /* wait for all subifs to disappear */ |
| 103 | + do { |
| 104 | + schedule(); |
| 105 | + rtnl_lock(); |
| 106 | + count = ic->ic_subifs; |
| 107 | + rtnl_unlock(); |
| 108 | + } while (count > 0); |
| 109 | |
| 110 | rtnl_lock(); |
| 111 | - while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) |
| 112 | + while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) { |
| 113 | ic->ic_vap_delete(vap); |
| 114 | + } |
| 115 | rtnl_unlock(); |
| 116 | |
| 117 | del_timer(&ic->ic_dfs_excl_timer); |
| 118 | @@ -396,7 +411,7 @@ EXPORT_SYMBOL(ieee80211_ifdetach); |
| 119 | |
| 120 | int |
| 121 | ieee80211_vap_setup(struct ieee80211com *ic, struct net_device *dev, |
| 122 | - const char *name, int opmode, int flags) |
| 123 | + const char *name, int opmode, int flags, struct ieee80211vap *master) |
| 124 | { |
| 125 | #define IEEE80211_C_OPMODE \ |
| 126 | (IEEE80211_C_IBSS | IEEE80211_C_HOSTAP | IEEE80211_C_AHDEMO | \ |
| 127 | @@ -510,9 +525,18 @@ ieee80211_vap_setup(struct ieee80211com |
| 128 | |
| 129 | vap->iv_monitor_crc_errors = 0; |
| 130 | vap->iv_monitor_phy_errors = 0; |
| 131 | + TAILQ_INIT(&vap->iv_wdslinks); |
| 132 | |
| 133 | - IEEE80211_ADDR_COPY(vap->iv_myaddr, ic->ic_myaddr); |
| 134 | - IEEE80211_ADDR_COPY(vap->iv_bssid, ic->ic_myaddr); |
| 135 | + if (master && (vap->iv_opmode == IEEE80211_M_WDS)) { |
| 136 | + vap->iv_master = master; |
| 137 | + TAILQ_INSERT_TAIL(&master->iv_wdslinks, vap, iv_wdsnext); |
| 138 | + /* use the same BSSID as the master interface */ |
| 139 | + IEEE80211_ADDR_COPY(vap->iv_myaddr, vap->iv_master->iv_myaddr); |
| 140 | + IEEE80211_ADDR_COPY(vap->iv_bssid, vap->iv_master->iv_myaddr); |
| 141 | + } else { |
| 142 | + IEEE80211_ADDR_COPY(vap->iv_myaddr, ic->ic_myaddr); |
| 143 | + IEEE80211_ADDR_COPY(vap->iv_bssid, ic->ic_myaddr); |
| 144 | + } |
| 145 | /* NB: Defer setting dev_addr so driver can override */ |
| 146 | |
| 147 | ieee80211_crypto_vattach(vap); |
| 148 | @@ -547,7 +571,8 @@ ieee80211_vap_attach(struct ieee80211vap |
| 149 | ifmedia_set(&vap->iv_media, imr.ifm_active); |
| 150 | |
| 151 | IEEE80211_LOCK_IRQ(ic); |
| 152 | - TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next); |
| 153 | + if (vap->iv_opmode != IEEE80211_M_WDS) |
| 154 | + TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next); |
| 155 | IEEE80211_UNLOCK_IRQ(ic); |
| 156 | |
| 157 | IEEE80211_ADDR_COPY(dev->dev_addr, vap->iv_myaddr); |
| 158 | @@ -579,10 +604,27 @@ ieee80211_vap_detach(struct ieee80211vap |
| 159 | { |
| 160 | struct ieee80211com *ic = vap->iv_ic; |
| 161 | struct net_device *dev = vap->iv_dev; |
| 162 | + struct ieee80211vap *avp; |
| 163 | + |
| 164 | + /* Drop all WDS links that belong to this vap */ |
| 165 | + while ((avp = TAILQ_FIRST(&vap->iv_wdslinks)) != NULL) { |
| 166 | + if (avp->iv_state != IEEE80211_S_INIT) |
| 167 | + ieee80211_stop(avp->iv_dev); |
| 168 | + ic->ic_vap_delete(avp); |
| 169 | + } |
| 170 | |
| 171 | IEEE80211_CANCEL_TQUEUE(&vap->iv_stajoin1tq); |
| 172 | IEEE80211_LOCK_IRQ(ic); |
| 173 | - TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next); |
| 174 | + if (vap->iv_wdsnode) { |
| 175 | + vap->iv_wdsnode->ni_subif = NULL; |
| 176 | + ieee80211_unref_node(&vap->iv_wdsnode); |
| 177 | + } |
| 178 | + if ((vap->iv_opmode == IEEE80211_M_WDS) && |
| 179 | + (vap->iv_master != NULL)) |
| 180 | + TAILQ_REMOVE(&vap->iv_master->iv_wdslinks, vap, iv_wdsnext); |
| 181 | + else |
| 182 | + TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next); |
| 183 | + |
| 184 | if (TAILQ_EMPTY(&ic->ic_vaps)) /* reset to supported mode */ |
| 185 | ic->ic_opmode = IEEE80211_M_STA; |
| 186 | IEEE80211_UNLOCK_IRQ(ic); |
| 187 | --- a/net80211/ieee80211_ioctl.h |
| 188 | +++ b/net80211/ieee80211_ioctl.h |
| 189 | @@ -474,7 +474,7 @@ struct ieee80211req { |
| 190 | #define IEEE80211_IOC_DTIM_PERIOD 52 /* DTIM period (beacons) */ |
| 191 | #define IEEE80211_IOC_BEACON_INTERVAL 53 /* beacon interval (ms) */ |
| 192 | #define IEEE80211_IOC_ADDMAC 54 /* add sta to MAC ACL table */ |
| 193 | -#define IEEE80211_IOC_DELMAC 55 /* del sta from MAC ACL table */ |
| 194 | +#define IEEE80211_IOC_SETMAC 55 /* set interface wds mac addr */ |
| 195 | #define IEEE80211_IOC_FF 56 /* ATH fast frames (on, off) */ |
| 196 | #define IEEE80211_IOC_TURBOP 57 /* ATH turbo' (on, off) */ |
| 197 | #define IEEE80211_IOC_APPIEBUF 58 /* IE in the management frame */ |
| 198 | @@ -552,8 +552,8 @@ struct ieee80211req_scan_result { |
| 199 | #define IEEE80211_IOCTL_HALMAP (SIOCIWFIRSTPRIV+21) |
| 200 | #define IEEE80211_IOCTL_ADDMAC (SIOCIWFIRSTPRIV+22) |
| 201 | #define IEEE80211_IOCTL_DELMAC (SIOCIWFIRSTPRIV+24) |
| 202 | -#define IEEE80211_IOCTL_WDSADDMAC (SIOCIWFIRSTPRIV+26) |
| 203 | -#define IEEE80211_IOCTL_WDSDELMAC (SIOCIWFIRSTPRIV+28) |
| 204 | +#define IEEE80211_IOCTL_WDSADDMAC (SIOCIWFIRSTPRIV+25) |
| 205 | +#define IEEE80211_IOCTL_WDSSETMAC (SIOCIWFIRSTPRIV+26) |
| 206 | #define IEEE80211_IOCTL_KICKMAC (SIOCIWFIRSTPRIV+30) |
| 207 | #define IEEE80211_IOCTL_SETSCANLIST (SIOCIWFIRSTPRIV+31) |
| 208 | |
| 209 | @@ -649,6 +649,7 @@ enum { |
| 210 | IEEE80211_PARAM_BGSCAN_THRESH = 79, /* bg scan rssi threshold */ |
| 211 | IEEE80211_PARAM_RSSI_DIS_THR = 80, /* rssi threshold for disconnection */ |
| 212 | IEEE80211_PARAM_RSSI_DIS_COUNT = 81, /* counter for rssi threshold */ |
| 213 | + IEEE80211_PARAM_WDS_SEP = 82, /* move wds stations into separate interfaces */ |
| 214 | }; |
| 215 | |
| 216 | #define SIOCG80211STATS (SIOCDEVPRIVATE+2) |
| 217 | --- a/net80211/ieee80211_linux.h |
| 218 | +++ b/net80211/ieee80211_linux.h |
| 219 | @@ -81,6 +81,12 @@ set_quality(struct iw_quality *iq, u_int |
| 220 | #endif |
| 221 | } |
| 222 | |
| 223 | +#ifndef container_of |
| 224 | +#define container_of(ptr, type, member) ({ \ |
| 225 | + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ |
| 226 | + (type *)( (char *)__mptr - offsetof(type,member) );}) |
| 227 | +#endif |
| 228 | + |
| 229 | /* |
| 230 | * Task deferral |
| 231 | * |
| 232 | @@ -113,6 +119,29 @@ typedef void *IEEE80211_TQUEUE_ARG; |
| 233 | |
| 234 | #define IEEE80211_RESCHEDULE schedule |
| 235 | |
| 236 | +#include <linux/sched.h> |
| 237 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41) |
| 238 | +#include <linux/tqueue.h> |
| 239 | +#define work_struct tq_struct |
| 240 | +#define schedule_work(t) schedule_task((t)) |
| 241 | +#define flush_scheduled_work() flush_scheduled_tasks() |
| 242 | +#define IEEE80211_INIT_WORK(t, f) do { \ |
| 243 | + memset((t), 0, sizeof(struct tq_struct)); \ |
| 244 | + (t)->routine = (void (*)(void*)) (f); \ |
| 245 | + (t)->data=(void *) (t); \ |
| 246 | +} while (0) |
| 247 | +#else |
| 248 | +#include <linux/workqueue.h> |
| 249 | + |
| 250 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) |
| 251 | +#define IEEE80211_INIT_WORK(_t, _f) INIT_WORK((_t), (void (*)(void *))(_f), (_t)); |
| 252 | +#else |
| 253 | +#define IEEE80211_INIT_WORK(_t, _f) INIT_WORK((_t), (_f)); |
| 254 | +#endif |
| 255 | + |
| 256 | +#endif /* KERNEL_VERSION < 2.5.41 */ |
| 257 | + |
| 258 | + |
| 259 | /* Locking */ |
| 260 | /* NB: beware, spin_is_locked() is not usefully defined for !(DEBUG || SMP) |
| 261 | * because spinlocks do not exist in this configuration. Instead IRQs |
| 262 | @@ -167,6 +196,14 @@ typedef spinlock_t ieee80211com_lock_t; |
| 263 | IEEE80211_VAPS_LOCK_ASSERT(_ic); \ |
| 264 | spin_unlock_bh(&(_ic)->ic_vapslock); \ |
| 265 | } while (0) |
| 266 | +#define IEEE80211_VAPS_LOCK_IRQ(_ic) do { \ |
| 267 | + unsigned long __ilockflags; \ |
| 268 | + IEEE80211_VAPS_LOCK_CHECK(_ic); \ |
| 269 | + spin_lock_irqsave(&(_ic)->ic_vapslock, __ilockflags); |
| 270 | +#define IEEE80211_VAPS_UNLOCK_IRQ(_ic) \ |
| 271 | + IEEE80211_VAPS_LOCK_ASSERT(_ic); \ |
| 272 | + spin_unlock_irqrestore(&(_ic)->ic_vapslock, __ilockflags); \ |
| 273 | +} while (0) |
| 274 | |
| 275 | #if (defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)) && defined(spin_is_locked) |
| 276 | #define IEEE80211_VAPS_LOCK_ASSERT(_ic) \ |
| 277 | @@ -650,5 +687,5 @@ struct ifreq; |
| 278 | int ieee80211_ioctl_create_vap(struct ieee80211com *, struct ifreq *, |
| 279 | struct net_device *); |
| 280 | struct ieee80211vap *ieee80211_create_vap(struct ieee80211com *, char *, |
| 281 | - struct net_device *, int, int); |
| 282 | + struct net_device *, int, int, struct ieee80211vap *); |
| 283 | #endif /* _NET80211_IEEE80211_LINUX_H_ */ |
| 284 | --- a/net80211/ieee80211_var.h |
| 285 | +++ b/net80211/ieee80211_var.h |
| 286 | @@ -187,6 +187,12 @@ struct ieee80211vap { |
| 287 | struct ieee80211_proc_entry *iv_proc_entries; |
| 288 | struct vlan_group *iv_vlgrp; /* vlan group state */ |
| 289 | |
| 290 | + /* list of wds links */ |
| 291 | + TAILQ_HEAD(, ieee80211vap) iv_wdslinks; |
| 292 | + TAILQ_ENTRY(ieee80211vap) iv_wdsnext; |
| 293 | + struct ieee80211vap *iv_master; |
| 294 | + struct ieee80211_node *iv_wdsnode; |
| 295 | + |
| 296 | TAILQ_ENTRY(ieee80211vap) iv_next; /* list of vap instances */ |
| 297 | struct ieee80211com *iv_ic; /* back ptr to common state */ |
| 298 | u_int32_t iv_debug; /* debug msg flags */ |
| 299 | @@ -316,6 +322,7 @@ struct ieee80211com { |
| 300 | u_int8_t ic_myaddr[IEEE80211_ADDR_LEN]; |
| 301 | struct timer_list ic_inact; /* mgmt/inactivity timer */ |
| 302 | |
| 303 | + unsigned int ic_subifs; |
| 304 | u_int32_t ic_flags; /* state flags */ |
| 305 | u_int32_t ic_flags_ext; /* extension of state flags */ |
| 306 | u_int32_t ic_caps; /* capabilities */ |
| 307 | @@ -447,7 +454,7 @@ struct ieee80211com { |
| 308 | atomic_t ic_node_counter; |
| 309 | /* Virtual AP create/delete */ |
| 310 | struct ieee80211vap *(*ic_vap_create)(struct ieee80211com *, |
| 311 | - const char *, int, int, struct net_device *); |
| 312 | + const char *, int, int, struct net_device *, struct ieee80211vap *); |
| 313 | void (*ic_vap_delete)(struct ieee80211vap *); |
| 314 | |
| 315 | /* Send/recv 802.11 management frame */ |
| 316 | @@ -619,6 +626,7 @@ MALLOC_DECLARE(M_80211_VAP); |
| 317 | #define IEEE80211_FEXT_DROPUNENC_EAPOL 0x00000800 /* CONF: drop unencrypted eapol frames */ |
| 318 | #define IEEE80211_FEXT_APPIE_UPDATE 0x00001000 /* STATE: beacon APP IE updated */ |
| 319 | #define IEEE80211_FEXT_BGSCAN_THR 0x00002000 /* bgscan due to low rssi */ |
| 320 | +#define IEEE80211_FEXT_WDSSEP 0x00004000 /* move wds clients into separate interfaces */ |
| 321 | |
| 322 | #define IEEE80211_COM_UAPSD_ENABLE(_ic) ((_ic)->ic_flags_ext |= IEEE80211_FEXT_UAPSD) |
| 323 | #define IEEE80211_COM_UAPSD_DISABLE(_ic) ((_ic)->ic_flags_ext &= ~IEEE80211_FEXT_UAPSD) |
| 324 | @@ -703,7 +711,7 @@ MALLOC_DECLARE(M_80211_VAP); |
| 325 | int ieee80211_ifattach(struct ieee80211com *); |
| 326 | void ieee80211_ifdetach(struct ieee80211com *); |
| 327 | int ieee80211_vap_setup(struct ieee80211com *, struct net_device *, |
| 328 | - const char *, int, int); |
| 329 | + const char *, int, int, struct ieee80211vap *); |
| 330 | int ieee80211_vap_attach(struct ieee80211vap *, ifm_change_cb_t, ifm_stat_cb_t); |
| 331 | void ieee80211_vap_detach(struct ieee80211vap *); |
| 332 | void ieee80211_mark_dfs(struct ieee80211com *, struct ieee80211_channel *); |
| 333 | --- a/net80211/ieee80211_wireless.c |
| 334 | +++ b/net80211/ieee80211_wireless.c |
| 335 | @@ -2190,7 +2190,7 @@ ieee80211_setupxr(struct ieee80211vap *v |
| 336 | ieee80211_scan_flush(ic); /* NB: could optimize */ |
| 337 | |
| 338 | if (!(xrvap = ic->ic_vap_create(ic, name, IEEE80211_M_HOSTAP, |
| 339 | - IEEE80211_VAP_XR | IEEE80211_CLONE_BSSID, dev))) |
| 340 | + IEEE80211_VAP_XR | IEEE80211_CLONE_BSSID, dev, NULL))) |
| 341 | return; |
| 342 | |
| 343 | /* We use iv_xrvap to link to the parent VAP as well */ |
| 344 | @@ -2867,6 +2867,14 @@ ieee80211_ioctl_setparam(struct net_devi |
| 345 | else |
| 346 | vap->iv_minrateindex = 0; |
| 347 | break; |
| 348 | + case IEEE80211_PARAM_WDS_SEP: |
| 349 | + if (vap->iv_opmode != IEEE80211_M_HOSTAP) |
| 350 | + retv = -EINVAL; |
| 351 | + else if (value) |
| 352 | + vap->iv_flags_ext |= IEEE80211_FEXT_WDSSEP; |
| 353 | + else |
| 354 | + vap->iv_flags_ext &= ~IEEE80211_FEXT_WDSSEP; |
| 355 | + break; |
| 356 | #ifdef ATH_REVERSE_ENGINEERING |
| 357 | case IEEE80211_PARAM_DUMPREGS: |
| 358 | ieee80211_dump_registers(dev, info, w, extra); |
| 359 | @@ -3223,6 +3231,9 @@ ieee80211_ioctl_getparam(struct net_devi |
| 360 | case IEEE80211_PARAM_MINRATE: |
| 361 | param[0] = vap->iv_minrateindex; |
| 362 | break; |
| 363 | + case IEEE80211_PARAM_WDS_SEP: |
| 364 | + param[0] = !!(vap->iv_flags_ext & IEEE80211_FEXT_WDSSEP); |
| 365 | + break; |
| 366 | default: |
| 367 | return -EOPNOTSUPP; |
| 368 | } |
| 369 | @@ -3801,74 +3812,54 @@ ieee80211_ioctl_setmlme(struct net_devic |
| 370 | return 0; |
| 371 | } |
| 372 | |
| 373 | +#define WDSNAME ".wds%d" |
| 374 | static int |
| 375 | -ieee80211_ioctl_wdsmac(struct net_device *dev, struct iw_request_info *info, |
| 376 | +ieee80211_ioctl_wdsaddmac(struct net_device *dev, struct iw_request_info *info, |
| 377 | void *w, char *extra) |
| 378 | { |
| 379 | struct ieee80211vap *vap = dev->priv; |
| 380 | struct sockaddr *sa = (struct sockaddr *)extra; |
| 381 | + struct ieee80211com *ic = vap->iv_ic; |
| 382 | + struct ieee80211vap *avp; |
| 383 | + char *name; |
| 384 | |
| 385 | - if (!IEEE80211_ADDR_NULL(vap->wds_mac)) { |
| 386 | - printk("%s: Failed to add WDS MAC: " MAC_FMT "\n", dev->name, |
| 387 | - MAC_ADDR(sa->sa_data)); |
| 388 | - printk("%s: Device already has WDS mac address attached," |
| 389 | - " remove first\n", dev->name); |
| 390 | - return -1; |
| 391 | - } |
| 392 | - |
| 393 | - memcpy(vap->wds_mac, sa->sa_data, IEEE80211_ADDR_LEN); |
| 394 | + if (vap->iv_opmode != IEEE80211_M_HOSTAP) |
| 395 | + return -EINVAL; |
| 396 | |
| 397 | - printk("%s: Added WDS MAC: " MAC_FMT "\n", dev->name, |
| 398 | - MAC_ADDR(vap->wds_mac)); |
| 399 | + name = kmalloc(strlen(vap->iv_dev->name) + sizeof(WDSNAME) + 1, GFP_KERNEL); |
| 400 | + if (!name) |
| 401 | + return -ENOMEM; |
| 402 | |
| 403 | - if (IS_UP(vap->iv_dev)) { |
| 404 | - /* Force us back to scan state to force us to go back through RUN |
| 405 | - * state and create/pin the WDS peer node into memory. */ |
| 406 | - return ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); |
| 407 | - } |
| 408 | + strcpy(name, vap->iv_dev->name); |
| 409 | + strcat(name, WDSNAME); |
| 410 | + avp = ieee80211_create_vap(ic, name, ic->ic_dev, IEEE80211_M_WDS, 0, vap); |
| 411 | + kfree(name); |
| 412 | + if (!avp) |
| 413 | + return -ENOMEM; |
| 414 | |
| 415 | + memcpy(avp->wds_mac, sa->sa_data, IEEE80211_ADDR_LEN); |
| 416 | return 0; |
| 417 | } |
| 418 | +#undef WDSNAME |
| 419 | |
| 420 | static int |
| 421 | -ieee80211_ioctl_wdsdelmac(struct net_device *dev, struct iw_request_info *info, |
| 422 | +ieee80211_ioctl_wdssetmac(struct net_device *dev, struct iw_request_info *info, |
| 423 | void *w, char *extra) |
| 424 | { |
| 425 | struct ieee80211vap *vap = dev->priv; |
| 426 | struct sockaddr *sa = (struct sockaddr *)extra; |
| 427 | - struct ieee80211com *ic = vap->iv_ic; |
| 428 | - struct ieee80211_node *wds_ni; |
| 429 | |
| 430 | - /* WDS Mac address filed already? */ |
| 431 | - if (IEEE80211_ADDR_NULL(vap->wds_mac)) |
| 432 | - return 0; |
| 433 | + if (vap->iv_opmode != IEEE80211_M_WDS) |
| 434 | + return -EINVAL; |
| 435 | |
| 436 | - /* Compare suplied MAC address with WDS MAC of this interface |
| 437 | - * remove when mac address is known |
| 438 | - */ |
| 439 | - if (memcmp(vap->wds_mac, sa->sa_data, IEEE80211_ADDR_LEN) == 0) { |
| 440 | - if (IS_UP(vap->iv_dev)) { |
| 441 | - wds_ni = ieee80211_find_txnode(vap, vap->wds_mac); |
| 442 | - if (wds_ni != NULL) { |
| 443 | - /* Release reference created by find node */ |
| 444 | - ieee80211_unref_node(&wds_ni); |
| 445 | - /* Release reference created by transition to RUN state, |
| 446 | - * [pinning peer node into the table] */ |
| 447 | - ieee80211_unref_node(&wds_ni); |
| 448 | - } |
| 449 | - } |
| 450 | - memset(vap->wds_mac, 0x00, IEEE80211_ADDR_LEN); |
| 451 | - if (IS_UP(vap->iv_dev)) { |
| 452 | - /* This leaves a dead WDS node, until started again */ |
| 453 | - return ic->ic_reset(ic->ic_dev); |
| 454 | - } |
| 455 | - return 0; |
| 456 | + memcpy(vap->wds_mac, sa->sa_data, IEEE80211_ADDR_LEN); |
| 457 | + if (IS_UP(vap->iv_dev)) { |
| 458 | + /* Force us back to scan state to force us to go back through RUN |
| 459 | + * state and create/pin the WDS peer node into memory. */ |
| 460 | + return ieee80211_new_state(vap, IEEE80211_S_SCAN, 0); |
| 461 | } |
| 462 | |
| 463 | - printk("%s: WDS MAC address " MAC_FMT " is not known by this interface\n", |
| 464 | - dev->name, MAC_ADDR(sa->sa_data)); |
| 465 | - |
| 466 | - return -1; |
| 467 | + return 0; |
| 468 | } |
| 469 | |
| 470 | /* |
| 471 | @@ -4470,6 +4461,8 @@ get_sta_space(void *arg, struct ieee8021 |
| 472 | struct ieee80211vap *vap = ni->ni_vap; |
| 473 | size_t ielen; |
| 474 | |
| 475 | + if (req->vap->iv_wdsnode && ni->ni_subif) |
| 476 | + vap = ni->ni_subif; |
| 477 | if (vap != req->vap && vap != req->vap->iv_xrvap) /* only entries for this vap */ |
| 478 | return; |
| 479 | if ((vap->iv_opmode == IEEE80211_M_HOSTAP || |
| 480 | @@ -4489,6 +4482,8 @@ get_sta_info(void *arg, struct ieee80211 |
| 481 | size_t ielen, len; |
| 482 | u_int8_t *cp; |
| 483 | |
| 484 | + if (req->vap->iv_wdsnode && ni->ni_subif) |
| 485 | + vap = ni->ni_subif; |
| 486 | if (vap != req->vap && vap != req->vap->iv_xrvap) /* only entries for this vap (or) xrvap */ |
| 487 | return; |
| 488 | if ((vap->iv_opmode == IEEE80211_M_HOSTAP || |
| 489 | @@ -5391,8 +5386,8 @@ static const struct iw_priv_args ieee802 |
| 490 | IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "kickmac"}, |
| 491 | { IEEE80211_IOCTL_WDSADDMAC, |
| 492 | IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"wds_add" }, |
| 493 | - { IEEE80211_IOCTL_WDSDELMAC, |
| 494 | - IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"wds_del" }, |
| 495 | + { IEEE80211_IOCTL_WDSSETMAC, |
| 496 | + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,"wds_set" }, |
| 497 | { IEEE80211_IOCTL_SETCHANLIST, |
| 498 | IW_PRIV_TYPE_CHANLIST | IW_PRIV_SIZE_FIXED, 0,"setchanlist" }, |
| 499 | { IEEE80211_IOCTL_GETCHANLIST, |
| 500 | @@ -5790,6 +5785,10 @@ static const struct iw_priv_args ieee802 |
| 501 | 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_minrate"}, |
| 502 | { IEEE80211_IOCTL_SETSCANLIST, |
| 503 | IW_PRIV_TYPE_CHAR | 255, 0, "setscanlist"}, |
| 504 | + { IEEE80211_PARAM_WDS_SEP, |
| 505 | + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wdssep"}, |
| 506 | + { IEEE80211_PARAM_WDS_SEP, |
| 507 | + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "get_wdssep"}, |
| 508 | |
| 509 | #ifdef ATH_REVERSE_ENGINEERING |
| 510 | /* |
| 511 | @@ -5884,8 +5883,8 @@ static const iw_handler ieee80211_priv_h |
| 512 | #endif |
| 513 | set_priv(IEEE80211_IOCTL_ADDMAC, ieee80211_ioctl_addmac), |
| 514 | set_priv(IEEE80211_IOCTL_DELMAC, ieee80211_ioctl_delmac), |
| 515 | - set_priv(IEEE80211_IOCTL_WDSADDMAC, ieee80211_ioctl_wdsmac), |
| 516 | - set_priv(IEEE80211_IOCTL_WDSDELMAC, ieee80211_ioctl_wdsdelmac), |
| 517 | + set_priv(IEEE80211_IOCTL_WDSADDMAC, ieee80211_ioctl_wdsaddmac), |
| 518 | + set_priv(IEEE80211_IOCTL_WDSSETMAC, ieee80211_ioctl_wdssetmac), |
| 519 | set_priv(IEEE80211_IOCTL_KICKMAC, ieee80211_ioctl_kickmac), |
| 520 | set_priv(IEEE80211_IOCTL_SETSCANLIST, ieee80211_ioctl_setscanlist), |
| 521 | #ifdef ATH_REVERSE_ENGINEERING |
| 522 | @@ -5913,6 +5912,8 @@ static int |
| 523 | ieee80211_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) |
| 524 | { |
| 525 | struct ieee80211vap *vap = dev->priv; |
| 526 | + struct ieee80211com *ic = vap->iv_ic; |
| 527 | + struct ieee80211_node *ni; |
| 528 | |
| 529 | switch (cmd) { |
| 530 | case SIOCG80211STATS: |
| 531 | @@ -5921,8 +5922,20 @@ ieee80211_ioctl(struct net_device *dev, |
| 532 | case SIOC80211IFDESTROY: |
| 533 | if (!capable(CAP_NET_ADMIN)) |
| 534 | return -EPERM; |
| 535 | + /* drop all node subifs */ |
| 536 | + TAILQ_FOREACH(ni, &ic->ic_sta.nt_node, ni_list) { |
| 537 | + struct ieee80211vap *avp = ni->ni_subif; |
| 538 | + |
| 539 | + if (ni->ni_vap != vap) |
| 540 | + continue; |
| 541 | + if (!avp) |
| 542 | + continue; |
| 543 | + ni->ni_subif = NULL; |
| 544 | + ieee80211_stop(avp->iv_dev); |
| 545 | + ic->ic_vap_delete(avp); |
| 546 | + } |
| 547 | ieee80211_stop(vap->iv_dev); /* force state before cleanup */ |
| 548 | - vap->iv_ic->ic_vap_delete(vap); |
| 549 | + ic->ic_vap_delete(vap); |
| 550 | return 0; |
| 551 | case IEEE80211_IOCTL_GETKEY: |
| 552 | return ieee80211_ioctl_getkey(dev, (struct iwreq *) ifr); |
| 553 | @@ -5956,7 +5969,7 @@ ieee80211_ioctl_create_vap(struct ieee80 |
| 554 | |
| 555 | strncpy(name, cp.icp_name, sizeof(name)); |
| 556 | |
| 557 | - vap = ieee80211_create_vap(ic, name, mdev, cp.icp_opmode, cp.icp_flags); |
| 558 | + vap = ieee80211_create_vap(ic, name, mdev, cp.icp_opmode, cp.icp_flags, NULL); |
| 559 | if (vap == NULL) |
| 560 | return -EIO; |
| 561 | |
| 562 | @@ -5973,9 +5986,9 @@ EXPORT_SYMBOL(ieee80211_ioctl_create_vap |
| 563 | */ |
| 564 | struct ieee80211vap* |
| 565 | ieee80211_create_vap(struct ieee80211com *ic, char *name, |
| 566 | - struct net_device *mdev, int opmode, int opflags) |
| 567 | + struct net_device *mdev, int opmode, int opflags, struct ieee80211vap *master) |
| 568 | { |
| 569 | - return ic->ic_vap_create(ic, name, opmode, opflags, mdev); |
| 570 | + return ic->ic_vap_create(ic, name, opmode, opflags, mdev, master); |
| 571 | } |
| 572 | EXPORT_SYMBOL(ieee80211_create_vap); |
| 573 | |
| 574 | --- a/net80211/ieee80211_input.c |
| 575 | +++ b/net80211/ieee80211_input.c |
| 576 | @@ -199,8 +199,10 @@ ieee80211_input(struct ieee80211vap * va |
| 577 | { |
| 578 | #define HAS_SEQ(type) ((type & 0x4) == 0) |
| 579 | struct ieee80211_node * ni = ni_or_null; |
| 580 | - struct ieee80211com *ic = vap->iv_ic; |
| 581 | - struct net_device *dev = vap->iv_dev; |
| 582 | + struct ieee80211com *ic; |
| 583 | + struct net_device *dev; |
| 584 | + struct ieee80211_node *ni_wds = NULL; |
| 585 | + struct net_device_stats *stats; |
| 586 | struct ieee80211_frame *wh; |
| 587 | struct ieee80211_key *key; |
| 588 | struct ether_header *eh; |
| 589 | @@ -212,6 +214,19 @@ ieee80211_input(struct ieee80211vap * va |
| 590 | u_int8_t *bssid; |
| 591 | u_int16_t rxseq; |
| 592 | |
| 593 | + type = -1; /* undefined */ |
| 594 | + |
| 595 | + if (!vap) |
| 596 | + goto out; |
| 597 | + |
| 598 | + ic = vap->iv_ic; |
| 599 | + if (!ic) |
| 600 | + goto out; |
| 601 | + |
| 602 | + dev = vap->iv_dev; |
| 603 | + if (!dev) |
| 604 | + goto out; |
| 605 | + |
| 606 | /* initialize ni as in the previous API */ |
| 607 | if (ni_or_null == NULL) { |
| 608 | /* This function does not 'own' vap->iv_bss, so we cannot |
| 609 | @@ -227,7 +242,6 @@ ieee80211_input(struct ieee80211vap * va |
| 610 | |
| 611 | /* XXX adjust device in sk_buff? */ |
| 612 | |
| 613 | - type = -1; /* undefined */ |
| 614 | /* |
| 615 | * In monitor mode, send everything directly to bpf. |
| 616 | * Also do not process frames w/o i_addr2 any further. |
| 617 | @@ -434,7 +448,7 @@ ieee80211_input(struct ieee80211vap * va |
| 618 | |
| 619 | switch (type) { |
| 620 | case IEEE80211_FC0_TYPE_DATA: |
| 621 | - hdrspace = ieee80211_hdrspace(ic, wh); |
| 622 | + hdrspace = ieee80211_hdrsize(wh); |
| 623 | if (skb->len < hdrspace) { |
| 624 | IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, |
| 625 | wh, "data", "too short: len %u, expecting %u", |
| 626 | @@ -444,16 +458,24 @@ ieee80211_input(struct ieee80211vap * va |
| 627 | } |
| 628 | switch (vap->iv_opmode) { |
| 629 | case IEEE80211_M_STA: |
| 630 | - if ((dir != IEEE80211_FC1_DIR_FROMDS) && |
| 631 | - (!((vap->iv_flags_ext & IEEE80211_FEXT_WDS) && |
| 632 | - (dir == IEEE80211_FC1_DIR_DSTODS)))) { |
| 633 | + switch(dir) { |
| 634 | + case IEEE80211_FC1_DIR_FROMDS: |
| 635 | + break; |
| 636 | + case IEEE80211_FC1_DIR_DSTODS: |
| 637 | + if (vap->iv_flags_ext & IEEE80211_FEXT_WDS) |
| 638 | + break; |
| 639 | + default: |
| 640 | IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, |
| 641 | wh, "data", "invalid dir 0x%x", dir); |
| 642 | vap->iv_stats.is_rx_wrongdir++; |
| 643 | goto out; |
| 644 | } |
| 645 | |
| 646 | - if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { |
| 647 | + if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { |
| 648 | + /* ignore 3-addr mcast if we're WDS STA */ |
| 649 | + if (vap->iv_flags_ext & IEEE80211_FEXT_WDS) |
| 650 | + goto out; |
| 651 | + |
| 652 | /* Discard multicast if IFF_MULTICAST not set */ |
| 653 | if ((0 != memcmp(wh->i_addr3, dev->broadcast, ETH_ALEN)) && |
| 654 | (0 == (dev->flags & IFF_MULTICAST))) { |
| 655 | @@ -481,24 +503,10 @@ ieee80211_input(struct ieee80211vap * va |
| 656 | vap->iv_stats.is_rx_mcastecho++; |
| 657 | goto out; |
| 658 | } |
| 659 | - /* |
| 660 | - * if it is brodcasted by me on behalf of |
| 661 | - * a station behind me, drop it. |
| 662 | - */ |
| 663 | - if (vap->iv_flags_ext & IEEE80211_FEXT_WDS) { |
| 664 | - struct ieee80211_node_table *nt; |
| 665 | - struct ieee80211_node *ni_wds; |
| 666 | - nt = &ic->ic_sta; |
| 667 | - ni_wds = ieee80211_find_wds_node(nt, wh->i_addr3); |
| 668 | - if (ni_wds) { |
| 669 | - ieee80211_unref_node(&ni_wds); |
| 670 | - IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, |
| 671 | - wh, NULL, "%s", |
| 672 | - "multicast echo originated from node behind me"); |
| 673 | - vap->iv_stats.is_rx_mcastecho++; |
| 674 | - goto out; |
| 675 | - } |
| 676 | - } |
| 677 | + } else { |
| 678 | + /* Same BSSID, but not meant for us to receive */ |
| 679 | + if (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr)) |
| 680 | + goto out; |
| 681 | } |
| 682 | break; |
| 683 | case IEEE80211_M_IBSS: |
| 684 | @@ -540,16 +548,28 @@ ieee80211_input(struct ieee80211vap * va |
| 685 | vap->iv_stats.is_rx_notassoc++; |
| 686 | goto err; |
| 687 | } |
| 688 | + |
| 689 | /* |
| 690 | * If we're a 4 address packet, make sure we have an entry in |
| 691 | * the node table for the packet source address (addr4). |
| 692 | * If not, add one. |
| 693 | */ |
| 694 | + /* check for wds link first */ |
| 695 | + if ((dir == IEEE80211_FC1_DIR_DSTODS) && !ni->ni_subif) { |
| 696 | + if (vap->iv_flags_ext & IEEE80211_FEXT_WDSSEP) { |
| 697 | + ieee80211_wds_addif(ni); |
| 698 | + /* we must drop frames here until the interface has |
| 699 | + * been fully separated, otherwise a bridge might get |
| 700 | + * confused */ |
| 701 | + goto err; |
| 702 | + } |
| 703 | + } |
| 704 | + |
| 705 | /* XXX: Useless node mgmt API; make better */ |
| 706 | - if (dir == IEEE80211_FC1_DIR_DSTODS) { |
| 707 | - struct ieee80211_node_table *nt; |
| 708 | + if ((dir == IEEE80211_FC1_DIR_DSTODS) && !vap->iv_wdsnode && |
| 709 | + !ni_wds && !ni->ni_subif) { |
| 710 | + struct ieee80211_node_table *nt = &ic->ic_sta; |
| 711 | struct ieee80211_frame_addr4 *wh4; |
| 712 | - struct ieee80211_node *ni_wds; |
| 713 | |
| 714 | if (!(vap->iv_flags_ext & IEEE80211_FEXT_WDS)) { |
| 715 | IEEE80211_DISCARD(vap, IEEE80211_MSG_INPUT, |
| 716 | @@ -557,7 +577,6 @@ ieee80211_input(struct ieee80211vap * va |
| 717 | goto err; |
| 718 | } |
| 719 | wh4 = (struct ieee80211_frame_addr4 *)skb->data; |
| 720 | - nt = &ic->ic_sta; |
| 721 | ni_wds = ieee80211_find_wds_node(nt, wh4->i_addr4); |
| 722 | /* Last call increments ref count if !NULL */ |
| 723 | if ((ni_wds != NULL) && (ni_wds != ni)) { |
| 724 | @@ -608,6 +627,11 @@ ieee80211_input(struct ieee80211vap * va |
| 725 | goto out; |
| 726 | } |
| 727 | |
| 728 | + /* check if there is any data left */ |
| 729 | + hdrspace = ieee80211_hdrspace(ic, wh); |
| 730 | + if (skb->len < hdrspace) |
| 731 | + goto out; |
| 732 | + |
| 733 | /* |
| 734 | * Handle privacy requirements. Note that we |
| 735 | * must not be preempted from here until after |
| 736 | @@ -680,8 +704,12 @@ ieee80211_input(struct ieee80211vap * va |
| 737 | if (! accept_data_frame(vap, ni, key, skb, eh)) |
| 738 | goto out; |
| 739 | |
| 740 | - vap->iv_devstats.rx_packets++; |
| 741 | - vap->iv_devstats.rx_bytes += skb->len; |
| 742 | + if (ni->ni_subif && ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE))) |
| 743 | + stats = &ni->ni_subif->iv_devstats; |
| 744 | + else |
| 745 | + stats = &vap->iv_devstats; |
| 746 | + stats->rx_packets++; |
| 747 | + stats->rx_bytes += skb->len; |
| 748 | IEEE80211_NODE_STAT(ni, rx_data); |
| 749 | IEEE80211_NODE_STAT_ADD(ni, rx_bytes, skb->len); |
| 750 | ic->ic_lastdata = jiffies; |
| 751 | @@ -1114,6 +1142,18 @@ ieee80211_deliver_data(struct ieee80211_ |
| 752 | dev = vap->iv_xrvap->iv_dev; |
| 753 | #endif |
| 754 | |
| 755 | + /* if the node has a wds subif, move data frames there, |
| 756 | + * but keep EAP traffic on the master */ |
| 757 | + if (ni->ni_subif && ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE))) { |
| 758 | + if (ni->ni_vap == ni->ni_subif) { |
| 759 | + ieee80211_dev_kfree_skb(&skb); |
| 760 | + return; |
| 761 | + } else { |
| 762 | + vap = ni->ni_subif; |
| 763 | + dev = vap->iv_dev; |
| 764 | + } |
| 765 | + } |
| 766 | + |
| 767 | /* perform as a bridge within the vap */ |
| 768 | /* XXX intra-vap bridging only */ |
| 769 | if (vap->iv_opmode == IEEE80211_M_HOSTAP && |
| 770 | @@ -1139,7 +1179,16 @@ ieee80211_deliver_data(struct ieee80211_ |
| 771 | if (ni1 != NULL) { |
| 772 | if (ni1->ni_vap == vap && |
| 773 | ieee80211_node_is_authorized(ni1) && |
| 774 | + !ni1->ni_subif && |
| 775 | ni1 != vap->iv_bss) { |
| 776 | + |
| 777 | + /* tried to bridge to a subif, drop the packet */ |
| 778 | + if (ni->ni_subif) { |
| 779 | + ieee80211_unref_node(&ni1); |
| 780 | + ieee80211_dev_kfree_skb(&skb); |
| 781 | + return; |
| 782 | + } |
| 783 | + |
| 784 | skb1 = skb; |
| 785 | skb = NULL; |
| 786 | } |
| 787 | @@ -3084,8 +3133,7 @@ ieee80211_recv_mgmt(struct ieee80211vap |
| 788 | (vap->iv_opmode == IEEE80211_M_STA && ni->ni_associd) || |
| 789 | (vap->iv_opmode == IEEE80211_M_IBSS) || |
| 790 | ((subtype == IEEE80211_FC0_SUBTYPE_BEACON) && |
| 791 | - (vap->iv_opmode == IEEE80211_M_HOSTAP) && |
| 792 | - (ic->ic_protmode != IEEE80211_PROT_NONE)))) { |
| 793 | + (vap->iv_opmode == IEEE80211_M_HOSTAP)))) { |
| 794 | vap->iv_stats.is_rx_mgtdiscard++; |
| 795 | return; |
| 796 | } |
| 797 | @@ -3471,13 +3519,56 @@ ieee80211_recv_mgmt(struct ieee80211vap |
| 798 | */ |
| 799 | if (ic->ic_flags & IEEE80211_F_SCAN) { |
| 800 | ieee80211_add_scan(vap, &scan, wh, subtype, rssi, rtsf); |
| 801 | - return; |
| 802 | } |
| 803 | - if ((vap->iv_opmode == IEEE80211_M_IBSS) && |
| 804 | - (scan.capinfo & IEEE80211_CAPINFO_IBSS)) { |
| 805 | + /* NB: Behavior of WDS-Link and Ad-Hoc is very similar here: |
| 806 | + * When we receive a beacon that belongs to the AP that we're |
| 807 | + * connected to, use it to refresh the local node info. |
| 808 | + * If no node is found, go through the vap's wds link table |
| 809 | + * and try to find the sub-vap that is interested in this address |
| 810 | + */ |
| 811 | + if (((vap->iv_opmode == IEEE80211_M_IBSS) && |
| 812 | + (scan.capinfo & IEEE80211_CAPINFO_IBSS)) || |
| 813 | + (((vap->iv_opmode == IEEE80211_M_HOSTAP) || |
| 814 | + (vap->iv_opmode == IEEE80211_M_WDS)) && |
| 815 | + (scan.capinfo & IEEE80211_CAPINFO_ESS))) { |
| 816 | + struct ieee80211vap *avp = NULL; |
| 817 | + int found = 0; |
| 818 | + |
| 819 | + IEEE80211_LOCK_IRQ(vap->iv_ic); |
| 820 | + if (vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| 821 | + TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) { |
| 822 | + if (!memcmp(avp->wds_mac, wh->i_addr2, IEEE80211_ADDR_LEN)) { |
| 823 | + if (avp->iv_state != IEEE80211_S_RUN) |
| 824 | + continue; |
| 825 | + if (!avp->iv_wdsnode) |
| 826 | + continue; |
| 827 | + found = 1; |
| 828 | + break; |
| 829 | + } |
| 830 | + } |
| 831 | + if (found) |
| 832 | + ni = ni_or_null = avp->iv_wdsnode; |
| 833 | + } else if ((vap->iv_opmode == IEEE80211_M_WDS) && vap->iv_wdsnode) { |
| 834 | + found = 1; |
| 835 | + ni = ni_or_null = vap->iv_wdsnode; |
| 836 | + } |
| 837 | + IEEE80211_UNLOCK_IRQ(vap->iv_ic); |
| 838 | + |
| 839 | + if (!found) |
| 840 | + break; |
| 841 | + |
| 842 | if (ni_or_null == NULL) { |
| 843 | - /* Create a new entry in the neighbor table. */ |
| 844 | - ni = ieee80211_add_neighbor(vap, wh, &scan); |
| 845 | + if (avp) { |
| 846 | + IEEE80211_LOCK_IRQ(ic); |
| 847 | + ni = ieee80211_add_neighbor(avp, wh, &scan); |
| 848 | + /* force assoc */ |
| 849 | + ni->ni_associd |= 0xc000; |
| 850 | + avp->iv_wdsnode = ieee80211_ref_node(ni); |
| 851 | + IEEE80211_UNLOCK_IRQ(ic); |
| 852 | + } else if (vap->iv_opmode == IEEE80211_M_IBSS) { |
| 853 | + /* Create a new entry in the neighbor table. */ |
| 854 | + ni = ieee80211_add_neighbor(vap, wh, &scan); |
| 855 | + } |
| 856 | } else { |
| 857 | /* |
| 858 | * Copy data from beacon to neighbor table. |
| 859 | @@ -3490,6 +3581,7 @@ ieee80211_recv_mgmt(struct ieee80211vap |
| 860 | IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3); |
| 861 | memcpy(ni->ni_tstamp.data, scan.tstamp, |
| 862 | sizeof(ni->ni_tstamp)); |
| 863 | + ni->ni_inact = ni->ni_inact_reload; |
| 864 | ni->ni_intval = |
| 865 | IEEE80211_BINTVAL_SANITISE(scan.bintval); |
| 866 | ni->ni_capinfo = scan.capinfo; |
| 867 | --- a/net80211/ieee80211_node.c |
| 868 | +++ b/net80211/ieee80211_node.c |
| 869 | @@ -47,6 +47,7 @@ |
| 870 | #include <linux/netdevice.h> |
| 871 | #include <linux/etherdevice.h> |
| 872 | #include <linux/random.h> |
| 873 | +#include <linux/rtnetlink.h> |
| 874 | |
| 875 | #include "if_media.h" |
| 876 | |
| 877 | @@ -236,7 +237,11 @@ void |
| 878 | ieee80211_node_vdetach(struct ieee80211vap *vap) |
| 879 | { |
| 880 | struct ieee80211com *ic = vap->iv_ic; |
| 881 | + struct ieee80211_node *ni; |
| 882 | |
| 883 | + ni = vap->iv_wdsnode; |
| 884 | + if (ni) |
| 885 | + ni->ni_subif = NULL; |
| 886 | ieee80211_node_table_reset(&ic->ic_sta, vap); |
| 887 | if (vap->iv_bss != NULL) { |
| 888 | ieee80211_unref_node(&vap->iv_bss); |
| 889 | @@ -309,7 +314,7 @@ ieee80211_create_ibss(struct ieee80211va |
| 890 | /* Check to see if we already have a node for this mac |
| 891 | * NB: we gain a node reference here |
| 892 | */ |
| 893 | - ni = ieee80211_find_node(&ic->ic_sta, vap->iv_myaddr); |
| 894 | + ni = ieee80211_find_txnode(vap, vap->iv_myaddr); |
| 895 | if (ni == NULL) { |
| 896 | ni = ieee80211_alloc_node_table(vap, vap->iv_myaddr); |
| 897 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_ASSOC, |
| 898 | @@ -831,12 +836,18 @@ node_table_leave_locked(struct ieee80211 |
| 899 | LIST_REMOVE(ni, ni_hash); |
| 900 | } |
| 901 | ni->ni_table = NULL; |
| 902 | + if (ni->ni_vap->iv_wdsnode == ni) { |
| 903 | +#ifdef IEEE80211_DEBUG_REFCNT |
| 904 | + ieee80211_unref_node_debug(&ni->ni_vap->iv_wdsnode, func, line); |
| 905 | +#else |
| 906 | + ieee80211_unref_node(&ni->ni_vap->iv_wdsnode); |
| 907 | +#endif |
| 908 | + } |
| 909 | #ifdef IEEE80211_DEBUG_REFCNT |
| 910 | ieee80211_unref_node_debug(&ni, func, line); |
| 911 | #else |
| 912 | ieee80211_unref_node(&ni); |
| 913 | #endif |
| 914 | - |
| 915 | } |
| 916 | |
| 917 | /* This is overridden by ath_node_alloc in ath/if_ath.c, and so |
| 918 | @@ -1134,6 +1145,65 @@ ieee80211_alloc_node(struct ieee80211vap |
| 919 | return ni; |
| 920 | } |
| 921 | |
| 922 | +#define WDSIFNAME ".sta%d" |
| 923 | +static void |
| 924 | +ieee80211_wds_do_addif(struct work_struct *work) |
| 925 | +{ |
| 926 | + struct ieee80211_node *ni = container_of(work, struct ieee80211_node, ni_create); |
| 927 | + struct ieee80211vap *vap = ni->ni_vap; |
| 928 | + struct ieee80211com *ic = vap->iv_ic; |
| 929 | + struct ieee80211vap *avp = NULL; |
| 930 | + char *name; |
| 931 | + |
| 932 | + rtnl_lock(); |
| 933 | + /* did we get cancelled by the destroy call? */ |
| 934 | + if (!ni->ni_subif) |
| 935 | + goto done; |
| 936 | + |
| 937 | + ni->ni_subif = NULL; |
| 938 | + name = kmalloc(strlen(vap->iv_dev->name) + sizeof(WDSIFNAME) + 1, GFP_KERNEL); |
| 939 | + if (!name) |
| 940 | + goto done; |
| 941 | + |
| 942 | + strcpy(name, vap->iv_dev->name); |
| 943 | + strcat(name, WDSIFNAME); |
| 944 | + avp = ieee80211_create_vap(ic, name, ic->ic_dev, IEEE80211_M_WDS, 0, vap); |
| 945 | + kfree(name); |
| 946 | + if (!avp) |
| 947 | + goto done; |
| 948 | + |
| 949 | + memcpy(avp->wds_mac, ni->ni_bssid, IEEE80211_ADDR_LEN); |
| 950 | + avp->iv_wdsnode = ieee80211_ref_node(ni); |
| 951 | + ni->ni_subif = avp; |
| 952 | + ic->ic_subifs++; |
| 953 | + |
| 954 | +done: |
| 955 | + if (avp) { |
| 956 | + IEEE80211_VAPS_LOCK_IRQ(ic); |
| 957 | + avp->iv_newstate(vap, IEEE80211_S_RUN, -1); |
| 958 | + IEEE80211_VAPS_UNLOCK_IRQ(ic); |
| 959 | + } |
| 960 | + rtnl_unlock(); |
| 961 | + ieee80211_unref_node(&ni); |
| 962 | +} |
| 963 | +#undef WDSIFNAME |
| 964 | + |
| 965 | +void ieee80211_wds_addif(struct ieee80211_node *ni) |
| 966 | +{ |
| 967 | + /* check if the node is split out already, |
| 968 | + * or if we're in progress of setting up a new interface already */ |
| 969 | + if (ni->ni_subif) |
| 970 | + return; |
| 971 | + |
| 972 | + if (!ni->ni_table) |
| 973 | + return; |
| 974 | + |
| 975 | + ieee80211_ref_node(ni); |
| 976 | + ni->ni_subif = ni->ni_vap; |
| 977 | + IEEE80211_INIT_WORK(&ni->ni_create, ieee80211_wds_do_addif); |
| 978 | + schedule_work(&ni->ni_create); |
| 979 | +} |
| 980 | + |
| 981 | /* Add wds address to the node table */ |
| 982 | int |
| 983 | #ifdef IEEE80211_DEBUG_REFCNT |
| 984 | @@ -1553,22 +1623,39 @@ ieee80211_find_rxnode(struct ieee80211co |
| 985 | ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) == IEEE80211_FC0_SUBTYPE_PS_POLL) |
| 986 | struct ieee80211_node_table *nt; |
| 987 | struct ieee80211_node *ni; |
| 988 | + struct ieee80211vap *vap, *avp; |
| 989 | + const u_int8_t *addr; |
| 990 | + |
| 991 | + if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/) |
| 992 | + addr = wh->i_addr1; |
| 993 | + else |
| 994 | + addr = wh->i_addr2; |
| 995 | + |
| 996 | + if (IEEE80211_IS_MULTICAST(addr)) |
| 997 | + return NULL; |
| 998 | |
| 999 | /* XXX check ic_bss first in station mode */ |
| 1000 | /* XXX 4-address frames? */ |
| 1001 | nt = &ic->ic_sta; |
| 1002 | IEEE80211_NODE_TABLE_LOCK_IRQ(nt); |
| 1003 | - if (IS_CTL(wh) && !IS_PSPOLL(wh) /*&& !IS_RTS(ah)*/) |
| 1004 | -#ifdef IEEE80211_DEBUG_REFCNT |
| 1005 | - ni = ieee80211_find_node_locked_debug(nt, wh->i_addr1, func, line); |
| 1006 | -#else |
| 1007 | - ni = ieee80211_find_node_locked(nt, wh->i_addr1); |
| 1008 | -#endif |
| 1009 | - else |
| 1010 | + if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) == IEEE80211_FC1_DIR_DSTODS) { |
| 1011 | + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| 1012 | + TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) { |
| 1013 | + if (!IEEE80211_ADDR_EQ(addr, avp->wds_mac)) |
| 1014 | + continue; |
| 1015 | + |
| 1016 | + if (avp->iv_wdsnode) |
| 1017 | + return ieee80211_ref_node(avp->iv_wdsnode); |
| 1018 | + else |
| 1019 | + return NULL; |
| 1020 | + } |
| 1021 | + } |
| 1022 | + } |
| 1023 | + |
| 1024 | #ifdef IEEE80211_DEBUG_REFCNT |
| 1025 | - ni = ieee80211_find_node_locked_debug(nt, wh->i_addr2, func, line); |
| 1026 | + ni = ieee80211_find_node_locked_debug(nt, addr, func, line); |
| 1027 | #else |
| 1028 | - ni = ieee80211_find_node_locked(nt, wh->i_addr2); |
| 1029 | + ni = ieee80211_find_node_locked(nt, addr); |
| 1030 | #endif |
| 1031 | IEEE80211_NODE_TABLE_UNLOCK_IRQ(nt); |
| 1032 | |
| 1033 | @@ -1596,9 +1683,19 @@ ieee80211_find_txnode_debug(struct ieee8 |
| 1034 | ieee80211_find_txnode(struct ieee80211vap *vap, const u_int8_t *mac) |
| 1035 | #endif |
| 1036 | { |
| 1037 | + struct ieee80211com *ic = vap->iv_ic; |
| 1038 | struct ieee80211_node_table *nt; |
| 1039 | struct ieee80211_node *ni = NULL; |
| 1040 | |
| 1041 | + IEEE80211_LOCK_IRQ(ic); |
| 1042 | + if (vap->iv_opmode == IEEE80211_M_WDS) { |
| 1043 | + if (vap->iv_wdsnode && (vap->iv_state == IEEE80211_S_RUN)) |
| 1044 | + return ieee80211_ref_node(vap->iv_wdsnode); |
| 1045 | + else |
| 1046 | + return NULL; |
| 1047 | + } |
| 1048 | + IEEE80211_UNLOCK_IRQ(ic); |
| 1049 | + |
| 1050 | /* |
| 1051 | * The destination address should be in the node table |
| 1052 | * unless we are operating in station mode or this is a |
| 1053 | @@ -1669,6 +1766,11 @@ ieee80211_free_node(struct ieee80211_nod |
| 1054 | { |
| 1055 | struct ieee80211vap *vap = ni->ni_vap; |
| 1056 | |
| 1057 | + IEEE80211_LOCK_IRQ(ni->ni_ic); |
| 1058 | + if (vap && ni == vap->iv_wdsnode) |
| 1059 | + vap->iv_wdsnode = NULL; |
| 1060 | + IEEE80211_UNLOCK_IRQ(ni->ni_ic); |
| 1061 | + |
| 1062 | atomic_dec(&ni->ni_ic->ic_node_counter); |
| 1063 | node_print_message(IEEE80211_MSG_NODE|IEEE80211_MSG_NODE_REF, |
| 1064 | 1 /* show counter */, |
| 1065 | @@ -1781,22 +1883,6 @@ restart: |
| 1066 | jiffies > ni->ni_rxfragstamp + HZ) { |
| 1067 | ieee80211_dev_kfree_skb(&ni->ni_rxfrag); |
| 1068 | } |
| 1069 | - /* |
| 1070 | - * Special case ourself; we may be idle for extended periods |
| 1071 | - * of time and regardless reclaiming our state is wrong. |
| 1072 | - * Special case a WDS link: it may be dead or idle, but it is |
| 1073 | - * never ok to reclaim it, as this will block transmissions |
| 1074 | - * and nobody will recreate the node when the WDS peer is |
| 1075 | - * available again. */ |
| 1076 | - if ((ni == ni->ni_vap->iv_bss) || |
| 1077 | - (ni->ni_vap->iv_opmode == IEEE80211_M_WDS && |
| 1078 | - !memcmp(ni->ni_macaddr, ni->ni_vap->wds_mac, ETH_ALEN))) |
| 1079 | - { |
| 1080 | - /* NB: don't permit it to go negative */ |
| 1081 | - if (ni->ni_inact > 0) |
| 1082 | - ni->ni_inact--; |
| 1083 | - continue; |
| 1084 | - } |
| 1085 | ni->ni_inact--; |
| 1086 | if (ni->ni_associd != 0 || isadhoc) { |
| 1087 | struct ieee80211vap *vap = ni->ni_vap; |
| 1088 | @@ -2263,6 +2349,35 @@ ieee80211_node_leave_11g(struct ieee8021 |
| 1089 | } |
| 1090 | } |
| 1091 | |
| 1092 | +static void |
| 1093 | +ieee80211_subif_destroy(struct work_struct *work) |
| 1094 | +{ |
| 1095 | + struct ieee80211_node *ni = container_of(work, struct ieee80211_node, ni_destroy); |
| 1096 | + struct ieee80211vap *vap; |
| 1097 | + struct ieee80211com *ic; |
| 1098 | + |
| 1099 | + /* wait for full initialization before we start the teardown |
| 1100 | + * otherwise we could leak interfaces */ |
| 1101 | + while (ni->ni_subif == ni->ni_vap) |
| 1102 | + schedule(); |
| 1103 | + |
| 1104 | + rtnl_lock(); |
| 1105 | + vap = ni->ni_subif; |
| 1106 | + |
| 1107 | + if (!vap) |
| 1108 | + goto done; |
| 1109 | + |
| 1110 | + ic = vap->iv_ic; |
| 1111 | + ni->ni_subif = NULL; |
| 1112 | + ieee80211_stop(vap->iv_dev); |
| 1113 | + ic->ic_vap_delete(vap); |
| 1114 | + ic->ic_subifs--; |
| 1115 | + |
| 1116 | +done: |
| 1117 | + ieee80211_unref_node(&ni); |
| 1118 | + rtnl_unlock(); |
| 1119 | +} |
| 1120 | + |
| 1121 | /* |
| 1122 | * Handle bookkeeping for a station/neighbor leaving |
| 1123 | * the bss when operating in ap or adhoc modes. |
| 1124 | @@ -2279,6 +2394,12 @@ ieee80211_node_leave(struct ieee80211_no |
| 1125 | ni, "station with aid %d leaves (refcnt %u)", |
| 1126 | IEEE80211_NODE_AID(ni), atomic_read(&ni->ni_refcnt)); |
| 1127 | |
| 1128 | + if (ni->ni_subif) { |
| 1129 | + ieee80211_ref_node(ni); |
| 1130 | + IEEE80211_INIT_WORK(&ni->ni_destroy, ieee80211_subif_destroy); |
| 1131 | + schedule_work(&ni->ni_destroy); |
| 1132 | + } |
| 1133 | + |
| 1134 | /* From this point onwards we can no longer find the node, |
| 1135 | * so no more references are generated |
| 1136 | */ |
| 1137 | --- a/net80211/ieee80211_output.c |
| 1138 | +++ b/net80211/ieee80211_output.c |
| 1139 | @@ -246,15 +246,16 @@ ieee80211_hardstart(struct sk_buff *skb, |
| 1140 | * things like power save. |
| 1141 | */ |
| 1142 | eh = (struct ether_header *)skb->data; |
| 1143 | - if (vap->iv_opmode == IEEE80211_M_WDS) |
| 1144 | - ni = ieee80211_find_txnode(vap, vap->wds_mac); |
| 1145 | - else |
| 1146 | - ni = ieee80211_find_txnode(vap, eh->ether_dhost); |
| 1147 | + ni = ieee80211_find_txnode(vap, eh->ether_dhost); |
| 1148 | if (ni == NULL) { |
| 1149 | /* NB: ieee80211_find_txnode does stat+msg */ |
| 1150 | goto bad; |
| 1151 | } |
| 1152 | |
| 1153 | + if (ni->ni_subif && (vap != ni->ni_subif) && |
| 1154 | + ((eh)->ether_type != __constant_htons(ETHERTYPE_PAE))) |
| 1155 | + goto bad; |
| 1156 | + |
| 1157 | /* calculate priority so drivers can find the TX queue */ |
| 1158 | if (ieee80211_classify(ni, skb)) { |
| 1159 | IEEE80211_NOTE(vap, IEEE80211_MSG_OUTPUT, ni, |
| 1160 | @@ -334,20 +335,33 @@ void ieee80211_parent_queue_xmit(struct |
| 1161 | * constructing a frame as it sets i_fc[1]; other bits can |
| 1162 | * then be or'd in. |
| 1163 | */ |
| 1164 | -static void |
| 1165 | +static struct ieee80211_frame * |
| 1166 | ieee80211_send_setup(struct ieee80211vap *vap, |
| 1167 | struct ieee80211_node *ni, |
| 1168 | - struct ieee80211_frame *wh, |
| 1169 | + struct sk_buff *skb, |
| 1170 | int type, |
| 1171 | const u_int8_t sa[IEEE80211_ADDR_LEN], |
| 1172 | const u_int8_t da[IEEE80211_ADDR_LEN], |
| 1173 | const u_int8_t bssid[IEEE80211_ADDR_LEN]) |
| 1174 | { |
| 1175 | #define WH4(wh) ((struct ieee80211_frame_addr4 *)wh) |
| 1176 | + struct ieee80211_frame *wh; |
| 1177 | + int len = sizeof(struct ieee80211_frame); |
| 1178 | + int opmode = vap->iv_opmode; |
| 1179 | |
| 1180 | + if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { |
| 1181 | + if ((opmode == IEEE80211_M_STA) && |
| 1182 | + (vap->iv_flags_ext & IEEE80211_FEXT_WDS)) |
| 1183 | + opmode = IEEE80211_M_WDS; |
| 1184 | + |
| 1185 | + if (opmode == IEEE80211_M_WDS) |
| 1186 | + len = sizeof(struct ieee80211_frame_addr4); |
| 1187 | + } |
| 1188 | + |
| 1189 | + wh = (struct ieee80211_frame *)skb_push(skb, len); |
| 1190 | wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | type; |
| 1191 | if ((type & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) { |
| 1192 | - switch (vap->iv_opmode) { |
| 1193 | + switch (opmode) { |
| 1194 | case IEEE80211_M_STA: |
| 1195 | wh->i_fc[1] = IEEE80211_FC1_DIR_TODS; |
| 1196 | IEEE80211_ADDR_COPY(wh->i_addr1, bssid); |
| 1197 | @@ -389,6 +403,8 @@ ieee80211_send_setup(struct ieee80211vap |
| 1198 | *(__le16 *)&wh->i_seq[0] = |
| 1199 | htole16(ni->ni_txseqs[0] << IEEE80211_SEQ_SEQ_SHIFT); |
| 1200 | ni->ni_txseqs[0]++; |
| 1201 | + |
| 1202 | + return wh; |
| 1203 | #undef WH4 |
| 1204 | } |
| 1205 | |
| 1206 | @@ -410,9 +426,7 @@ ieee80211_mgmt_output(struct ieee80211_n |
| 1207 | |
| 1208 | SKB_CB(skb)->ni = ni; |
| 1209 | |
| 1210 | - wh = (struct ieee80211_frame *) |
| 1211 | - skb_push(skb, sizeof(struct ieee80211_frame)); |
| 1212 | - ieee80211_send_setup(vap, ni, wh, |
| 1213 | + wh = ieee80211_send_setup(vap, ni, skb, |
| 1214 | IEEE80211_FC0_TYPE_MGT | type, |
| 1215 | vap->iv_myaddr, ni->ni_macaddr, vap->iv_bssid); |
| 1216 | /* XXX power management */ |
| 1217 | @@ -458,6 +472,9 @@ ieee80211_send_nulldata(struct ieee80211 |
| 1218 | struct ieee80211_frame *wh; |
| 1219 | u_int8_t *frm; |
| 1220 | |
| 1221 | + if (ni->ni_subif) |
| 1222 | + vap = ni->ni_subif; |
| 1223 | + |
| 1224 | skb = ieee80211_getmgtframe(&frm, 0); |
| 1225 | if (skb == NULL) { |
| 1226 | /* XXX debug msg */ |
| 1227 | @@ -466,9 +483,7 @@ ieee80211_send_nulldata(struct ieee80211 |
| 1228 | return -ENOMEM; |
| 1229 | } |
| 1230 | |
| 1231 | - wh = (struct ieee80211_frame *) |
| 1232 | - skb_push(skb, sizeof(struct ieee80211_frame)); |
| 1233 | - ieee80211_send_setup(vap, ni, wh, |
| 1234 | + wh = ieee80211_send_setup(vap, ni, skb, |
| 1235 | IEEE80211_FC0_TYPE_DATA | IEEE80211_FC0_SUBTYPE_NODATA, |
| 1236 | vap->iv_myaddr, ni->ni_macaddr, vap->iv_bssid); |
| 1237 | /* NB: power management bit is never sent by an AP */ |
| 1238 | @@ -506,6 +521,7 @@ ieee80211_send_qosnulldata(struct ieee80 |
| 1239 | struct sk_buff *skb; |
| 1240 | struct ieee80211_qosframe *qwh; |
| 1241 | u_int8_t *frm; |
| 1242 | + u_int8_t *i_qos; |
| 1243 | int tid; |
| 1244 | |
| 1245 | skb = ieee80211_getmgtframe(&frm, 2); |
| 1246 | @@ -517,11 +533,12 @@ ieee80211_send_qosnulldata(struct ieee80 |
| 1247 | SKB_CB(skb)->ni = ieee80211_ref_node(ni); |
| 1248 | |
| 1249 | skb->priority = ac; |
| 1250 | - qwh = (struct ieee80211_qosframe *)skb_push(skb, sizeof(struct ieee80211_qosframe)); |
| 1251 | |
| 1252 | - qwh = (struct ieee80211_qosframe *)skb->data; |
| 1253 | + /* grab a pointer to QoS control and also compensate for the header length |
| 1254 | + * difference between QoS and non-QoS frame */ |
| 1255 | + i_qos = skb_push(skb, sizeof(struct ieee80211_qosframe) - sizeof(struct ieee80211_frame)); |
| 1256 | |
| 1257 | - ieee80211_send_setup(vap, ni, (struct ieee80211_frame *)qwh, |
| 1258 | + qwh = (struct ieee80211_qosframe *) ieee80211_send_setup(vap, ni, skb, |
| 1259 | IEEE80211_FC0_TYPE_DATA, |
| 1260 | vap->iv_myaddr, /* SA */ |
| 1261 | ni->ni_macaddr, /* DA */ |
| 1262 | @@ -535,10 +552,10 @@ ieee80211_send_qosnulldata(struct ieee80 |
| 1263 | |
| 1264 | /* map from access class/queue to 11e header priority value */ |
| 1265 | tid = WME_AC_TO_TID(ac); |
| 1266 | - qwh->i_qos[0] = tid & IEEE80211_QOS_TID; |
| 1267 | + i_qos[0] = tid & IEEE80211_QOS_TID; |
| 1268 | if (ic->ic_wme.wme_wmeChanParams.cap_wmeParams[ac].wmep_noackPolicy) |
| 1269 | qwh->i_qos[0] |= (1 << IEEE80211_QOS_ACKPOLICY_S) & IEEE80211_QOS_ACKPOLICY; |
| 1270 | - qwh->i_qos[1] = 0; |
| 1271 | + i_qos[1] = 0; |
| 1272 | |
| 1273 | IEEE80211_NODE_STAT(ni, tx_data); |
| 1274 | |
| 1275 | @@ -780,6 +797,8 @@ ieee80211_encap(struct ieee80211_node *n |
| 1276 | hdrsize = sizeof(struct ieee80211_frame); |
| 1277 | |
| 1278 | SKB_CB(skb)->auth_pkt = (eh.ether_type == __constant_htons(ETHERTYPE_PAE)); |
| 1279 | + if (ni->ni_subif) |
| 1280 | + vap = ni->ni_subif; |
| 1281 | |
| 1282 | switch (vap->iv_opmode) { |
| 1283 | case IEEE80211_M_IBSS: |
| 1284 | @@ -788,7 +807,7 @@ ieee80211_encap(struct ieee80211_node *n |
| 1285 | break; |
| 1286 | case IEEE80211_M_WDS: |
| 1287 | use4addr = 1; |
| 1288 | - ismulticast = IEEE80211_IS_MULTICAST(ni->ni_macaddr); |
| 1289 | + ismulticast = 0; |
| 1290 | break; |
| 1291 | case IEEE80211_M_HOSTAP: |
| 1292 | if (!IEEE80211_IS_MULTICAST(eh.ether_dhost) && |
| 1293 | @@ -799,20 +818,9 @@ ieee80211_encap(struct ieee80211_node *n |
| 1294 | ismulticast = IEEE80211_IS_MULTICAST(eh.ether_dhost); |
| 1295 | break; |
| 1296 | case IEEE80211_M_STA: |
| 1297 | - if ((vap->iv_flags_ext & IEEE80211_FEXT_WDS) && |
| 1298 | - !IEEE80211_ADDR_EQ(eh.ether_shost, vap->iv_myaddr)) { |
| 1299 | + if (vap->iv_flags_ext & IEEE80211_FEXT_WDS) { |
| 1300 | use4addr = 1; |
| 1301 | - ismulticast = IEEE80211_IS_MULTICAST(ni->ni_macaddr); |
| 1302 | - /* Add a WDS entry to the station VAP */ |
| 1303 | - if (IEEE80211_IS_MULTICAST(eh.ether_dhost)) { |
| 1304 | - struct ieee80211_node_table *nt = &ic->ic_sta; |
| 1305 | - struct ieee80211_node *ni_wds |
| 1306 | - = ieee80211_find_wds_node(nt, eh.ether_shost); |
| 1307 | - if (ni_wds) |
| 1308 | - ieee80211_unref_node(&ni_wds); |
| 1309 | - else |
| 1310 | - ieee80211_add_wds_addr(nt, ni, eh.ether_shost, 0); |
| 1311 | - } |
| 1312 | + ismulticast = 0; |
| 1313 | } else |
| 1314 | ismulticast = IEEE80211_IS_MULTICAST(vap->iv_bssid); |
| 1315 | break; |
| 1316 | @@ -973,7 +981,7 @@ ieee80211_encap(struct ieee80211_node *n |
| 1317 | break; |
| 1318 | case IEEE80211_M_WDS: |
| 1319 | wh->i_fc[1] = IEEE80211_FC1_DIR_DSTODS; |
| 1320 | - IEEE80211_ADDR_COPY(wh->i_addr1, ni->ni_macaddr); |
| 1321 | + IEEE80211_ADDR_COPY(wh->i_addr1, vap->wds_mac); |
| 1322 | IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); |
| 1323 | IEEE80211_ADDR_COPY(wh->i_addr3, eh.ether_dhost); |
| 1324 | IEEE80211_ADDR_COPY(WH4(wh)->i_addr4, eh.ether_shost); |
| 1325 | @@ -1683,9 +1691,7 @@ ieee80211_send_probereq(struct ieee80211 |
| 1326 | |
| 1327 | SKB_CB(skb)->ni = ieee80211_ref_node(ni); |
| 1328 | |
| 1329 | - wh = (struct ieee80211_frame *) |
| 1330 | - skb_push(skb, sizeof(struct ieee80211_frame)); |
| 1331 | - ieee80211_send_setup(vap, ni, wh, |
| 1332 | + wh = ieee80211_send_setup(vap, ni, skb, |
| 1333 | IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ, |
| 1334 | sa, da, bssid); |
| 1335 | /* XXX power management? */ |
| 1336 | --- a/tools/athkey.c |
| 1337 | +++ b/tools/athkey.c |
| 1338 | @@ -118,7 +118,7 @@ set80211priv(const char *dev, int op, vo |
| 1339 | IOCTL_ERR(IEEE80211_IOCTL_ADDMAC), |
| 1340 | IOCTL_ERR(IEEE80211_IOCTL_DELMAC), |
| 1341 | IOCTL_ERR(IEEE80211_IOCTL_WDSADDMAC), |
| 1342 | - IOCTL_ERR(IEEE80211_IOCTL_WDSDELMAC), |
| 1343 | + IOCTL_ERR(IEEE80211_IOCTL_WDSSETMAC), |
| 1344 | IOCTL_ERR(IEEE80211_IOCTL_READREG), |
| 1345 | IOCTL_ERR(IEEE80211_IOCTL_WRITEREG), |
| 1346 | }; |
| 1347 | --- a/tools/athchans.c |
| 1348 | +++ b/tools/athchans.c |
| 1349 | @@ -118,7 +118,7 @@ set80211priv(const char *dev, int op, vo |
| 1350 | IOCTL_ERR(IEEE80211_IOCTL_ADDMAC), |
| 1351 | IOCTL_ERR(IEEE80211_IOCTL_DELMAC), |
| 1352 | IOCTL_ERR(IEEE80211_IOCTL_WDSADDMAC), |
| 1353 | - IOCTL_ERR(IEEE80211_IOCTL_WDSDELMAC), |
| 1354 | + IOCTL_ERR(IEEE80211_IOCTL_WDSSETMAC), |
| 1355 | IOCTL_ERR(IEEE80211_IOCTL_READREG), |
| 1356 | IOCTL_ERR(IEEE80211_IOCTL_WRITEREG), |
| 1357 | }; |
| 1358 | --- a/tools/wlanconfig.c |
| 1359 | +++ b/tools/wlanconfig.c |
| 1360 | @@ -968,7 +968,7 @@ do80211priv(struct iwreq *iwr, const cha |
| 1361 | IOCTL_ERR(IEEE80211_IOCTL_ADDMAC), |
| 1362 | IOCTL_ERR(IEEE80211_IOCTL_DELMAC), |
| 1363 | IOCTL_ERR(IEEE80211_IOCTL_WDSADDMAC), |
| 1364 | - IOCTL_ERR(IEEE80211_IOCTL_WDSDELMAC), |
| 1365 | + IOCTL_ERR(IEEE80211_IOCTL_WDSSETMAC), |
| 1366 | IOCTL_ERR(IEEE80211_IOCTL_READREG), |
| 1367 | IOCTL_ERR(IEEE80211_IOCTL_WRITEREG), |
| 1368 | }; |
| 1369 | --- a/net80211/ieee80211_proto.c |
| 1370 | +++ b/net80211/ieee80211_proto.c |
| 1371 | @@ -979,6 +979,12 @@ ieee80211_init(struct net_device *dev, i |
| 1372 | "start running (state=%d)\n", vap->iv_state); |
| 1373 | |
| 1374 | |
| 1375 | + if (vap->iv_master && vap->iv_master->iv_state == IEEE80211_S_INIT) { |
| 1376 | + int ret = ieee80211_init(vap->iv_master->iv_dev, forcescan); |
| 1377 | + if (ret < 0) |
| 1378 | + return ret; |
| 1379 | + } |
| 1380 | + |
| 1381 | if ((dev->flags & IFF_RUNNING) == 0) { |
| 1382 | if (ic->ic_nopened++ == 0 && |
| 1383 | (parent->flags & IFF_RUNNING) == 0) |
| 1384 | @@ -1081,6 +1087,8 @@ ieee80211_init(struct net_device *dev, i |
| 1385 | int |
| 1386 | ieee80211_open(struct net_device *dev) |
| 1387 | { |
| 1388 | + struct ieee80211vap *vap = dev->priv; |
| 1389 | + |
| 1390 | return ieee80211_init(dev, 0); |
| 1391 | } |
| 1392 | |
| 1393 | @@ -1090,7 +1098,7 @@ ieee80211_open(struct net_device *dev) |
| 1394 | void |
| 1395 | ieee80211_start_running(struct ieee80211com *ic) |
| 1396 | { |
| 1397 | - struct ieee80211vap *vap; |
| 1398 | + struct ieee80211vap *vap, *avp; |
| 1399 | struct net_device *dev; |
| 1400 | |
| 1401 | /* XXX locking */ |
| 1402 | @@ -1099,6 +1107,16 @@ ieee80211_start_running(struct ieee80211 |
| 1403 | /* NB: avoid recursion */ |
| 1404 | if ((dev->flags & IFF_UP) && !(dev->flags & IFF_RUNNING)) |
| 1405 | ieee80211_open(dev); |
| 1406 | + |
| 1407 | + TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) { |
| 1408 | + if (avp->iv_wdsnode && avp->iv_wdsnode->ni_subif == avp) |
| 1409 | + continue; |
| 1410 | + |
| 1411 | + dev = avp->iv_dev; |
| 1412 | + /* NB: avoid recursion */ |
| 1413 | + if ((dev->flags & IFF_UP) && !(dev->flags & IFF_RUNNING)) |
| 1414 | + ieee80211_open(dev); |
| 1415 | + } |
| 1416 | } |
| 1417 | } |
| 1418 | EXPORT_SYMBOL(ieee80211_start_running); |
| 1419 | @@ -1116,11 +1134,43 @@ ieee80211_stop(struct net_device *dev) |
| 1420 | struct ieee80211vap *vap = dev->priv; |
| 1421 | struct ieee80211com *ic = vap->iv_ic; |
| 1422 | struct net_device *parent = ic->ic_dev; |
| 1423 | + struct ieee80211_node *tni, *ni; |
| 1424 | + struct ieee80211vap *avp; |
| 1425 | |
| 1426 | IEEE80211_DPRINTF(vap, |
| 1427 | IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, |
| 1428 | "%s\n", "stop running"); |
| 1429 | |
| 1430 | + if (vap->iv_wdsnode && !vap->iv_wdsnode->ni_subif) |
| 1431 | + ieee80211_unref_node(&vap->iv_wdsnode); |
| 1432 | + |
| 1433 | + /* stop wds interfaces */ |
| 1434 | + TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_next) { |
| 1435 | + if (avp->iv_state != IEEE80211_S_INIT) |
| 1436 | + ieee80211_stop(avp->iv_dev); |
| 1437 | + } |
| 1438 | + |
| 1439 | + /* get rid of all wds nodes while we're still locked */ |
| 1440 | + do { |
| 1441 | + ni = NULL; |
| 1442 | + |
| 1443 | + IEEE80211_NODE_TABLE_LOCK_IRQ(&ic->ic_sta); |
| 1444 | + TAILQ_FOREACH(tni, &ic->ic_sta.nt_node, ni_list) { |
| 1445 | + if (tni->ni_vap != vap) |
| 1446 | + continue; |
| 1447 | + if (!tni->ni_subif) |
| 1448 | + continue; |
| 1449 | + ni = tni; |
| 1450 | + break; |
| 1451 | + } |
| 1452 | + IEEE80211_NODE_TABLE_UNLOCK_IRQ(&ic->ic_sta); |
| 1453 | + |
| 1454 | + if (!ni) |
| 1455 | + break; |
| 1456 | + |
| 1457 | + ieee80211_node_leave(ni); |
| 1458 | + } while (1); |
| 1459 | + |
| 1460 | ieee80211_new_state(vap, IEEE80211_S_INIT, -1); |
| 1461 | if (dev->flags & IFF_RUNNING) { |
| 1462 | dev->flags &= ~IFF_RUNNING; /* mark us stopped */ |
| 1463 | @@ -1148,7 +1198,7 @@ EXPORT_SYMBOL(ieee80211_stop); |
| 1464 | void |
| 1465 | ieee80211_stop_running(struct ieee80211com *ic) |
| 1466 | { |
| 1467 | - struct ieee80211vap *vap; |
| 1468 | + struct ieee80211vap *vap, *avp; |
| 1469 | struct net_device *dev; |
| 1470 | |
| 1471 | /* XXX locking */ |
| 1472 | @@ -1156,6 +1206,12 @@ ieee80211_stop_running(struct ieee80211c |
| 1473 | dev = vap->iv_dev; |
| 1474 | if (dev->flags & IFF_RUNNING) /* NB: avoid recursion */ |
| 1475 | ieee80211_stop(dev); |
| 1476 | + |
| 1477 | + TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) { |
| 1478 | + dev = avp->iv_dev; |
| 1479 | + if (dev->flags & IFF_RUNNING) /* NB: avoid recursion */ |
| 1480 | + ieee80211_stop(dev); |
| 1481 | + } |
| 1482 | } |
| 1483 | } |
| 1484 | EXPORT_SYMBOL(ieee80211_stop_running); |
| 1485 | @@ -1342,9 +1398,9 @@ ieee80211_new_state(struct ieee80211vap |
| 1486 | struct ieee80211com *ic = vap->iv_ic; |
| 1487 | int rc; |
| 1488 | |
| 1489 | - IEEE80211_VAPS_LOCK_BH(ic); |
| 1490 | + IEEE80211_VAPS_LOCK_IRQ(ic); |
| 1491 | rc = vap->iv_newstate(vap, nstate, arg); |
| 1492 | - IEEE80211_VAPS_UNLOCK_BH(ic); |
| 1493 | + IEEE80211_VAPS_UNLOCK_IRQ(ic); |
| 1494 | return rc; |
| 1495 | } |
| 1496 | |
| 1497 | @@ -1557,57 +1613,12 @@ __ieee80211_newstate(struct ieee80211vap |
| 1498 | switch (ostate) { |
| 1499 | case IEEE80211_S_INIT: |
| 1500 | if (vap->iv_opmode == IEEE80211_M_MONITOR || |
| 1501 | - vap->iv_opmode == IEEE80211_M_WDS || |
| 1502 | vap->iv_opmode == IEEE80211_M_HOSTAP) { |
| 1503 | /* |
| 1504 | * Already have a channel; bypass the |
| 1505 | * scan and startup immediately. |
| 1506 | */ |
| 1507 | ieee80211_create_ibss(vap, ic->ic_curchan); |
| 1508 | - |
| 1509 | - /* In WDS mode, allocate and initialize peer node. */ |
| 1510 | - if (vap->iv_opmode == IEEE80211_M_WDS) { |
| 1511 | - /* XXX: This is horribly non-atomic. */ |
| 1512 | - struct ieee80211_node *wds_ni = |
| 1513 | - ieee80211_find_node(&ic->ic_sta, |
| 1514 | - vap->wds_mac); |
| 1515 | - |
| 1516 | - if (wds_ni == NULL) { |
| 1517 | - wds_ni = ieee80211_alloc_node_table( |
| 1518 | - vap, |
| 1519 | - vap->wds_mac); |
| 1520 | - if (wds_ni != NULL) { |
| 1521 | - ieee80211_add_wds_addr( |
| 1522 | - &ic->ic_sta, |
| 1523 | - wds_ni, |
| 1524 | - vap->wds_mac, |
| 1525 | - 1); |
| 1526 | - ieee80211_ref_node(wds_ni); /* pin in memory */ |
| 1527 | - } |
| 1528 | - else |
| 1529 | - IEEE80211_DPRINTF( |
| 1530 | - vap, |
| 1531 | - IEEE80211_MSG_NODE, |
| 1532 | - "%s: Unable to " |
| 1533 | - "allocate node for " |
| 1534 | - "WDS: " MAC_FMT "\n", |
| 1535 | - __func__, |
| 1536 | - MAC_ADDR( |
| 1537 | - vap->wds_mac) |
| 1538 | - ); |
| 1539 | - } |
| 1540 | - |
| 1541 | - if (wds_ni != NULL) { |
| 1542 | - ieee80211_node_authorize(wds_ni); |
| 1543 | - wds_ni->ni_chan = |
| 1544 | - vap->iv_bss->ni_chan; |
| 1545 | - wds_ni->ni_capinfo = |
| 1546 | - ni->ni_capinfo; |
| 1547 | - wds_ni->ni_associd = 1; |
| 1548 | - wds_ni->ni_ath_flags = |
| 1549 | - vap->iv_ath_cap; |
| 1550 | - } |
| 1551 | - } |
| 1552 | break; |
| 1553 | } |
| 1554 | /* fall thru... */ |
| 1555 | @@ -1675,6 +1686,7 @@ __ieee80211_newstate(struct ieee80211vap |
| 1556 | */ |
| 1557 | if (ni->ni_authmode != IEEE80211_AUTH_8021X) |
| 1558 | ieee80211_node_authorize(ni); |
| 1559 | + |
| 1560 | #ifdef ATH_SUPERG_XR |
| 1561 | /* |
| 1562 | * fire a timer to bring up XR vap if configured. |
| 1563 | @@ -1808,6 +1820,11 @@ ieee80211_newstate(struct ieee80211vap * |
| 1564 | ieee80211_state_name[dstate]); |
| 1565 | |
| 1566 | ieee80211_update_link_status(vap, nstate, ostate); |
| 1567 | + |
| 1568 | + if ((nstate != IEEE80211_S_RUN) && vap->iv_wdsnode && |
| 1569 | + !vap->iv_wdsnode->ni_subif) |
| 1570 | + ieee80211_unref_node(&vap->iv_wdsnode); |
| 1571 | + |
| 1572 | switch (nstate) { |
| 1573 | case IEEE80211_S_AUTH: |
| 1574 | case IEEE80211_S_ASSOC: |
| 1575 | @@ -1930,8 +1947,15 @@ ieee80211_newstate(struct ieee80211vap * |
| 1576 | if (ostate == IEEE80211_S_SCAN || |
| 1577 | ostate == IEEE80211_S_AUTH || |
| 1578 | ostate == IEEE80211_S_ASSOC) { |
| 1579 | + |
| 1580 | /* Transition (S_SCAN|S_AUTH|S_ASSOC) -> S_RUN */ |
| 1581 | __ieee80211_newstate(vap, nstate, arg); |
| 1582 | + |
| 1583 | + /* if we're in wds, let the ap know that we're doing this */ |
| 1584 | + if ((vap->iv_opmode == IEEE80211_M_STA) && |
| 1585 | + (vap->iv_flags_ext & IEEE80211_FEXT_WDS)) |
| 1586 | + ieee80211_send_nulldata(ieee80211_ref_node(vap->iv_bss)); |
| 1587 | + |
| 1588 | /* Then bring up all other vaps pending on the scan */ |
| 1589 | dstate = get_dominant_state(ic); |
| 1590 | if (dstate == IEEE80211_S_RUN) { |
| 1591 | --- a/ath/if_athvar.h |
| 1592 | +++ b/ath/if_athvar.h |
| 1593 | @@ -79,28 +79,6 @@ typedef void *TQUEUE_ARG; |
| 1594 | #define tasklet_enable(t) do { (void) t; local_bh_enable(); } while (0) |
| 1595 | #endif /* !DECLARE_TASKLET */ |
| 1596 | |
| 1597 | -#include <linux/sched.h> |
| 1598 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41) |
| 1599 | -#include <linux/tqueue.h> |
| 1600 | -#define work_struct tq_struct |
| 1601 | -#define schedule_work(t) schedule_task((t)) |
| 1602 | -#define flush_scheduled_work() flush_scheduled_tasks() |
| 1603 | -#define ATH_INIT_WORK(t, f) do { \ |
| 1604 | - memset((t), 0, sizeof(struct tq_struct)); \ |
| 1605 | - (t)->routine = (void (*)(void*)) (f); \ |
| 1606 | - (t)->data=(void *) (t); \ |
| 1607 | -} while (0) |
| 1608 | -#else |
| 1609 | -#include <linux/workqueue.h> |
| 1610 | - |
| 1611 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) |
| 1612 | -#define ATH_INIT_WORK(_t, _f) INIT_WORK((_t), (void (*)(void *))(_f), (_t)); |
| 1613 | -#else |
| 1614 | -#define ATH_INIT_WORK(_t, _f) INIT_WORK((_t), (_f)); |
| 1615 | -#endif |
| 1616 | - |
| 1617 | -#endif /* KERNEL_VERSION < 2.5.41 */ |
| 1618 | - |
| 1619 | /* |
| 1620 | * Guess how the interrupt handler should work. |
| 1621 | */ |
| 1622 | --- a/net80211/ieee80211_linux.c |
| 1623 | +++ b/net80211/ieee80211_linux.c |
| 1624 | @@ -145,7 +145,7 @@ ieee80211_getmgtframe(u_int8_t **frm, u_ |
| 1625 | struct sk_buff *skb; |
| 1626 | u_int len; |
| 1627 | |
| 1628 | - len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4); |
| 1629 | + len = roundup(sizeof(struct ieee80211_frame_addr4) + pktlen, 4); |
| 1630 | #ifdef IEEE80211_DEBUG_REFCNT |
| 1631 | skb = ieee80211_dev_alloc_skb_debug(len + align - 1, func, line); |
| 1632 | #else |
| 1633 | @@ -161,7 +161,7 @@ ieee80211_getmgtframe(u_int8_t **frm, u_ |
| 1634 | SKB_CB(skb)->flags = 0; |
| 1635 | SKB_CB(skb)->next = NULL; |
| 1636 | |
| 1637 | - skb_reserve(skb, sizeof(struct ieee80211_frame)); |
| 1638 | + skb_reserve(skb, sizeof(struct ieee80211_frame_addr4)); |
| 1639 | *frm = skb_put(skb, pktlen); |
| 1640 | } |
| 1641 | return skb; |
| 1642 | --- a/net80211/ieee80211_node.h |
| 1643 | +++ b/net80211/ieee80211_node.h |
| 1644 | @@ -92,11 +92,13 @@ struct ath_softc; |
| 1645 | * the ieee80211com structure. |
| 1646 | */ |
| 1647 | struct ieee80211_node { |
| 1648 | - struct ieee80211vap *ni_vap; |
| 1649 | + struct ieee80211vap *ni_vap, *ni_subif; |
| 1650 | struct ieee80211com *ni_ic; |
| 1651 | struct ieee80211_node_table *ni_table; |
| 1652 | TAILQ_ENTRY(ieee80211_node) ni_list; |
| 1653 | LIST_ENTRY(ieee80211_node) ni_hash; |
| 1654 | + struct work_struct ni_create; /* task for creating a subif */ |
| 1655 | + struct work_struct ni_destroy; /* task for destroying a subif */ |
| 1656 | atomic_t ni_refcnt; |
| 1657 | u_int ni_scangen; /* gen# for timeout scan */ |
| 1658 | u_int8_t ni_authmode; /* authentication algorithm */ |
| 1659 | @@ -430,5 +432,6 @@ void ieee80211_node_join(struct ieee8021 |
| 1660 | void ieee80211_node_leave(struct ieee80211_node *); |
| 1661 | u_int8_t ieee80211_getrssi(struct ieee80211com *); |
| 1662 | int32_t ieee80211_get_node_count(struct ieee80211com *); |
| 1663 | +void ieee80211_wds_addif(struct ieee80211_node *ni); |
| 1664 | #endif /* _NET80211_IEEE80211_NODE_H_ */ |
| 1665 | |
| 1666 | |