mac80211: let cfg80211 manage auth state
[safe/jmp/linux-2.6] / net / mac80211 / mlme.c
index 7f99093..f060bc6 100644 (file)
@@ -949,11 +949,10 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
 }
 
 static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
-                                    struct ieee80211_mgd_work *wk,
+                                    struct ieee80211_bss *bss,
                                     u32 bss_info_changed)
 {
        struct ieee80211_local *local = sdata->local;
-       struct ieee80211_bss *bss = wk->bss;
 
        bss_info_changed |= BSS_CHANGED_ASSOC;
        /* set timing information */
@@ -966,7 +965,6 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
                bss->cbss.capability, bss->has_erp_value, bss->erp_value);
 
        sdata->u.mgd.associated = bss;
-       sdata->u.mgd.old_associate_work = wk;
        memcpy(sdata->u.mgd.bssid, bss->cbss.bssid, ETH_ALEN);
 
        /* just to be sure */
@@ -1090,8 +1088,7 @@ ieee80211_authenticate(struct ieee80211_sub_if_data *sdata,
        return RX_MGMT_NONE;
 }
 
-static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
-                                  bool deauth)
+static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_local *local = sdata->local;
@@ -1109,16 +1106,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
        ifmgd->associated = NULL;
        memset(ifmgd->bssid, 0, ETH_ALEN);
 
-       if (deauth) {
-               kfree(ifmgd->old_associate_work);
-               ifmgd->old_associate_work = NULL;
-       } else {
-               struct ieee80211_mgd_work *wk = ifmgd->old_associate_work;
-
-               wk->state = IEEE80211_MGD_STATE_IDLE;
-               list_add(&wk->list, &ifmgd->work_list);
-       }
-
        /*
         * we need to commit the associated = NULL change because the
         * scan code uses that to determine whether this iface should
@@ -1333,7 +1320,8 @@ EXPORT_SYMBOL(ieee80211_beacon_loss);
 static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata,
                                     struct ieee80211_mgd_work *wk)
 {
-       wk->state = IEEE80211_MGD_STATE_IDLE;
+       list_del(&wk->list);
+       kfree(wk);
        printk(KERN_DEBUG "%s: authenticated\n", sdata->name);
 }
 
@@ -1411,7 +1399,6 @@ ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
 
 static enum rx_mgmt_action __must_check
 ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
-                        struct ieee80211_mgd_work *wk,
                         struct ieee80211_mgmt *mgmt, size_t len)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -1423,23 +1410,15 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
 
        ASSERT_MGD_MTX(ifmgd);
 
-       if (wk)
-               bssid = wk->bss->cbss.bssid;
-       else
-               bssid = ifmgd->associated->cbss.bssid;
+       bssid = ifmgd->associated->cbss.bssid;
 
        reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
 
        printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n",
                        sdata->name, bssid, reason_code);
 
-       if (!wk) {
-               ieee80211_set_disassoc(sdata, true);
-               ieee80211_recalc_idle(sdata->local);
-       } else {
-               list_del(&wk->list);
-               kfree(wk);
-       }
+       ieee80211_set_disassoc(sdata);
+       ieee80211_recalc_idle(sdata->local);
 
        return RX_MGMT_CFG80211_DEAUTH;
 }
@@ -1468,7 +1447,7 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
        printk(KERN_DEBUG "%s: disassociated from %pM (Reason: %u)\n",
                        sdata->name, mgmt->sa, reason_code);
 
-       ieee80211_set_disassoc(sdata, false);
+       ieee80211_set_disassoc(sdata);
        ieee80211_recalc_idle(sdata->local);
        return RX_MGMT_CFG80211_DISASSOC;
 }
@@ -1484,6 +1463,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_supported_band *sband;
        struct sta_info *sta;
+       struct ieee80211_bss *bss = wk->bss;
        u32 rates, basic_rates;
        u16 capab_info, status_code, aid;
        struct ieee802_11_elems elems;
@@ -1502,7 +1482,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
        if (len < 24 + 6)
                return RX_MGMT_NONE;
 
-       if (memcmp(wk->bss->cbss.bssid, mgmt->sa, ETH_ALEN) != 0)
+       if (memcmp(bss->cbss.bssid, mgmt->sa, ETH_ALEN) != 0)
                return RX_MGMT_NONE;
 
        capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
@@ -1532,10 +1512,17 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
                return RX_MGMT_NONE;
        }
 
+       /*
+        * Here the association was either successful or not.
+        */
+
+       /* delete work item -- must be before set_associated for PS */
+       list_del(&wk->list);
+       kfree(wk);
+
        if (status_code != WLAN_STATUS_SUCCESS) {
                printk(KERN_DEBUG "%s: AP denied association (code=%d)\n",
                       sdata->name, status_code);
-               wk->state = IEEE80211_MGD_STATE_IDLE;
                return RX_MGMT_CFG80211_ASSOC;
        }
 
@@ -1553,7 +1540,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
        printk(KERN_DEBUG "%s: associated\n", sdata->name);
        ifmgd->aid = aid;
 
