]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - net/mac80211/mlme.c
mac80211: fix race condition between assoc_done and first EAP packet
[mv-sheeva.git] / net / mac80211 / mlme.c
index c99237cd4b9826e97b6fb9290f72f19e552406e8..60a6f273cd3060293a3b601e76a8dfaa355e865a 100644 (file)
@@ -917,6 +917,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
                            params.aifs, params.cw_min, params.cw_max,
                            params.txop, params.uapsd);
 #endif
+               local->tx_conf[queue] = params;
                if (drv_conf_tx(local, queue, &params))
                        wiphy_debug(local->hw.wiphy,
                                    "failed to set TX queue parameters for queue %d\n",
@@ -1219,7 +1220,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
        } else {
                ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
                ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid[1], NULL, 0,
-                                        true);
+                                        (u32) -1, true);
        }
 
        ifmgd->probe_send_count++;
@@ -1304,7 +1305,8 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
 
        ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
        skb = ieee80211_build_probe_req(sdata, ifmgd->associated->bssid,
-                                       ssid + 2, ssid[1], NULL, 0, true);
+                                       (u32) -1, ssid + 2, ssid[1],
+                                       NULL, 0, true);
 
        return skb;
 }
@@ -1480,10 +1482,14 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
 
        ifmgd->aid = aid;
 
-       sta = sta_info_alloc(sdata, cbss->bssid, GFP_KERNEL);
-       if (!sta) {
-               printk(KERN_DEBUG "%s: failed to alloc STA entry for"
-                      " the AP\n", sdata->name);
+       mutex_lock(&sdata->local->sta_mtx);
+       /*
+        * station info was already allocated and inserted before
+        * the association and should be available to us
+        */
+       sta = sta_info_get_rx(sdata, cbss->bssid);
+       if (WARN_ON(!sta)) {
+               mutex_unlock(&sdata->local->sta_mtx);
                return false;
        }
 
@@ -1554,7 +1560,8 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
        if (elems.wmm_param)
                set_sta_flags(sta, WLAN_STA_WME);
 
-       err = sta_info_insert(sta);
+       /* sta_info_reinsert will also unlock the mutex lock */
+       err = sta_info_reinsert(sta);
        sta = NULL;
        if (err) {
                printk(KERN_DEBUG "%s: failed to insert STA entry for"
@@ -2333,14 +2340,16 @@ static enum work_done_result
 ieee80211_probe_auth_done(struct ieee80211_work *wk,
                          struct sk_buff *skb)
 {
+       struct ieee80211_local *local = wk->sdata->local;
+
        if (!skb) {
                cfg80211_send_auth_timeout(wk->sdata->dev, wk->filter_ta);
-               return WORK_DONE_DESTROY;
+               goto destroy;
        }
 
        if (wk->type == IEEE80211_WORK_AUTH) {
                cfg80211_send_rx_auth(wk->sdata->dev, skb->data, skb->len);
-               return WORK_DONE_DESTROY;
+               goto destroy;
        }
 
        mutex_lock(&wk->sdata->u.mgd.mtx);
@@ -2350,6 +2359,12 @@ ieee80211_probe_auth_done(struct ieee80211_work *wk,
        wk->type = IEEE80211_WORK_AUTH;
        wk->probe_auth.tries = 0;
        return WORK_DONE_REQUEUE;
+ destroy:
+       if (wk->probe_auth.synced)
+               drv_finish_tx_sync(local, wk->sdata, wk->filter_ta,
+                                  IEEE80211_TX_SYNC_AUTH);
+
+       return WORK_DONE_DESTROY;
 }
 
 int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
@@ -2419,17 +2434,46 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
        return 0;
 }
 
+/* create and insert a dummy station entry */
+static int ieee80211_pre_assoc(struct ieee80211_sub_if_data *sdata,
+                               u8 *bssid) {
+       struct sta_info *sta;
+       int err;
+
+       sta = sta_info_alloc(sdata, bssid, GFP_KERNEL);
+       if (!sta) {
+               printk(KERN_DEBUG "%s: failed to alloc STA entry for"
+                          " the AP\n", sdata->name);
+               return -ENOMEM;
+       }
+
+       sta->dummy = true;
+
+       err = sta_info_insert(sta);
+       sta = NULL;
+       if (err) {
+               printk(KERN_DEBUG "%s: failed to insert Dummy STA entry for"
+                      " the AP (error %d)\n", sdata->name, err);
+               return err;
+       }
+
+       return 0;
+}
+
 static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
                                                  struct sk_buff *skb)
 {
+       struct ieee80211_local *local = wk->sdata->local;
        struct ieee80211_mgmt *mgmt;
        struct ieee80211_rx_status *rx_status;
        struct ieee802_11_elems elems;
+       struct cfg80211_bss *cbss = wk->assoc.bss;
        u16 status;
 
        if (!skb) {
+               sta_info_destroy_addr(wk->sdata, cbss->bssid);
                cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta);
-               return WORK_DONE_DESTROY;
+               goto destroy;
        }
 
        if (wk->type == IEEE80211_WORK_ASSOC_BEACON_WAIT) {
@@ -2449,19 +2493,32 @@ static enum work_done_result ieee80211_assoc_done(struct ieee80211_work *wk,
        status = le16_to_cpu(mgmt->u.assoc_resp.status_code);
 
        if (status == WLAN_STATUS_SUCCESS) {
+               if (wk->assoc.synced)
+                       drv_finish_tx_sync(local, wk->sdata, wk->filter_ta,
+                                          IEEE80211_TX_SYNC_ASSOC);
+
                mutex_lock(&wk->sdata->u.mgd.mtx);
                if (!ieee80211_assoc_success(wk, mgmt, skb->len)) {
                        mutex_unlock(&wk->sdata->u.mgd.mtx);
                        /* oops -- internal error -- send timeout for now */
+                       sta_info_destroy_addr(wk->sdata, cbss->bssid);
                        cfg80211_send_assoc_timeout(wk->sdata->dev,
                                                    wk->filter_ta);
                        return WORK_DONE_DESTROY;
                }
 
                mutex_unlock(&wk->sdata->u.mgd.mtx);
+       } else {
+               /* assoc failed - destroy the dummy station entry */
+               sta_info_destroy_addr(wk->sdata, cbss->bssid);
        }
 
        cfg80211_send_rx_assoc(wk->sdata->dev, skb->data, skb->len);
+ destroy:
+       if (wk->assoc.synced)
+               drv_finish_tx_sync(local, wk->sdata, wk->filter_ta,
+                                  IEEE80211_TX_SYNC_ASSOC);
+
        return WORK_DONE_DESTROY;
 }
 
@@ -2472,7 +2529,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_bss *bss = (void *)req->bss->priv;
        struct ieee80211_work *wk;
        const u8 *ssid;
-       int i;
+       int i, err;
 
        mutex_lock(&ifmgd->mtx);
        if (ifmgd->associated) {
@@ -2497,6 +2554,16 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        if (!wk)
                return -ENOMEM;
 
+       /*
+        * create a dummy station info entry in order
+        * to start accepting incoming EAPOL packets from the station
+        */
+       err = ieee80211_pre_assoc(sdata, req->bss->bssid);
+       if (err) {
+               kfree(wk);
+               return err;
+       }
+
        ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
        ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;