summaryrefslogtreecommitdiffstats
path: root/net/mac80211/mlme.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r--net/mac80211/mlme.c173
1 files changed, 72 insertions, 101 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 7f9909340c09..f060bc616203 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -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);