-       sta = sta_info_alloc(sdata, wk->bss->cbss.bssid, GFP_KERNEL);
+       sta = sta_info_alloc(sdata, bss->cbss.bssid, GFP_KERNEL);
        if (!sta) {
                printk(KERN_DEBUG "%s: failed to alloc STA entry for"
                       " the AP\n", sdata->name);
@@ -1645,18 +1632,14 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
            (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) &&
            !(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
                changed |= ieee80211_enable_ht(sdata, elems.ht_info_elem,
-                                              wk->bss->cbss.bssid,
+                                              bss->cbss.bssid,
                                               ap_ht_cap_flags);
 
-        /* delete work item -- must be before set_associated for PS */
-       list_del(&wk->list);
-
        /* set AID and assoc capability,
         * ieee80211_set_associated() will tell the driver */
        bss_conf->aid = aid;
        bss_conf->assoc_capability = capab_info;
-       /* this will take ownership of wk */
-       ieee80211_set_associated(sdata, wk, changed);
+       ieee80211_set_associated(sdata, bss, changed);
 
        /*
         * Start timer to probe the connection to the AP now.
@@ -1999,8 +1982,7 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                                                     skb->len, rx_status);
                        break;
                case IEEE80211_STYPE_DEAUTH:
-                       rma = ieee80211_rx_mgmt_deauth(sdata, NULL,
-                                                      mgmt, skb->len);
+                       rma = ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
                        break;
                case IEEE80211_STYPE_DISASSOC:
                        rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
@@ -2051,8 +2033,15 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
                                                           skb->len, true);
                        break;
                case IEEE80211_STYPE_DEAUTH:
-                       rma = ieee80211_rx_mgmt_deauth(sdata, wk, mgmt,
-                                                      skb->len);
+                       if (skb->len >= 24 + 2 /* mgmt + deauth reason */) {
+                               /*
+                                * We get here if we get deauth while
+                                * trying to auth/assoc. Telling cfg80211
+                                * is handled below, unconditionally.
+                                */
+                               list_del(&wk->list);
+                               kfree(wk);
+                       }
                        break;
                }
                /*
@@ -2066,6 +2055,12 @@ static void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
 
        mutex_unlock(&ifmgd->mtx);
 
+       if (skb->len >= 24 + 2 /* mgmt + deauth reason */ &&
+           (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_DEAUTH) {
+               WARN_ON(rma != RX_MGMT_NONE);
+               rma = RX_MGMT_CFG80211_DEAUTH;
+       }
+
        switch (rma) {
        case RX_MGMT_NONE:
                /* no action */
@@ -2116,7 +2111,6 @@ static void ieee80211_sta_work(struct work_struct *work)
        struct ieee80211_mgd_work *wk, *tmp;
        LIST_HEAD(free_work);
        enum rx_mgmt_action rma;
-       bool anybusy = false;
 
        if (!ieee80211_sdata_running(sdata))
                return;
@@ -2171,7 +2165,7 @@ static void ieee80211_sta_work(struct work_struct *work)
                        printk(KERN_DEBUG "No probe response from AP %pM"
                                " after %dms, disconnecting.\n",
                                bssid, (1000 * IEEE80211_PROBE_WAIT)/HZ);
-                       ieee80211_set_disassoc(sdata, true);
+                       ieee80211_set_disassoc(sdata);
                        ieee80211_recalc_idle(local);
                        mutex_unlock(&ifmgd->mtx);
                        /*
@@ -2203,8 +2197,6 @@ static void ieee80211_sta_work(struct work_struct *work)
                switch (wk->state) {
                default:
                        WARN_ON(1);
-                       /* fall through */
-               case IEEE80211_MGD_STATE_IDLE:
                        /* nothing */
                        rma = RX_MGMT_NONE;
                        break;
@@ -2227,20 +2219,19 @@ static void ieee80211_sta_work(struct work_struct *work)
                case RX_MGMT_CFG80211_ASSOC_TO:
                        list_del(&wk->list);
                        list_add(&wk->list, &free_work);
-                       wk->tries = rma; /* small abuse but only local */
+                       /*
+                        * small abuse but only local -- keep the
+                        * action type in wk->timeout while the item
+                        * is on the cleanup list
+                        */
+                       wk->timeout = rma;
                        break;
                default:
                        WARN(1, "unexpected: %d", rma);
                }
        }
 
