madwifi: fix various noderef/memleak issues
authorFelix Fietkau <nbd@openwrt.org>
Sat, 17 Oct 2009 04:56:05 +0000 (04:56 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Sat, 17 Oct 2009 04:56:05 +0000 (04:56 +0000)
SVN-Revision: 18052

package/madwifi/patches/421-channel_handling.patch
package/madwifi/patches/430-use_netdev_priv.patch
package/madwifi/patches/432-netdev_ops.patch
package/madwifi/patches/446-single_module.patch
package/madwifi/patches/451-ibss_race_fix.patch

index e5302c1389d763447dee05aec4f1a6dd46eea21b..8424321d76c778ef25c46e2a9cf58dae771d4b03 100644 (file)
        }
        /* Initialize candidate channels to all available */
        memcpy(ic->ic_chan_active, ic->ic_chan_avail,
-@@ -311,11 +296,58 @@ ieee80211_ifattach(struct ieee80211com *
+@@ -311,11 +296,59 @@ ieee80211_ifattach(struct ieee80211com *
         * When 11g is supported, force the rate set to
         * include basic rates suitable for a mixed b/g bss.
         */
 +      if (init)
 +              return;
 +
++      ifmedia_removeall(&ic->ic_media);
 +      ieee80211_media_setup(ic, &ic->ic_media, ic->ic_caps, NULL, NULL);
 +      ieee80211com_media_status(ic->ic_dev, &imr);
 +      ifmedia_set(&ic->ic_media, imr.ifm_active);
        /* Setup initial channel settings */
        ic->ic_bsschan = IEEE80211_CHAN_ANYC;
        /* Arbitrarily pick the first channel */
-@@ -327,6 +359,7 @@ ieee80211_ifattach(struct ieee80211com *
+@@ -327,6 +360,7 @@ ieee80211_ifattach(struct ieee80211com *
        /* Enable WME by default, if we're capable. */
        if (ic->ic_caps & IEEE80211_C_WME)
                ic->ic_flags |= IEEE80211_F_WME;
        (void) ieee80211_setmode(ic, ic->ic_curmode);
  
        /* Store default beacon interval, as nec. */
-@@ -763,7 +796,8 @@ ieee80211_media_setup(struct ieee80211co
+@@ -763,7 +797,8 @@ ieee80211_media_setup(struct ieee80211co
        struct ieee80211_rateset allrates;
  
        /* Fill in media characteristics. */
        maxrate = 0;
        memset(&allrates, 0, sizeof(allrates));
  
-@@ -793,7 +827,7 @@ ieee80211_media_setup(struct ieee80211co
+@@ -793,7 +828,7 @@ ieee80211_media_setup(struct ieee80211co
                        ADD(media, IFM_AUTO, mopt | IFM_IEEE80211_WDS);
                if (mode == IEEE80211_MODE_AUTO)
                        continue;
  
                for (i = 0; i < rs->rs_nrates; i++) {
                        rate = rs->rs_rates[i];
-@@ -1207,7 +1241,7 @@ ieee80211_announce(struct ieee80211com *
+@@ -1207,7 +1242,7 @@ ieee80211_announce(struct ieee80211com *
                if ((ic->ic_modecaps & (1 << mode)) == 0)
                        continue;
                if_printf(dev, "%s rates: ", ieee80211_phymode_name[mode]);
                for (i = 0; i < rs->rs_nrates; i++) {
                        rate = rs->rs_rates[i];
                        mword = ieee80211_rate2media(ic, rate, mode);
-@@ -1417,7 +1451,7 @@ ieee80211com_media_change(struct net_dev
+@@ -1417,7 +1452,7 @@ ieee80211com_media_change(struct net_dev
                         * now so drivers have a consistent state.
                         */
                        KASSERT(vap->iv_bss != NULL, ("no bss node"));
                }
                error = -ENETRESET;
        }
-@@ -1435,7 +1469,7 @@ findrate(struct ieee80211com *ic, enum i
+@@ -1435,7 +1470,7 @@ findrate(struct ieee80211com *ic, enum i
  {
  #define       IEEERATE(_ic,_m,_i) \
        ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
        for (i = 0; i < nrates; i++)
                if (IEEERATE(ic, mode, i) == rate)
                        return i;
-@@ -1877,11 +1911,6 @@ ieee80211_build_countryie(struct ieee802
+@@ -1877,11 +1912,6 @@ ieee80211_build_countryie(struct ieee802
                        if (ieee80211_chan2mode(c) != curmode_noturbo)
                                continue;
  
                        if (*cur_runlen == 0) {
                                (*cur_runlen)++;
                                *cur_pow = c->ic_maxregpower;
-@@ -1915,7 +1944,7 @@ void
+@@ -1915,7 +1945,7 @@ void
  ieee80211_build_sc_ie(struct ieee80211com *ic)
  {
        struct ieee80211_ie_sc *ie = &ic->ic_sc_ie;
index ad2b6d494d0c0413e5bae48713379b335fc5a9bb..642a9d21d231c09c983f0e44bbe0106af2300763 100644 (file)
  #define skb_tail_pointer(_skb) ((_skb)->tail)
 --- a/net80211/ieee80211.c
 +++ b/net80211/ieee80211.c
-@@ -457,7 +457,7 @@ ieee80211_vap_setup(struct ieee80211com 
+@@ -458,7 +458,7 @@ ieee80211_vap_setup(struct ieee80211com 
  #define       IEEE80211_C_OPMODE \
        (IEEE80211_C_IBSS | IEEE80211_C_HOSTAP | IEEE80211_C_AHDEMO | \
         IEEE80211_C_MONITOR)
        struct net_device *parent = ic->ic_dev;
        int err;
  
-@@ -1354,7 +1354,7 @@ media_status(enum ieee80211_opmode opmod
+@@ -1355,7 +1355,7 @@ media_status(enum ieee80211_opmode opmod
  static void
  ieee80211com_media_status(struct net_device *dev, struct ifmediareq *imr)
  {
  
        imr->ifm_status = IFM_AVALID;
        if (!TAILQ_EMPTY(&ic->ic_vaps))
-@@ -1406,7 +1406,7 @@ media2mode(const struct ifmedia_entry *i
+@@ -1407,7 +1407,7 @@ media2mode(const struct ifmedia_entry *i
  static int
  ieee80211com_media_change(struct net_device *dev)
  {
        struct ieee80211vap *vap;
        struct ifmedia_entry *ime = ic->ic_media.ifm_cur;
        enum ieee80211_phymode newphymode;
-@@ -1510,7 +1510,7 @@ checkrate(struct ieee80211com *ic, enum 
+@@ -1511,7 +1511,7 @@ checkrate(struct ieee80211com *ic, enum 
  int
  ieee80211_media_change(struct net_device *dev)
  {
        struct ieee80211com *ic = vap->iv_ic;
        struct ifmedia_entry *ime = vap->iv_media.ifm_cur;
        enum ieee80211_phymode newmode;
-@@ -1544,7 +1544,7 @@ EXPORT_SYMBOL(ieee80211_media_change);
+@@ -1545,7 +1545,7 @@ EXPORT_SYMBOL(ieee80211_media_change);
  void
  ieee80211_media_status(struct net_device *dev, struct ifmediareq *imr)
  {
        struct ieee80211com *ic = vap->iv_ic;
        enum ieee80211_phymode mode;
        struct ieee80211_rateset *rs;
-@@ -1750,7 +1750,7 @@ EXPORT_SYMBOL(ieee80211_media2rate);
+@@ -1751,7 +1751,7 @@ EXPORT_SYMBOL(ieee80211_media2rate);
  static struct net_device_stats *
  ieee80211_getstats(struct net_device *dev)
  {
        struct net_device_stats *stats = &vap->iv_devstats;
  
        /* XXX: Total guess as to what to count where */
-@@ -1789,7 +1789,7 @@ ieee80211_change_mtu(struct net_device *
+@@ -1790,7 +1790,7 @@ ieee80211_change_mtu(struct net_device *
  static void
  ieee80211_set_multicast_list(struct net_device *dev)
  {
index 372000fc8e4c64e9731b0a0448e21f6fb68b2c0d..2e542bfecef858cad6cfaedc280b70e282e44226 100644 (file)
@@ -69,7 +69,7 @@ http://madwifi-project.org/changeset/4005
        case NETDEV_CHANGENAME:
 --- a/net80211/ieee80211.c
 +++ b/net80211/ieee80211.c
-@@ -450,6 +450,18 @@ ieee80211_ifdetach(struct ieee80211com *
+@@ -451,6 +451,18 @@ ieee80211_ifdetach(struct ieee80211com *
  }
  EXPORT_SYMBOL(ieee80211_ifdetach);
  
@@ -88,7 +88,7 @@ http://madwifi-project.org/changeset/4005
  int
  ieee80211_vap_setup(struct ieee80211com *ic, struct net_device *dev,
        const char *name, int opmode, int flags, struct ieee80211vap *master)
-@@ -470,16 +482,21 @@ ieee80211_vap_setup(struct ieee80211com 
+@@ -471,16 +483,21 @@ ieee80211_vap_setup(struct ieee80211com 
                } else
                        strncpy(dev->name, name, sizeof(dev->name));
        }
@@ -110,7 +110,7 @@ http://madwifi-project.org/changeset/4005
        dev->tx_queue_len = 0;                  /* NB: bypass queuing */
        dev->hard_header_len = parent->hard_header_len;
        /*
-@@ -1823,7 +1840,11 @@ ieee80211_set_multicast_list(struct net_
+@@ -1824,7 +1841,11 @@ ieee80211_set_multicast_list(struct net_
        IEEE80211_UNLOCK_IRQ(ic);
  
        /* XXX: Merge multicast list into parent device */
index ed02ae8f5eb8ed2d7d3368ec007f1e4b213af5cf..e546b054a810d21c155c4f83f194b3a6bf581ba9 100644 (file)
  #include <linux/module.h>
  #include <linux/skbuff.h>
  #include <linux/netdevice.h>
-@@ -2014,3 +2015,65 @@ ieee80211_build_sc_ie(struct ieee80211co
+@@ -2015,3 +2016,65 @@ ieee80211_build_sc_ie(struct ieee80211co
  int ath_debug_global = 0;
  EXPORT_SYMBOL(ath_debug_global);
  
index 7785dc1d787453dbda20e5117c57a73fc465a41e..9be3311fdada3a9e902014dacda2739abbd10689 100644 (file)
 --- a/net80211/ieee80211_input.c
 +++ b/net80211/ieee80211_input.c
-@@ -322,7 +322,6 @@ ieee80211_input(struct ieee80211vap * va
+@@ -294,10 +294,10 @@ ieee80211_input(struct ieee80211vap * va
+                       break;
+               case IEEE80211_M_IBSS:
+               case IEEE80211_M_AHDEMO:
+-                      if (!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bssid) ||
++                      if ((!IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bssid) ||
+                           (!IEEE80211_ADDR_EQ(wh->i_addr1, vap->iv_myaddr) &&
+-                           !IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+-                           (subtype != IEEE80211_FC0_SUBTYPE_BEACON))) {
++                           !IEEE80211_IS_MULTICAST(wh->i_addr1))) &&
++                           (type == IEEE80211_FC0_TYPE_DATA)) {
+                               if (!(vap->iv_dev->flags & IFF_PROMISC)) {
+                                       IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT,
+                                               bssid, NULL, "%s", "not to bss");
+@@ -322,22 +322,15 @@ ieee80211_input(struct ieee80211vap * va
                        }
                        /* Do not try to find a node reference if the packet really did come from the BSS */
                        if (type == IEEE80211_FC0_TYPE_DATA && ni == vap->iv_bss &&
 -                                      !IEEE80211_ADDR_EQ(vap->iv_bss->ni_macaddr, wh->i_addr2) &&
                                        IEEE80211_ADDR_EQ(vap->iv_bssid, wh->i_addr3)) {
                                /* Try to find sender in local node table. */
-                               ni = ieee80211_find_node(&ic->ic_sta, wh->i_addr2);
-@@ -3572,10 +3571,12 @@ ieee80211_recv_mgmt(struct ieee80211vap 
+-                              ni = ieee80211_find_node(&ic->ic_sta, wh->i_addr2);
++                              if (!ni_or_null) {
++                                      ieee80211_unref_node(&ni);
++                                      ni = ieee80211_find_txnode(vap, wh->i_addr2);
++                              }
+                               if (ni == NULL) {
+-                                      /*
+-                                       * Fake up a node for this newly discovered
+-                                       * member of the IBSS.  This should probably
+-                                       * done after an ACL check.
+-                                       */
+-                                      ni = ieee80211_fakeup_adhoc_node(vap,
+-                                                      wh->i_addr2);
+-                                      if (ni == NULL) {
+-                                              /* NB: stat kept for alloc failure */
+-                                              goto err;
+-                                      }
++                                      /* NB: stat kept for alloc failure */
++                                      goto discard;
+                               }
+                       }
+                       iwspy_event(vap, ni, rssi);
+@@ -3553,8 +3546,8 @@ ieee80211_recv_mgmt(struct ieee80211vap 
+                               (((vap->iv_opmode == IEEE80211_M_HOSTAP) ||
+                                (vap->iv_opmode == IEEE80211_M_WDS)) &&
+                               (scan.capinfo & IEEE80211_CAPINFO_ESS))) {
++                      struct ieee80211_node *tni = NULL;
+                       struct ieee80211vap *avp = NULL;
+-                      int do_unref = 0;
+                       int found = 0;
+                       IEEE80211_LOCK_IRQ(vap->iv_ic);
+@@ -3568,14 +3561,12 @@ ieee80211_recv_mgmt(struct ieee80211vap 
+                                       }
+                               }
+                               if (found)
+-                                      ni = ni_or_null = avp->iv_wdsnode;
++                                      tni = ieee80211_ref_node(avp->iv_wdsnode);
                        } else if (vap->iv_opmode == IEEE80211_M_WDS) {
                                found = 1;
-                               ni = ni_or_null = vap->iv_wdsnode;
+-                              ni = ni_or_null = vap->iv_wdsnode;
 -                      } else if (vap->iv_opmode == IEEE80211_M_IBSS) {
-+                      } else if ((vap->iv_opmode == IEEE80211_M_IBSS) && (vap->iv_state == IEEE80211_S_RUN)) {
-                               ni_or_null = ieee80211_find_node(&ic->ic_sta, wh->i_addr2);
+-                              ni_or_null = ieee80211_find_node(&ic->ic_sta, wh->i_addr2);
 -                              if (ni_or_null)
-+                              if (ni_or_null) {
-                                       ni = ni_or_null;
-+                                      do_unref = 1;
-+                              }
+-                                      ni = ni_or_null;
++                              tni = ieee80211_ref_node(vap->iv_wdsnode);
++                      } else if ((vap->iv_opmode == IEEE80211_M_IBSS) && (vap->iv_state == IEEE80211_S_RUN)) {
++                              tni = ieee80211_find_node(&ic->ic_sta, wh->i_addr2);
                                found = 1;
                        }
                        IEEE80211_UNLOCK_IRQ(vap->iv_ic);
+@@ -3583,20 +3574,21 @@ ieee80211_recv_mgmt(struct ieee80211vap 
+                       if (!found)
+                               break;
+-                      if (ni_or_null == NULL) {
++                      memcpy(&SKB_CB(skb)->beacon_tsf, scan.tstamp, sizeof(u_int64_t));
++
++                      if (tni == NULL) {
+                               if (avp) {
+                                       IEEE80211_LOCK_IRQ(ic);
+-                                      ni = ieee80211_add_neighbor(avp, wh, &scan);
++                                      tni = ieee80211_add_neighbor(avp, wh, &scan);
+                                       /* force assoc */
+-                                      ni->ni_associd |= 0xc000;
+-                                      avp->iv_wdsnode = ieee80211_ref_node(ni);
++                                      tni->ni_associd |= 0xc000;
++                                      avp->iv_wdsnode = ieee80211_ref_node(tni);
+                                       IEEE80211_UNLOCK_IRQ(ic);
+                               } else if ((vap->iv_opmode == IEEE80211_M_IBSS) &&
+                                          IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bssid)) {
+                                       /* Create a new entry in the neighbor table. */
+-                                      ni = ieee80211_add_neighbor(vap, wh, &scan);
++                                      tni = ieee80211_add_neighbor(vap, wh, &scan);
+                               }
+-                              do_unref = 1;
+                       } else {
+                               /*
+                                * Copy data from beacon to neighbor table.
+@@ -3604,39 +3596,38 @@ ieee80211_recv_mgmt(struct ieee80211vap 
+                                * ieee80211_add_neighbor(), so we just copy
+                                * everything over to be safe.
+                                */
+-                              ni->ni_esslen = scan.ssid[1];
+-                              memcpy(ni->ni_essid, scan.ssid + 2, scan.ssid[1]);
+-                              IEEE80211_ADDR_COPY(ni->ni_bssid, wh->i_addr3);
+-                              memcpy(ni->ni_tstamp.data, scan.tstamp,
+-                                      sizeof(ni->ni_tstamp));
+-                              ni->ni_inact = ni->ni_inact_reload;
+-                              ni->ni_intval = 
++                              tni->ni_esslen = scan.ssid[1];
++                              memcpy(tni->ni_essid, scan.ssid + 2, scan.ssid[1]);
++                              IEEE80211_ADDR_COPY(tni->ni_bssid, wh->i_addr3);
++                              memcpy(tni->ni_tstamp.data, scan.tstamp,
++                                      sizeof(tni->ni_tstamp));
++                              tni->ni_inact = tni->ni_inact_reload;
++                              tni->ni_intval =
+                                       IEEE80211_BINTVAL_SANITISE(scan.bintval);
+-                              ni->ni_capinfo = scan.capinfo;
+-                              ni->ni_chan = ic->ic_curchan;
+-                              ni->ni_fhdwell = scan.fhdwell;
+-                              ni->ni_fhindex = scan.fhindex;
+-                              ni->ni_erp = scan.erp;
+-                              ni->ni_timoff = scan.timoff;
++                              tni->ni_capinfo = scan.capinfo;
++                              tni->ni_chan = ic->ic_curchan;
++                              tni->ni_fhdwell = scan.fhdwell;
++                              tni->ni_fhindex = scan.fhindex;
++                              tni->ni_erp = scan.erp;
++                              tni->ni_timoff = scan.timoff;
+                               if (scan.wme != NULL)
+-                                      ieee80211_saveie(&ni->ni_wme_ie, scan.wme);
++                                      ieee80211_saveie(&tni->ni_wme_ie, scan.wme);
+                               if (scan.wpa != NULL)
+-                                      ieee80211_saveie(&ni->ni_wpa_ie, scan.wpa);
++                                      ieee80211_saveie(&tni->ni_wpa_ie, scan.wpa);
+                               if (scan.rsn != NULL)
+-                                      ieee80211_saveie(&ni->ni_rsn_ie, scan.rsn);
++                                      ieee80211_saveie(&tni->ni_rsn_ie, scan.rsn);
+                               if (scan.ath != NULL)
+-                                      ieee80211_saveath(ni, scan.ath);
++                                      ieee80211_saveath(tni, scan.ath);
+                               /* NB: must be after ni_chan is setup */
+-                              ieee80211_setup_rates(ni, scan.rates,
++                              ieee80211_setup_rates(tni, scan.rates,
+                                       scan.xrates, IEEE80211_F_DOSORT);
+                       }
+-                      if (ni != NULL) {
+-                              ni->ni_rssi = rssi;
+-                              ni->ni_rtsf = rtsf;
+-                              ni->ni_last_rx = jiffies;
+-                              if (do_unref)
+-                                      ieee80211_unref_node(&ni);
++                      if (tni != NULL) {
++                              tni->ni_rssi = rssi;
++                              tni->ni_rtsf = rtsf;
++                              tni->ni_last_rx = jiffies;
++                              ieee80211_unref_node(&tni);
+                       }
+               }
+               break;
 --- a/net80211/ieee80211_node.c
 +++ b/net80211/ieee80211_node.c
-@@ -317,16 +317,16 @@ ieee80211_create_ibss(struct ieee80211va
+@@ -53,6 +53,7 @@
+ #include <net80211/ieee80211_var.h>
+ #include <net80211/if_athproto.h>
++#include <net80211/ieee80211_node.h>
+ /*
+  * Association IDs are managed with a bit vector.
+@@ -317,16 +318,11 @@ ieee80211_create_ibss(struct ieee80211va
        /* Check to see if we already have a node for this mac
         * NB: we gain a node reference here
         */
 -      ni = ieee80211_find_txnode(vap, vap->iv_myaddr);
-+      ni = ieee80211_find_node(&ic->ic_sta, vap->iv_myaddr);
-+      if (ni) {
-+              ieee80211_node_leave(ni);
-+              ieee80211_unref_node(&ni);
-+      }
-+
++      ieee80211_node_table_reset(&ic->ic_sta, vap);
 +      ni = ieee80211_alloc_node_table(vap, vap->iv_myaddr);
        if (ni == NULL) {
 -              ni = ieee80211_alloc_node_table(vap, vap->iv_myaddr);
        }
  
        IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_myaddr);
-@@ -759,6 +759,9 @@ ieee80211_sta_join(struct ieee80211vap *
-       ieee80211_setup_rates(ni, se->se_rates, se->se_xrates,
-               IEEE80211_F_DOSORT | IEEE80211_F_DONEGO | IEEE80211_F_DODEL);
+@@ -429,8 +425,8 @@ ieee80211_reset_bss(struct ieee80211vap 
+                         __func__, ni, MAC_ADDR(vap->iv_myaddr));
+       KASSERT(ni != NULL, ("unable to setup inital BSS node"));
  
-+      if (vap->iv_opmode == IEEE80211_M_IBSS)
-+              ieee80211_node_table_reset(&vap->iv_ic->ic_sta, vap);
-+
-       return ieee80211_sta_join1(PASS_NODE(ni));
- }
- EXPORT_SYMBOL(ieee80211_sta_join);
+-      vap->iv_bss = ieee80211_ref_node(ni);
+-      KASSERT((atomic_read(&vap->iv_bss->ni_refcnt) == 3), 
++      vap->iv_bss = ni;
++      KASSERT((atomic_read(&vap->iv_bss->ni_refcnt) == 2),
+               ("wrong refcount for new node."));
+       if (obss != NULL) {
+@@ -647,7 +643,7 @@ ieee80211_sta_join1(struct ieee80211_nod
+               (vap->iv_state == IEEE80211_S_RUN) && bssid_equal(obss, selbs)); */
+       vap->iv_bss = selbs;
+       IEEE80211_ADDR_COPY(vap->iv_bssid, selbs->ni_bssid);
+-      if (obss != NULL) {
++      if ((obss != NULL) && (obss != selbs)) {
+               if (obss->ni_table)
+                       ieee80211_node_leave(obss);
+               ieee80211_unref_node(&obss);
 --- a/ath/if_ath.c
 +++ b/ath/if_ath.c
-@@ -6655,10 +6655,8 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+@@ -6625,14 +6625,6 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+       sc->sc_recv_mgmt(vap, ni_or_null, skb, subtype, rssi, rtsf);
+-      /* Lookup the new node if any (this grabs a reference to it) */
+-      ni = ieee80211_find_rxnode(vap->iv_ic, vap,
+-               (const struct ieee80211_frame_min *)skb->data);
+-      if (ni == NULL) {
+-              DPRINTF(sc, ATH_DEBUG_BEACON, "Dropping; node unknown.\n");
+-              return;
+-      }
+-
+       switch (subtype) {
+       case IEEE80211_FC0_SUBTYPE_BEACON:
+               /* update RSSI statistics for use by the HAL */
+@@ -6654,11 +6646,9 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+                        * we do the IBSS merging in software. Also do not merge
                         * if the difference it too small. Otherwise we are playing
                         * tsf-pingpong with other vendors drivers */
-                       beacon_tsf = le64_to_cpu(ni->ni_tstamp.tsf);
+-                      beacon_tsf = le64_to_cpu(ni->ni_tstamp.tsf);
 -                      if (beacon_tsf > rtsf + 0xffff) {
++                      beacon_tsf = le64_to_cpu(SKB_CB(skb)->beacon_tsf);
 +                      if (beacon_tsf > rtsf + 0xffff)
                                ath_hal_settsf64(sc->sc_ah, beacon_tsf - rtsf);
 -                              ieee80211_ibss_merge(ni);
                        break;
                }
                /* NB: Fall Through */
+@@ -6680,13 +6670,21 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+                       hw_tsf = ath_hal_gettsf64(sc->sc_ah);
+                       hw_tu  = hw_tsf >> 10;
+-                      beacon_tsf = le64_to_cpu(ni->ni_tstamp.tsf);
++                      beacon_tsf = le64_to_cpu(SKB_CB(skb)->beacon_tsf);
+                       beacon_tu  = beacon_tsf >> 10;
++                      if (!beacon_tsf)
++                              break;
++
++                      if (IEEE80211_ADDR_EQ(wh->i_addr3, vap->iv_bssid))
++                              break;
++
+                       DPRINTF(sc, ATH_DEBUG_BEACON,
+-                                      "Beacon transmitted at %10llx, "
++                                      "Beacon transmitted from "MAC_FMT" ("MAC_FMT") at %10llx, "
+                                       "received at %10llx(%lld), hw TSF "
+                                       "%10llx(%lld)\n",
++                                      MAC_ADDR(wh->i_addr3),
++                                      MAC_ADDR(vap->iv_bssid),
+                                       beacon_tsf,
+                                       rtsf, rtsf - beacon_tsf,
+                                       hw_tsf, hw_tsf - beacon_tsf);
+@@ -6699,39 +6697,13 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+                               do_merge = 1;
+                       }
+-                      /* Check sc_nexttbtt */
+-                      if (sc->sc_nexttbtt < hw_tu) {
+-                              DPRINTF(sc, ATH_DEBUG_BEACON,
+-                                      "sc_nexttbtt (%8x TU) is in the past "
+-                                      "(tsf %8x TU), updating timers\n",
+-                                      sc->sc_nexttbtt, hw_tu);
+-                              do_merge = 1;
+-                      }
+-
+-                      intval = ni->ni_intval & HAL_BEACON_PERIOD;
+-#if 0
+-                      /* This code is disabled since it would produce
+-                       * unwanted merge. For instance, in a two nodes network
+-                       * A & B, A can merge to B and at the same time, B will
+-                       * merge to A, still having a split */
+-                      if (intval != 0) {
+-                              if ((sc->sc_nexttbtt % intval) !=
+-                                              (beacon_tu % intval)) {
+-                                      DPRINTF(sc, ATH_DEBUG_BEACON,
+-                                                      "ibss merge: "
+-                                                      "sc_nexttbtt %10x TU "
+-                                                      "(%3d) beacon %10x TU "
+-                                                      "(%3d)\n",
+-                                                      sc->sc_nexttbtt,
+-                                                      sc->sc_nexttbtt % intval,
+-                                                      beacon_tu,
+-                                                      beacon_tu % intval);
+-                                      do_merge = 1;
+-                              }
+-                      }
+-#endif
+-                      if (do_merge)
++                      if (do_merge) {
++                              /* Lookup the new node if any (this grabs a reference to it) */
++                              ni = ieee80211_find_txnode(vap, wh->i_addr2);
++                              memcpy(ni->ni_bssid, wh->i_addr3, IEEE80211_ADDR_LEN);
+                               ieee80211_ibss_merge(ni);
++                              ieee80211_unref_node(&ni);
++                      }
+                       if ((sc->sc_opmode == HAL_M_IBSS) &&
+                                       ath_hw_check_atim(sc, 1, vap->iv_bss->ni_intval))
+@@ -6739,8 +6711,6 @@ ath_recv_mgmt(struct ieee80211vap * vap,
+               }
+               break;
+       }
+-
+-      ieee80211_unref_node(&ni);
+ }
+ static void
+--- a/net80211/ieee80211_linux.h
++++ b/net80211/ieee80211_linux.h
+@@ -411,7 +411,7 @@ typedef spinlock_t acl_lock_t;
+  *     8 bytes so we reserve/avoid it.
+  */
+       struct ieee80211_cb {
+-      u_int8_t vlan[8];                       /* reserve for vlan tag info */
++      u_int64_t beacon_tsf;
+       struct ieee80211_node *ni;
+       u_int32_t flags;
+ #define       M_LINK0         0x01                    /* frame needs WEP encryption */
+--- a/net80211/ieee80211_scan_sta.c
++++ b/net80211/ieee80211_scan_sta.c
+@@ -1125,11 +1125,8 @@ adhoc_default_action(struct ieee80211vap
+       u_int8_t zeroMacAddr[IEEE80211_ADDR_LEN];
+       memset(&zeroMacAddr, 0, IEEE80211_ADDR_LEN);
+-      if (IEEE80211_ADDR_EQ(se->se_bssid, &zeroMacAddr[0])) {
+-              ieee80211_create_ibss(vap, se->se_chan);
+-              return 1;
+-      } else
+-              return ieee80211_sta_join(vap, se);
++      ieee80211_create_ibss(vap, se->se_chan);
++      return 1;
+ }
+ static const struct ieee80211_scanner adhoc_default = {