|
@@ -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);
|
|
|
|