-       list_for_each_entry(wk, &ifmgd->work_list, list) {
-               if (wk->state != IEEE80211_MGD_STATE_IDLE) {
-                       anybusy = true;
-                       break;
-               }
-       }
-       if (!anybusy &&
+       if (list_empty(&ifmgd->work_list) &&
            test_and_clear_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request))
                ieee80211_queue_delayed_work(&local->hw,
                                             &local->scan_work,
@@ -2249,7 +2240,8 @@ static void ieee80211_sta_work(struct work_struct *work)
        mutex_unlock(&ifmgd->mtx);
 
        list_for_each_entry_safe(wk, tmp, &free_work, list) {
-               switch (wk->tries) {
+               /* see above how we're using wk->timeout */
+               switch (wk->timeout) {
                case RX_MGMT_CFG80211_AUTH_TO:
                        cfg80211_send_auth_timeout(sdata->dev,
                                                   wk->bss->cbss.bssid);
@@ -2259,7 +2251,7 @@ static void ieee80211_sta_work(struct work_struct *work)
                                                    wk->bss->cbss.bssid);
                        break;
                default:
-                       WARN(1, "unexpected: %d", wk->tries);
+                       WARN(1, "unexpected: %lu", wk->timeout);
                }
 
                list_del(&wk->list);
@@ -2487,35 +2479,18 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                        struct cfg80211_assoc_request *req)
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-       struct ieee80211_mgd_work *wk, *found = NULL;
+       struct ieee80211_mgd_work *wk;
+       const u8 *ssid;
        int i, err;
 
        mutex_lock(&ifmgd->mtx);
 
-       list_for_each_entry(wk, &ifmgd->work_list, list) {
-               if (&wk->bss->cbss == req->bss &&
-                   wk->state == IEEE80211_MGD_STATE_IDLE) {
-                       found = wk;
-                       break;
-               }
-       }
-
-       if (!found) {
-               err = -ENOLINK;
-               goto out;
-       }
-
-       list_del(&found->list);
-
-       wk = krealloc(found, sizeof(*wk) + req->ie_len, GFP_KERNEL);
+       wk = kzalloc(sizeof(*wk) + req->ie_len, GFP_KERNEL);
        if (!wk) {
-               list_add(&found->list, &ifmgd->work_list);
                err = -ENOMEM;
                goto out;
        }
 
-       list_add(&wk->list, &ifmgd->work_list);
-
        ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
 
        for (i = 0; i < req->crypto.n_ciphers_pairwise; i++)
@@ -2524,8 +2499,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
                    req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104)
                        ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
 
-       sdata->local->oper_channel = req->bss->channel;
-       ieee80211_hw_config(sdata->local, 0);
 
        if (req->ie && req->ie_len) {
                memcpy(wk->ie, req->ie, req->ie_len);
@@ -2533,11 +2506,16 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        } else
                wk->ie_len = 0;
 
+       wk->bss = (void *)req->bss;
+
+       ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
+       memcpy(wk->ssid, ssid + 2, ssid[1]);
+       wk->ssid_len = ssid[1];
+
        if (req->prev_bssid)
                memcpy(wk->prev_bssid, req->prev_bssid, ETH_ALEN);
 
        wk->state = IEEE80211_MGD_STATE_ASSOC;
-       wk->tries = 0;
        wk->timeout = jiffies; /* run right away */
 
        if (req->use_mfp) {
@@ -2553,6 +2531,10 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
        else
                ifmgd->flags &= ~IEEE80211_STA_CONTROL_PORT;
 
+       sdata->local->oper_channel = req->bss->channel;
+       ieee80211_hw_config(sdata->local, 0);
+
+       list_add(&wk->list, &ifmgd->work_list);
        ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.work);
 
        err = 0;
@@ -2568,23 +2550,23 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
 {
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        struct ieee80211_mgd_work *wk;
-       const u8 *bssid = NULL;
+       const u8 *bssid = req->bss->bssid;
        bool not_auth_yet = false;
 
        mutex_lock(&ifmgd->mtx);
 
        if (ifmgd->associated && &ifmgd->associated->cbss == req->bss) {
                bssid = req->bss->bssid;
-               ieee80211_set_disassoc(sdata, true);
+               ieee80211_set_disassoc(sdata);
        } else list_for_each_entry(wk, &ifmgd->work_list, list) {
-               if (&wk->bss->cbss == req->bss) {
-                       bssid = req->bss->bssid;
-                       if (wk->state == IEEE80211_MGD_STATE_PROBE)
-                               not_auth_yet = true;
-                       list_del(&wk->list);
-                       kfree(wk);
-                       break;
-               }
+               if (wk->state != IEEE80211_MGD_STATE_PROBE)
+                       continue;
+               if (req->bss != &wk->bss->cbss)
+                       continue;
+               not_auth_yet = true;
+               list_del(&wk->list);
+               kfree(wk);
+               break;
        }
 
        /*
@@ -2601,17 +2583,6 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
                return 0;
        }
 
-       /*
-        * cfg80211 should catch this ... but it's racy since
-        * we can receive a deauth frame, process it, hand it
-        * to cfg80211 while that's in a locked section already
-        * trying to tell us that the user wants to disconnect.
-        */
-       if (!bssid) {
-               mutex_unlock(&ifmgd->mtx);
-               return -ENOLINK;
-       }
-
        mutex_unlock(&ifmgd->mtx);
 
        printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n",
@@ -2648,7 +2619,7 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
        printk(KERN_DEBUG "%s: disassociating from %pM by local choice (reason=%d)\n",
               sdata->name, req->bss->bssid, req->reason_code);
 
-       ieee80211_set_disassoc(sdata, false);
+       ieee80211_set_disassoc(sdata);
 
        mutex_unlock(&ifmgd->mtx);