summaryrefslogtreecommitdiffstats
path: root/net/mac80211/mlme.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-07-07 03:45:17 +0200
committerJohn W. Linville <linville@tuxdriver.com>2009-07-10 15:02:30 -0400
commit77fdaa12cea26c204cc12c312fe40bc0f3dcdfd8 (patch)
treec28fdd28f2ca2783783adb4b5e13b7ba57a223a3 /net/mac80211/mlme.c
parenta7c1cfc9616ee76213a6d4fd4c17f13fdc92ddce (diff)
mac80211: rework MLME for multiple authentications
Sit tight. This shakes up the world as you know it. Let go of your spaghetti tongs, they will no longer be required, the horrible statemachine in net/mac80211/mlme.c is no more... With the cfg80211 SME mac80211 now has much less to keep track of, but, on the other hand, for FT it needs to be able to keep track of at least one authentication being in progress while associated. So convert from a single state machine to having small ones for all the different things we need to do. For real FT it will still need work wrt. PS, but this should be a good step. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r--net/mac80211/mlme.c1332
1 files changed, 700 insertions, 632 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 29575eea3ed1..108e8c9c60fd 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -27,20 +27,51 @@
#include "rate.h"
#include "led.h"
-#define IEEE80211_ASSOC_SCANS_MAX_TRIES 2
#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
#define IEEE80211_AUTH_MAX_TRIES 3
#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
#define IEEE80211_ASSOC_MAX_TRIES 3
#define IEEE80211_MONITORING_INTERVAL (2 * HZ)
#define IEEE80211_PROBE_WAIT (HZ / 5)
-#define IEEE80211_PROBE_IDLE_TIME (60 * HZ)
-#define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ)
#define TMR_RUNNING_TIMER 0
#define TMR_RUNNING_CHANSW 1
+/*
+ * All cfg80211 functions have to be called outside a locked
+ * section so that they can acquire a lock themselves... This
+ * is much simpler than queuing up things in cfg80211, but we
+ * do need some indirection for that here.
+ */
+enum rx_mgmt_action {
+ /* no action required */
+ RX_MGMT_NONE,
+
+ /* caller must call cfg80211_send_rx_auth() */
+ RX_MGMT_CFG80211_AUTH,
+
+ /* caller must call cfg80211_send_rx_assoc() */
+ RX_MGMT_CFG80211_ASSOC,
+
+ /* caller must call cfg80211_send_deauth() */
+ RX_MGMT_CFG80211_DEAUTH,
+
+ /* caller must call cfg80211_send_disassoc() */
+ RX_MGMT_CFG80211_DISASSOC,
+
+ /* caller must call cfg80211_auth_timeout() & free work */
+ RX_MGMT_CFG80211_AUTH_TO,
+
+ /* caller must call cfg80211_assoc_timeout() & free work */
+ RX_MGMT_CFG80211_ASSOC_TO,
+};
+
/* utils */
+static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd)
+{
+ WARN_ON(!mutex_is_locked(&ifmgd->mtx));
+}
+
static int ecw2cw(int ecw)
{
return (1 << ecw) - 1;
@@ -74,11 +105,10 @@ static int ieee80211_compatible_rates(struct ieee80211_bss *bss,
*/
static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
struct ieee80211_ht_info *hti,
- u16 ap_ht_cap_flags)
+ const u8 *bssid, u16 ap_ht_cap_flags)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_supported_band *sband;
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct sta_info *sta;
u32 changed = 0;
u16 ht_opmode;
@@ -127,12 +157,10 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
ieee80211_hw_config(local, 0);
rcu_read_lock();
-
- sta = sta_info_get(local, ifmgd->bssid);
+ sta = sta_info_get(local, bssid);
if (sta)
rate_control_rate_update(local, sband, sta,
IEEE80211_RC_HT_CHANGED);
-
rcu_read_unlock();
}
@@ -155,7 +183,8 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
/* frame sending functions */
-static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
+static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgd_work *wk)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
@@ -165,14 +194,13 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
const u8 *ies, *ht_ie;
int i, len, count, rates_len, supp_rates_len;
u16 capab;
- struct ieee80211_bss *bss;
int wmm = 0;
struct ieee80211_supported_band *sband;
u32 rates = 0;
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
- sizeof(*mgmt) + 200 + ifmgd->extra_ie_len +
- ifmgd->ssid_len);
+ sizeof(*mgmt) + 200 + wk->ie_len +
+ wk->ssid_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
"frame\n", sdata->dev->name);
@@ -191,45 +219,35 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
}
- bss = ieee80211_rx_bss_get(local, ifmgd->bssid,
- local->hw.conf.channel->center_freq,
- ifmgd->ssid, ifmgd->ssid_len);
- if (bss) {
- if (bss->cbss.capability & WLAN_CAPABILITY_PRIVACY)
- capab |= WLAN_CAPABILITY_PRIVACY;
- if (bss->wmm_used)
- wmm = 1;
+ if (wk->bss->cbss.capability & WLAN_CAPABILITY_PRIVACY)
+ capab |= WLAN_CAPABILITY_PRIVACY;
+ if (wk->bss->wmm_used)
+ wmm = 1;
- /* get all rates supported by the device and the AP as
- * some APs don't like getting a superset of their rates
- * in the association request (e.g. D-Link DAP 1353 in
- * b-only mode) */
- rates_len = ieee80211_compatible_rates(bss, sband, &rates);
+ /* get all rates supported by the device and the AP as
+ * some APs don't like getting a superset of their rates
+ * in the association request (e.g. D-Link DAP 1353 in
+ * b-only mode) */
+ rates_len = ieee80211_compatible_rates(wk->bss, sband, &rates);
- if ((bss->cbss.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
- (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
- capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
-
- ieee80211_rx_bss_put(local, bss);
- } else {
- rates = ~0;
- rates_len = sband->n_bitrates;
- }
+ if ((wk->bss->cbss.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
+ (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
+ capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
memset(mgmt, 0, 24);
- memcpy(mgmt->da, ifmgd->bssid, ETH_ALEN);
+ memcpy(mgmt->da, wk->bss->cbss.bssid, ETH_ALEN);
memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
- memcpy(mgmt->bssid, ifmgd->bssid, ETH_ALEN);
+ memcpy(mgmt->bssid, wk->bss->cbss.bssid, ETH_ALEN);
- if (ifmgd->flags & IEEE80211_STA_PREV_BSSID_SET) {
+ if (!is_zero_ether_addr(wk->prev_bssid)) {
skb_put(skb, 10);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
IEEE80211_STYPE_REASSOC_REQ);
mgmt->u.reassoc_req.capab_info = cpu_to_le16(capab);
mgmt->u.reassoc_req.listen_interval =
cpu_to_le16(local->hw.conf.listen_interval);
- memcpy(mgmt->u.reassoc_req.current_ap, ifmgd->prev_bssid,
+ memcpy(mgmt->u.reassoc_req.current_ap, wk->prev_bssid,
ETH_ALEN);
} else {
skb_put(skb, 4);
@@ -241,10 +259,10 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
}
/* SSID */
- ies = pos = skb_put(skb, 2 + ifmgd->ssid_len);
+ ies = pos = skb_put(skb, 2 + wk->ssid_len);
*pos++ = WLAN_EID_SSID;
- *pos++ = ifmgd->ssid_len;
- memcpy(pos, ifmgd->ssid, ifmgd->ssid_len);
+ *pos++ = wk->ssid_len;
+ memcpy(pos, wk->ssid, wk->ssid_len);
/* add all rates which were marked to be used above */
supp_rates_len = rates_len;
@@ -299,9 +317,9 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
}
}
- if (ifmgd->extra_ie) {
- pos = skb_put(skb, ifmgd->extra_ie_len);
- memcpy(pos, ifmgd->extra_ie, ifmgd->extra_ie_len);
+ if (wk->ie_len && wk->ie) {
+ pos = skb_put(skb, wk->ie_len);
+ memcpy(pos, wk->ie, wk->ie_len);
}
if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED)) {
@@ -326,7 +344,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
*/
if (wmm && (ifmgd->flags & IEEE80211_STA_WMM_ENABLED) &&
sband->ht_cap.ht_supported &&
- (ht_ie = ieee80211_bss_get_ie(&bss->cbss, WLAN_EID_HT_INFORMATION)) &&
+ (ht_ie = ieee80211_bss_get_ie(&wk->bss->cbss, WLAN_EID_HT_INFORMATION)) &&
ht_ie[1] >= sizeof(struct ieee80211_ht_info) &&
(!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))) {
struct ieee80211_ht_info *ht_info =
@@ -363,18 +381,12 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
}
- kfree(ifmgd->assocreq_ies);
- ifmgd->assocreq_ies_len = (skb->data + skb->len) - ies;
- ifmgd->assocreq_ies = kmalloc(ifmgd->assocreq_ies_len, GFP_KERNEL);
- if (ifmgd->assocreq_ies)
- memcpy(ifmgd->assocreq_ies, ies, ifmgd->assocreq_ies_len);
-
ieee80211_tx_skb(sdata, skb, 0);
}
static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
- u16 stype, u16 reason)
+ const u8 *bssid, u16 stype, u16 reason)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -391,9 +403,9 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
memset(mgmt, 0, 24);
- memcpy(mgmt->da, ifmgd->bssid, ETH_ALEN);
+ memcpy(mgmt->da, bssid, ETH_ALEN);
memcpy(mgmt->sa, sdata->dev->dev_addr, ETH_ALEN);
- memcpy(mgmt->bssid, ifmgd->bssid, ETH_ALEN);
+ memcpy(mgmt->bssid, bssid, ETH_ALEN);
mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
skb_put(skb, 2);
/* u.deauth.reason_code == u.disassoc.reason_code */
@@ -477,28 +489,26 @@ static void ieee80211_chswitch_work(struct work_struct *work)
{
struct ieee80211_sub_if_data *sdata =
container_of(work, struct ieee80211_sub_if_data, u.mgd.chswitch_work);
- struct ieee80211_bss *bss;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
if (!netif_running(sdata->dev))
return;
- bss = ieee80211_rx_bss_get(sdata->local, ifmgd->bssid,
- sdata->local->hw.conf.channel->center_freq,
- ifmgd->ssid, ifmgd->ssid_len);
- if (!bss)
- goto exit;
+ mutex_lock(&ifmgd->mtx);
+ if (!ifmgd->associated)
+ goto out;
sdata->local->oper_channel = sdata->local->csa_channel;
+ ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL);
+
/* XXX: shouldn't really modify cfg80211-owned data! */
- if (!ieee80211_hw_config(sdata->local, IEEE80211_CONF_CHANGE_CHANNEL))
- bss->cbss.channel = sdata->local->oper_channel;
+ ifmgd->associated->cbss.channel = sdata->local->oper_channel;
- ieee80211_rx_bss_put(sdata->local, bss);
-exit:
- ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
ieee80211_wake_queues_by_reason(&sdata->local->hw,
IEEE80211_QUEUE_STOP_REASON_CSA);
+ out:
+ ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED;
+ mutex_unlock(&ifmgd->mtx);
}
static void ieee80211_chswitch_timer(unsigned long data)
@@ -523,7 +533,9 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata,
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
int new_freq = ieee80211_channel_to_frequency(sw_elem->new_ch_num);
- if (ifmgd->state != IEEE80211_STA_MLME_ASSOCIATED)
+ ASSERT_MGD_MTX(ifmgd);
+
+ if (!ifmgd->associated)
return;
if (sdata->local->sw_scanning || sdata->local->hw_scanning)
@@ -634,7 +646,7 @@ void ieee80211_recalc_ps(struct ieee80211_local *local, s32 latency)
}
if (count == 1 && found->u.mgd.powersave &&
- (found->u.mgd.flags & IEEE80211_STA_ASSOCIATED) &&
+ found->u.mgd.associated && list_empty(&found->u.mgd.work_list) &&
!(found->u.mgd.flags & IEEE80211_STA_PROBEREQ_POLL)) {
s32 beaconint_us;
@@ -789,9 +801,6 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
u16 capab, bool erp_valid, u8 erp)
{
struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-#endif
u32 changed = 0;
bool use_protection;
bool use_short_preamble;
@@ -808,42 +817,16 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME);
if (use_protection != bss_conf->use_cts_prot) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: CTS protection %s (BSSID=%pM)\n",
- sdata->dev->name,
- use_protection ? "enabled" : "disabled",
- ifmgd->bssid);
- }
-#endif
bss_conf->use_cts_prot = use_protection;
changed |= BSS_CHANGED_ERP_CTS_PROT;
}
if (use_short_preamble != bss_conf->use_short_preamble) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: switched to %s barker preamble"
- " (BSSID=%pM)\n",
- sdata->dev->name,
- use_short_preamble ? "short" : "long",
- ifmgd->bssid);
- }
-#endif
bss_conf->use_short_preamble = use_short_preamble;
changed |= BSS_CHANGED_ERP_PREAMBLE;
}
if (use_short_slot != bss_conf->use_short_slot) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: switched to %s slot time"
- " (BSSID=%pM)\n",
- sdata->dev->name,
- use_short_slot ? "short" : "long",
- ifmgd->bssid);
- }
-#endif
bss_conf->use_short_slot = use_short_slot;
changed |= BSS_CHANGED_ERP_SLOT;
}
@@ -852,32 +835,23 @@ 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_bss *bss,
u32 bss_info_changed)
{
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
- struct ieee80211_conf *conf = &local_to_hw(local)->conf;
-
- struct ieee80211_bss *bss;
bss_info_changed |= BSS_CHANGED_ASSOC;
- ifmgd->flags |= IEEE80211_STA_ASSOCIATED;
+ /* set timing information */
+ sdata->vif.bss_conf.beacon_int = bss->cbss.beacon_interval;
+ sdata->vif.bss_conf.timestamp = bss->cbss.tsf;
+ sdata->vif.bss_conf.dtim_period = bss->dtim_period;
- bss = ieee80211_rx_bss_get(local, ifmgd->bssid,
- conf->channel->center_freq,
- ifmgd->ssid, ifmgd->ssid_len);
- if (bss) {
- /* set timing information */
- sdata->vif.bss_conf.beacon_int = bss->cbss.beacon_interval;
- sdata->vif.bss_conf.timestamp = bss->cbss.tsf;
- sdata->vif.bss_conf.dtim_period = bss->dtim_period;
+ bss_info_changed |= BSS_CHANGED_BEACON_INT;
+ bss_info_changed |= ieee80211_handle_bss_capability(sdata,
+ bss->cbss.capability, bss->has_erp_value, bss->erp_value);
- bss_info_changed |= BSS_CHANGED_BEACON_INT;
- bss_info_changed |= ieee80211_handle_bss_capability(sdata,
- bss->cbss.capability, bss->has_erp_value, bss->erp_value);
-
- ieee80211_rx_bss_put(local, bss);
- }
+ sdata->u.mgd.associated = bss;
+ memcpy(sdata->u.mgd.bssid, bss->cbss.bssid, ETH_ALEN);
ieee80211_led_assoc(local, 1);
@@ -905,152 +879,133 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
netif_carrier_on(sdata->dev);
}
-static void ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata)
+static enum rx_mgmt_action __must_check
+ieee80211_direct_probe(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgd_work *wk)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
- ifmgd->direct_probe_tries++;
- if (ifmgd->direct_probe_tries > IEEE80211_AUTH_MAX_TRIES) {
+ wk->tries++;
+ if (wk->tries > IEEE80211_AUTH_MAX_TRIES) {
printk(KERN_DEBUG "%s: direct probe to AP %pM timed out\n",
- sdata->dev->name, ifmgd->bssid);
- ifmgd->state = IEEE80211_STA_MLME_DISABLED;
- ieee80211_recalc_idle(local);
- cfg80211_send_auth_timeout(sdata->dev, ifmgd->bssid,
- GFP_KERNEL);
+ sdata->dev->name, wk->bss->cbss.bssid);
/*
* Most likely AP is not in the range so remove the
- * bss information associated to the AP
+ * bss struct for that AP.
*/
- ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
- sdata->local->hw.conf.channel->center_freq,
- ifmgd->ssid, ifmgd->ssid_len);
+ cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss);
/*
* We might have a pending scan which had no chance to run yet
- * due to state == IEEE80211_STA_MLME_DIRECT_PROBE.
- * Hence, queue the STAs work again
+ * due to work needing to be done. Hence, queue the STAs work
+ * again for that.
*/
queue_work(local->hw.workqueue, &ifmgd->work);
- return;
+ return RX_MGMT_CFG80211_AUTH_TO;
}
- printk(KERN_DEBUG "%s: direct probe to AP %pM try %d\n",
- sdata->dev->name, ifmgd->bssid,
- ifmgd->direct_probe_tries);
+ printk(KERN_DEBUG "%s: direct probe to AP %pM (try %d)\n",
+ sdata->dev->name, wk->bss->cbss.bssid,
+ wk->tries);
- ifmgd->state = IEEE80211_STA_MLME_DIRECT_PROBE;
-
- /* Direct probe is sent to broadcast address as some APs
+ /*
+ * Direct probe is sent to broadcast address as some APs
* will not answer to direct packet in unassociated state.
*/
- ieee80211_send_probe_req(sdata, NULL,
- ifmgd->ssid, ifmgd->ssid_len, NULL, 0);
+ ieee80211_send_probe_req(sdata, NULL, wk->ssid, wk->ssid_len, NULL, 0);
+
+ wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
+ mod_timer(&ifmgd->timer, wk->timeout);
- mod_timer(&ifmgd->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
+ return RX_MGMT_NONE;
}
-static void ieee80211_authenticate(struct ieee80211_sub_if_data *sdata)
+static enum rx_mgmt_action __must_check
+ieee80211_authenticate(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgd_work *wk)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
- ifmgd->auth_tries++;
- if (ifmgd->auth_tries > IEEE80211_AUTH_MAX_TRIES) {
+ wk->tries++;
+ if (wk->tries > IEEE80211_AUTH_MAX_TRIES) {
printk(KERN_DEBUG "%s: authentication with AP %pM"
" timed out\n",
- sdata->dev->name, ifmgd->bssid);
- ifmgd->state = IEEE80211_STA_MLME_DISABLED;
- ieee80211_recalc_idle(local);
- cfg80211_send_auth_timeout(sdata->dev, ifmgd->bssid,
- GFP_KERNEL);
- ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
- sdata->local->hw.conf.channel->center_freq,
- ifmgd->ssid, ifmgd->ssid_len);
+ sdata->dev->name, wk->bss->cbss.bssid);
+
+ /*
+ * Most likely AP is not in the range so remove the
+ * bss struct for that AP.
+ */
+ cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss);
/*
* We might have a pending scan which had no chance to run yet
- * due to state == IEEE80211_STA_MLME_AUTHENTICATE.
- * Hence, queue the STAs work again
+ * due to work needing to be done. Hence, queue the STAs work
+ * again for that.
*/
queue_work(local->hw.workqueue, &ifmgd->work);
- return;
+ return RX_MGMT_CFG80211_AUTH_TO;
}
- ifmgd->state = IEEE80211_STA_MLME_AUTHENTICATE;
- printk(KERN_DEBUG "%s: authenticate with AP %pM\n",
- sdata->dev->name, ifmgd->bssid);
+ printk(KERN_DEBUG "%s: authenticate with AP %pM (try %d)\n",
+ sdata->dev->name, wk->bss->cbss.bssid, wk->tries);
+
+ ieee80211_send_auth(sdata, 1, wk->auth_alg, wk->ie, wk->ie_len,
+ wk->bss->cbss.bssid, 0);
+ wk->auth_transaction = 2;
- ieee80211_send_auth(sdata, 1, ifmgd->auth_alg, ifmgd->sme_auth_ie,
- ifmgd->sme_auth_ie_len, ifmgd->bssid, 0);
- ifmgd->auth_transaction = 2;
+ wk->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
+ mod_timer(&ifmgd->timer, wk->timeout);
- mod_timer(&ifmgd->timer, jiffies + IEEE80211_AUTH_TIMEOUT);
+ return RX_MGMT_NONE;
}
-/*
- * The disassoc 'reason' argument can be either our own reason
- * if self disconnected or a reason code from the AP.
- */
static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
- bool deauth, bool self_disconnected,
- u16 reason)
+ const u8 *bssid, bool deauth)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
- struct ieee80211_conf *conf = &local_to_hw(local)->conf;
- struct ieee80211_bss *bss;
struct sta_info *sta;
u32 changed = 0, config_changed = 0;
- if (deauth) {
- ifmgd->direct_probe_tries = 0;
- ifmgd->auth_tries = 0;
- }
- ifmgd->assoc_scan_tries = 0;
- ifmgd->assoc_tries = 0;
+ ASSERT_MGD_MTX(ifmgd);
+
+ ifmgd->associated = NULL;
+ memset(ifmgd->bssid, 0, ETH_ALEN);
+
+ /*
+ * we need to commit the associated = NULL change because the
+ * scan code uses that to determine whether this iface should
+ * go to/wake up from powersave or not -- and could otherwise
+ * wake the queues erroneously.
+ */
+ smp_mb();
+
+ /*
+ * Thus, we can only afterwards stop the queues -- to account
+ * for the case where another CPU is finishing a scan at this
+ * time -- we don't want the scan code to enable queues.
+ */
netif_tx_stop_all_queues(sdata->dev);
netif_carrier_off(sdata->dev);
rcu_read_lock();
- sta = sta_info_get(local, ifmgd->bssid);
+ sta = sta_info_get(local, bssid);
if (sta)
ieee80211_sta_tear_down_BA_sessions(sta);
rcu_read_unlock();
- bss = ieee80211_rx_bss_get(local, ifmgd->bssid,
- conf->channel->center_freq,
- ifmgd->ssid, ifmgd->ssid_len);
-
- if (bss)
- ieee80211_rx_bss_put(local, bss);
-
- if (self_disconnected) {
- if (deauth)
- ieee80211_send_deauth_disassoc(sdata,
- IEEE80211_STYPE_DEAUTH, reason);
- else
- ieee80211_send_deauth_disassoc(sdata,
- IEEE80211_STYPE_DISASSOC, reason);
- }
-
- ifmgd->flags &= ~IEEE80211_STA_ASSOCIATED;
changed |= ieee80211_reset_erp_info(sdata);
ieee80211_led_assoc(local, 0);
changed |= BSS_CHANGED_ASSOC;
sdata->vif.bss_conf.assoc = false;
- if (self_disconnected || reason == WLAN_REASON_DISASSOC_STA_HAS_LEFT) {
- ifmgd->state = IEEE80211_STA_MLME_DISABLED;
- ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
- sdata->local->hw.conf.channel->center_freq,
- ifmgd->ssid, ifmgd->ssid_len);
- }
-
ieee80211_set_wmm_default(sdata);
ieee80211_recalc_idle(local);
@@ -1079,7 +1034,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
rcu_read_lock();
- sta = sta_info_get(local, ifmgd->bssid);
+ sta = sta_info_get(local, bssid);
if (!sta) {
rcu_read_unlock();
return;
@@ -1092,38 +1047,42 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
sta_info_destroy(sta);
}
-static void ieee80211_associate(struct ieee80211_sub_if_data *sdata)
+static enum rx_mgmt_action __must_check
+ieee80211_associate(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgd_work *wk)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
- ifmgd->assoc_tries++;
- if (ifmgd->assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
+ wk->tries++;
+ if (wk->tries > IEEE80211_ASSOC_MAX_TRIES) {
printk(KERN_DEBUG "%s: association with AP %pM"
" timed out\n",
- sdata->dev->name, ifmgd->bssid);
- ifmgd->state = IEEE80211_STA_MLME_DISABLED;
- ieee80211_recalc_idle(local);
- cfg80211_send_assoc_timeout(sdata->dev, ifmgd->bssid,
- GFP_KERNEL);
- ieee80211_rx_bss_remove(sdata, ifmgd->bssid,
- sdata->local->hw.conf.channel->center_freq,
- ifmgd->ssid, ifmgd->ssid_len);
+ sdata->dev->name, wk->bss->cbss.bssid);
+
+ /*
+ * Most likely AP is not in the range so remove the
+ * bss struct for that AP.
+ */
+ cfg80211_unlink_bss(local->hw.wiphy, &wk->bss->cbss);
+
/*
* We might have a pending scan which had no chance to run yet
- * due to state == IEEE80211_STA_MLME_ASSOCIATE.
- * Hence, queue the STAs work again
+ * due to work needing to be done. Hence, queue the STAs work
+ * again for that.
*/
queue_work(local->hw.workqueue, &ifmgd->work);
- return;
+ return RX_MGMT_CFG80211_ASSOC_TO;
}
- ifmgd->state = IEEE80211_STA_MLME_ASSOCIATE;
- printk(KERN_DEBUG "%s: associate with AP %pM\n",
- sdata->dev->name, ifmgd->bssid);
- ieee80211_send_assoc(sdata);
+ printk(KERN_DEBUG "%s: associate with AP %pM (try %d)\n",
+ sdata->dev->name, wk->bss->cbss.bssid, wk->tries);
+ ieee80211_send_assoc(sdata, wk);
+
+ wk->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
+ mod_timer(&ifmgd->timer, wk->timeout);
- mod_timer(&ifmgd->timer, jiffies + IEEE80211_ASSOC_TIMEOUT);
+ return RX_MGMT_NONE;
}
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
@@ -1148,6 +1107,7 @@ void ieee80211_beacon_loss_work(struct work_struct *work)
container_of(work, struct ieee80211_sub_if_data,
u.mgd.beacon_loss_work);
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ const u8 *ssid;
/*
* The driver has already reported this event and we have
@@ -1160,12 +1120,15 @@ void ieee80211_beacon_loss_work(struct work_struct *work)
if (ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL)
return;
+ mutex_lock(&ifmgd->mtx);
+
+ if (!ifmgd->associated)
+ goto out;
+
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: driver reports beacon loss from AP %pM "
- "- sending probe request\n", sdata->dev->name,
- sdata->u.mgd.bssid);
- }
+ if (net_ratelimit())
+ printk(KERN_DEBUG "%s: driver reports beacon loss from AP "
+ "- sending probe request\n", sdata->dev->name);
#endif
ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL;
@@ -1174,10 +1137,13 @@ void ieee80211_beacon_loss_work(struct work_struct *work)
ieee80211_recalc_ps(sdata->local, -1);
mutex_unlock(&sdata->local->iflist_mtx);
- ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid,
- ifmgd->ssid_len, NULL, 0);
+ ssid = ieee80211_bss_get_ie(&ifmgd->associated->cbss, WLAN_EID_SSID);
+ ieee80211_send_probe_req(sdata, ifmgd->associated->cbss.bssid,
+ ssid + 2, ssid[1], NULL, 0);
mod_timer(&ifmgd->timer, jiffies + IEEE80211_PROBE_WAIT);
+ out:
+ mutex_unlock(&ifmgd->mtx);
}
void ieee80211_beacon_loss(struct ieee80211_vif *vif)
@@ -1189,102 +1155,16 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif)
}
EXPORT_SYMBOL(ieee80211_beacon_loss);
-static void ieee80211_associated(struct ieee80211_sub_if_data *sdata)
+static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgd_work *wk)
{
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- struct ieee80211_local *local = sdata->local;
- struct sta_info *sta;
- unsigned long last_rx;
- bool disassoc = false;
-
- /* TODO: start monitoring current AP signal quality and number of
- * missed beacons. Scan other channels every now and then and search
- * for better APs. */
- /* TODO: remove expired BSSes */
-
- ifmgd->state = IEEE80211_STA_MLME_ASSOCIATED;
-
- rcu_read_lock();
-
- sta = sta_info_get(local, ifmgd->bssid);
- if (!sta) {
- printk(KERN_DEBUG "%s: No STA entry for own AP %pM\n",
- sdata->dev->name, ifmgd->bssid);
- disassoc = true;
- rcu_read_unlock();
- goto out;
- }
-
- last_rx = sta->last_rx;
- rcu_read_unlock();
-
- if ((ifmgd->flags & IEEE80211_STA_PROBEREQ_POLL) &&
- time_after(jiffies, last_rx + IEEE80211_PROBE_WAIT)) {
- printk(KERN_DEBUG "%s: no probe response from AP %pM "
- "- disassociating\n",
- sdata->dev->name, ifmgd->bssid);
- disassoc = true;
- ifmgd->flags &= ~IEEE80211_STA_PROBEREQ_POLL;
- goto out;
- }
-
- /*
- * Beacon filtering is only enabled with power save and then the
- * stack should not check for beacon loss.
- */
- if (!((local->hw.flags & IEEE80211_HW_BEACON_FILTER) &&
- (local->hw.conf.flags & IEEE80211_CONF_PS)) &&
- time_after(jiffies,
- ifmgd->last_beacon + IEEE80211_MONITORING_INTERVAL)) {
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
- if (net_ratelimit()) {
- printk(KERN_DEBUG "%s: beacon loss from AP %pM "
- "- sending probe request\n",
- sdata->dev->name, ifmgd->bssid);
- }
-#endif
- ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL;
- mutex_lock(&local->iflist_mtx);
- ieee80211_recalc_ps(local, -1);
- mutex_unlock(&local->iflist_mtx);
- ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid,
- ifmgd->ssid_len, NULL, 0);
- mod_timer(&ifmgd->timer, jiffies + IEEE80211_PROBE_WAIT);
- goto out;
- }
-
- if (time_after(jiffies, last_rx + IEEE80211_PROBE_IDLE_TIME)) {
- ifmgd->flags |= IEEE80211_STA_PROBEREQ_POLL;
- mutex_lock(&local->iflist_mtx);
- ieee80211_recalc_ps(local, -1);
- mutex_unlock(&local->iflist_mtx);
- ieee80211_send_probe_req(sdata, ifmgd->bssid, ifmgd->ssid,
- ifmgd->ssid_len, NULL, 0);
- }
-
- out:
- if (!disassoc)
- mod_timer(&ifmgd->timer,
- jiffies + IEEE80211_MONITORING_INTERVAL);
- else
- ieee80211_set_disassoc(sdata, true, true,
- WLAN_REASON_PREV_AUTH_NOT_VALID);
-}
-
-
-static void ieee80211_auth_completed(struct ieee80211_sub_if_data *sdata)
-{
- struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
-
+ wk->state = IEEE80211_MGD_STATE_IDLE;
printk(KERN_DEBUG "%s: authenticated\n", sdata->dev->name);
- ifmgd->flags |= IEEE80211_STA_AUTHENTICATED;
- /* Wait for SME to request association */
- ifmgd->state = IEEE80211_STA_MLME_DISABLED;
- ieee80211_recalc_idle(sdata->local);
}
static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgd_work *wk,
struct ieee80211_mgmt *mgmt,
size_t len)
{
@@ -1295,120 +1175,132 @@ static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
if (!elems.challenge)
return;
- ieee80211_send_auth(sdata, 3, sdata->u.mgd.auth_alg,
+ ieee80211_send_auth(sdata, 3, wk->auth_alg,
elems.challenge - 2, elems.challenge_len + 2,
- sdata->u.mgd.bssid, 1);
- sdata->u.mgd.auth_transaction = 4;
+ wk->bss->cbss.bssid, 1);
+ wk->auth_transaction = 4;
}
-static void ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt,
- size_t len)
+static enum rx_mgmt_action __must_check
+ieee80211_rx_mgmt_auth(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;
u16 auth_alg, auth_transaction, status_code;
- if (ifmgd->state != IEEE80211_STA_MLME_AUTHENTICATE)
- return;
+ if (wk->state != IEEE80211_MGD_STATE_AUTH)
+ return RX_MGMT_NONE;
if (len < 24 + 6)
- return;
+ return RX_MGMT_NONE;
- if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN) != 0)
- return;
+ if (memcmp(wk->bss->cbss.bssid, mgmt->sa, ETH_ALEN) != 0)
+ return RX_MGMT_NONE;
- if (memcmp(ifmgd->bssid, mgmt->bssid, ETH_ALEN) != 0)
- return;
+ if (memcmp(wk->bss->cbss.bssid, mgmt->bssid, ETH_ALEN) != 0)
+ return RX_MGMT_NONE;
auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg);
auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction);
status_code = le16_to_cpu(mgmt->u.auth.status_code);
- if (auth_alg != ifmgd->auth_alg ||
- auth_transaction != ifmgd->auth_transaction)
- return;
+ if (auth_alg != wk->auth_alg ||
+ auth_transaction != wk->auth_transaction)
+ return RX_MGMT_NONE;
if (status_code != WLAN_STATUS_SUCCESS) {
- cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len,
- GFP_KERNEL);
- ifmgd->state = IEEE80211_STA_MLME_DISABLED;
- ieee80211_recalc_idle(sdata->local);
- return;
+ list_del(&wk->list);
+ kfree(wk);
+ return RX_MGMT_CFG80211_AUTH;
}
- switch (ifmgd->auth_alg) {
+ switch (wk->auth_alg) {
case WLAN_AUTH_OPEN:
case WLAN_AUTH_LEAP:
case WLAN_AUTH_FT:
- ieee80211_auth_completed(sdata);
- cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len,
- GFP_KERNEL);
- break;
+ ieee80211_auth_completed(sdata, wk);
+ return RX_MGMT_CFG80211_AUTH;
case WLAN_AUTH_SHARED_KEY:
- if (ifmgd->auth_transaction == 4) {
- ieee80211_auth_completed(sdata);
- cfg80211_send_rx_auth(sdata->dev, (u8 *) mgmt, len,
- GFP_KERNEL);
+ if (wk->auth_transaction == 4) {
+ ieee80211_auth_completed(sdata, wk);
+ return RX_MGMT_CFG80211_AUTH;
} else
- ieee80211_auth_challenge(sdata, mgmt, len);
+ ieee80211_auth_challenge(sdata, wk, mgmt, len);
break;
}
+
+ return RX_MGMT_NONE;
}
-static void ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt,
- size_t len)
+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;
+ const u8 *bssid = NULL;
u16 reason_code;
if (len < 24 + 2)
- return;
+ return RX_MGMT_NONE;
- if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN))
- return;
+ ASSERT_MGD_MTX(ifmgd);
+
+ if (wk)
+ bssid = wk->bss->cbss.bssid;
+ else
+ bssid = ifmgd->associated->cbss.bssid;
reason_code = le16_to_cpu(mgmt->u.deauth.reason_code);
- if (ifmgd->flags & IEEE80211_STA_AUTHENTICATED)
- printk(KERN_DEBUG "%s: deauthenticated (Reason: %u)\n",
- sdata->dev->name, reason_code);
+ printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n",
+ sdata->dev->name, bssid, reason_code);
+
+ if (!wk) {
+ ieee80211_set_disassoc(sdata, bssid, true);
+ } else {
+ list_del(&wk->list);
+ kfree(wk);
+ }
- ieee80211_set_disassoc(sdata, true, false, 0);
- ifmgd->flags &= ~IEEE80211_STA_AUTHENTICATED;
- cfg80211_send_deauth(sdata->dev, (u8 *) mgmt, len, GFP_KERNEL);
+ return RX_MGMT_CFG80211_DEAUTH;
}
-static void ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
- struct ieee80211_mgmt *mgmt,
- size_t len)
+static enum rx_mgmt_action __must_check
+ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_mgmt *mgmt, size_t len)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u16 reason_code;
if (len < 24 + 2)
- return;
+ return RX_MGMT_NONE;
- if (memcmp(ifmgd->bssid, mgmt->sa, ETH_ALEN))
- return;
+ ASSERT_MGD_MTX(ifmgd);
+
+ if (WARN_ON(!ifmgd->associated))
+ return RX_MGMT_NONE;
+
+ if (WARN_ON(memcmp(ifmgd->associated->cbss.bssid, mgmt->sa, ETH_ALEN)))
+ return RX_MGMT_NONE;
reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
- if (ifmgd->flags & IEEE80211_STA_ASSOCIATED)
- printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n",
- sdata->dev->name, reason_code);
+ printk(KERN_DEBUG "%s: disassociated (Reason: %u)\n",
+ sdata->dev->name, reason_code);
- ieee80211_set_disassoc(sdata, false, false, reason_code);
- cfg80211_s