|
|
@@ -30,6 +30,12 @@
|
|
|
#include "rate.h"
|
|
|
#include "led.h"
|
|
|
|
|
|
+#define IEEE80211_AUTH_TIMEOUT (HZ / 5)
|
|
|
+#define IEEE80211_AUTH_MAX_TRIES 3
|
|
|
+#define IEEE80211_AUTH_WAIT_ASSOC (HZ * 5)
|
|
|
+#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
|
|
|
+#define IEEE80211_ASSOC_MAX_TRIES 3
|
|
|
+
|
|
|
static int max_nullfunc_tries = 2;
|
|
|
module_param(max_nullfunc_tries, int, 0644);
|
|
|
MODULE_PARM_DESC(max_nullfunc_tries,
|
|
|
@@ -97,6 +103,15 @@ enum rx_mgmt_action {
|
|
|
|
|
|
/* caller must call cfg80211_send_disassoc() */
|
|
|
RX_MGMT_CFG80211_DISASSOC,
|
|
|
+
|
|
|
+ /* caller must call cfg80211_send_rx_auth() */
|
|
|
+ RX_MGMT_CFG80211_RX_AUTH,
|
|
|
+
|
|
|
+ /* caller must call cfg80211_send_rx_assoc() */
|
|
|
+ RX_MGMT_CFG80211_RX_ASSOC,
|
|
|
+
|
|
|
+ /* caller must call cfg80211_send_assoc_timeout() */
|
|
|
+ RX_MGMT_CFG80211_ASSOC_TIMEOUT,
|
|
|
};
|
|
|
|
|
|
/* utils */
|
|
|
@@ -115,8 +130,7 @@ static inline void ASSERT_MGD_MTX(struct ieee80211_if_managed *ifmgd)
|
|
|
* has happened -- the work that runs from this timer will
|
|
|
* do that.
|
|
|
*/
|
|
|
-static void run_again(struct ieee80211_if_managed *ifmgd,
|
|
|
- unsigned long timeout)
|
|
|
+static void run_again(struct ieee80211_if_managed *ifmgd, unsigned long timeout)
|
|
|
{
|
|
|
ASSERT_MGD_MTX(ifmgd);
|
|
|
|
|
|
@@ -284,6 +298,319 @@ static u32 ieee80211_enable_ht(struct ieee80211_sub_if_data *sdata,
|
|
|
|
|
|
/* frame sending functions */
|
|
|
|
|
|
+static int ieee80211_compatible_rates(const u8 *supp_rates, int supp_rates_len,
|
|
|
+ struct ieee80211_supported_band *sband,
|
|
|
+ u32 *rates)
|
|
|
+{
|
|
|
+ int i, j, count;
|
|
|
+ *rates = 0;
|
|
|
+ count = 0;
|
|
|
+ for (i = 0; i < supp_rates_len; i++) {
|
|
|
+ int rate = (supp_rates[i] & 0x7F) * 5;
|
|
|
+
|
|
|
+ for (j = 0; j < sband->n_bitrates; j++)
|
|
|
+ if (sband->bitrates[j].bitrate == rate) {
|
|
|
+ *rates |= BIT(j);
|
|
|
+ count++;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return count;
|
|
|
+}
|
|
|
+
|
|
|
+static void ieee80211_add_ht_ie(struct ieee80211_sub_if_data *sdata,
|
|
|
+ struct sk_buff *skb, const u8 *ht_info_ie,
|
|
|
+ struct ieee80211_supported_band *sband,
|
|
|
+ struct ieee80211_channel *channel,
|
|
|
+ enum ieee80211_smps_mode smps)
|
|
|
+{
|
|
|
+ struct ieee80211_ht_info *ht_info;
|
|
|
+ u8 *pos;
|
|
|
+ u32 flags = channel->flags;
|
|
|
+ u16 cap;
|
|
|
+ struct ieee80211_sta_ht_cap ht_cap;
|
|
|
+
|
|
|
+ BUILD_BUG_ON(sizeof(ht_cap) != sizeof(sband->ht_cap));
|
|
|
+
|
|
|
+ if (!sband->ht_cap.ht_supported)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (!ht_info_ie)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (ht_info_ie[1] < sizeof(struct ieee80211_ht_info))
|
|
|
+ return;
|
|
|
+
|
|
|
+ memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap));
|
|
|
+ ieee80211_apply_htcap_overrides(sdata, &ht_cap);
|
|
|
+
|
|
|
+ ht_info = (struct ieee80211_ht_info *)(ht_info_ie + 2);
|
|
|
+
|
|
|
+ /* determine capability flags */
|
|
|
+ cap = ht_cap.cap;
|
|
|
+
|
|
|
+ switch (ht_info->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) {
|
|
|
+ case IEEE80211_HT_PARAM_CHA_SEC_ABOVE:
|
|
|
+ if (flags & IEEE80211_CHAN_NO_HT40PLUS) {
|
|
|
+ cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
|
|
+ cap &= ~IEEE80211_HT_CAP_SGI_40;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case IEEE80211_HT_PARAM_CHA_SEC_BELOW:
|
|
|
+ if (flags & IEEE80211_CHAN_NO_HT40MINUS) {
|
|
|
+ cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
|
|
|
+ cap &= ~IEEE80211_HT_CAP_SGI_40;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* set SM PS mode properly */
|
|
|
+ cap &= ~IEEE80211_HT_CAP_SM_PS;
|
|
|
+ switch (smps) {
|
|
|
+ case IEEE80211_SMPS_AUTOMATIC:
|
|
|
+ case IEEE80211_SMPS_NUM_MODES:
|
|
|
+ WARN_ON(1);
|
|
|
+ case IEEE80211_SMPS_OFF:
|
|
|
+ cap |= WLAN_HT_CAP_SM_PS_DISABLED <<
|
|
|
+ IEEE80211_HT_CAP_SM_PS_SHIFT;
|
|
|
+ break;
|
|
|
+ case IEEE80211_SMPS_STATIC:
|
|
|
+ cap |= WLAN_HT_CAP_SM_PS_STATIC <<
|
|
|
+ IEEE80211_HT_CAP_SM_PS_SHIFT;
|
|
|
+ break;
|
|
|
+ case IEEE80211_SMPS_DYNAMIC:
|
|
|
+ cap |= WLAN_HT_CAP_SM_PS_DYNAMIC <<
|
|
|
+ IEEE80211_HT_CAP_SM_PS_SHIFT;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* reserve and fill IE */
|
|
|
+ pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2);
|
|
|
+ ieee80211_ie_build_ht_cap(pos, &ht_cap, cap);
|
|
|
+}
|
|
|
+
|
|
|
+static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
|
|
|
+{
|
|
|
+ struct ieee80211_local *local = sdata->local;
|
|
|
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
+ struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
|
|
|
+ struct sk_buff *skb;
|
|
|
+ struct ieee80211_mgmt *mgmt;
|
|
|
+ u8 *pos, qos_info;
|
|
|
+ size_t offset = 0, noffset;
|
|
|
+ int i, count, rates_len, supp_rates_len;
|
|
|
+ u16 capab;
|
|
|
+ struct ieee80211_supported_band *sband;
|
|
|
+ u32 rates = 0;
|
|
|
+ struct ieee80211_bss *bss = (void *)assoc_data->bss->priv;
|
|
|
+
|
|
|
+ lockdep_assert_held(&ifmgd->mtx);
|
|
|
+
|
|
|
+ sband = local->hw.wiphy->bands[local->oper_channel->band];
|
|
|
+
|
|
|
+ if (assoc_data->supp_rates_len) {
|
|
|
+ /*
|
|
|
+ * 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(assoc_data->supp_rates,
|
|
|
+ assoc_data->supp_rates_len,
|
|
|
+ sband, &rates);
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * In case AP not provide any supported rates information
|
|
|
+ * before association, we send information element(s) with
|
|
|
+ * all rates that we support.
|
|
|
+ */
|
|
|
+ rates = ~0;
|
|
|
+ rates_len = sband->n_bitrates;
|
|
|
+ }
|
|
|
+
|
|
|
+ skb = alloc_skb(local->hw.extra_tx_headroom +
|
|
|
+ sizeof(*mgmt) + /* bit too much but doesn't matter */
|
|
|
+ 2 + assoc_data->ssid_len + /* SSID */
|
|
|
+ 4 + rates_len + /* (extended) rates */
|
|
|
+ 4 + /* power capability */
|
|
|
+ 2 + 2 * sband->n_channels + /* supported channels */
|
|
|
+ 2 + sizeof(struct ieee80211_ht_cap) + /* HT */
|
|
|
+ assoc_data->ie_len + /* extra IEs */
|
|
|
+ 9, /* WMM */
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!skb)
|
|
|
+ return;
|
|
|
+
|
|
|
+ skb_reserve(skb, local->hw.extra_tx_headroom);
|
|
|
+
|
|
|
+ capab = WLAN_CAPABILITY_ESS;
|
|
|
+
|
|
|
+ if (sband->band == IEEE80211_BAND_2GHZ) {
|
|
|
+ if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
|
|
|
+ capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
|
|
|
+ if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
|
|
|
+ capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (assoc_data->capability & WLAN_CAPABILITY_PRIVACY)
|
|
|
+ capab |= WLAN_CAPABILITY_PRIVACY;
|
|
|
+
|
|
|
+ if ((assoc_data->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, assoc_data->bss->bssid, ETH_ALEN);
|
|
|
+ memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
|
|
|
+ memcpy(mgmt->bssid, assoc_data->bss->bssid, ETH_ALEN);
|
|
|
+
|
|
|
+ if (!is_zero_ether_addr(assoc_data->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, assoc_data->prev_bssid,
|
|
|
+ ETH_ALEN);
|
|
|
+ } else {
|
|
|
+ skb_put(skb, 4);
|
|
|
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
|
|
+ IEEE80211_STYPE_ASSOC_REQ);
|
|
|
+ mgmt->u.assoc_req.capab_info = cpu_to_le16(capab);
|
|
|
+ mgmt->u.assoc_req.listen_interval =
|
|
|
+ cpu_to_le16(local->hw.conf.listen_interval);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* SSID */
|
|
|
+ pos = skb_put(skb, 2 + assoc_data->ssid_len);
|
|
|
+ *pos++ = WLAN_EID_SSID;
|
|
|
+ *pos++ = assoc_data->ssid_len;
|
|
|
+ memcpy(pos, assoc_data->ssid, assoc_data->ssid_len);
|
|
|
+
|
|
|
+ /* add all rates which were marked to be used above */
|
|
|
+ supp_rates_len = rates_len;
|
|
|
+ if (supp_rates_len > 8)
|
|
|
+ supp_rates_len = 8;
|
|
|
+
|
|
|
+ pos = skb_put(skb, supp_rates_len + 2);
|
|
|
+ *pos++ = WLAN_EID_SUPP_RATES;
|
|
|
+ *pos++ = supp_rates_len;
|
|
|
+
|
|
|
+ count = 0;
|
|
|
+ for (i = 0; i < sband->n_bitrates; i++) {
|
|
|
+ if (BIT(i) & rates) {
|
|
|
+ int rate = sband->bitrates[i].bitrate;
|
|
|
+ *pos++ = (u8) (rate / 5);
|
|
|
+ if (++count == 8)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rates_len > count) {
|
|
|
+ pos = skb_put(skb, rates_len - count + 2);
|
|
|
+ *pos++ = WLAN_EID_EXT_SUPP_RATES;
|
|
|
+ *pos++ = rates_len - count;
|
|
|
+
|
|
|
+ for (i++; i < sband->n_bitrates; i++) {
|
|
|
+ if (BIT(i) & rates) {
|
|
|
+ int rate = sband->bitrates[i].bitrate;
|
|
|
+ *pos++ = (u8) (rate / 5);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT) {
|
|
|
+ /* 1. power capabilities */
|
|
|
+ pos = skb_put(skb, 4);
|
|
|
+ *pos++ = WLAN_EID_PWR_CAPABILITY;
|
|
|
+ *pos++ = 2;
|
|
|
+ *pos++ = 0; /* min tx power */
|
|
|
+ *pos++ = local->oper_channel->max_power; /* max tx power */
|
|
|
+
|
|
|
+ /* 2. supported channels */
|
|
|
+ /* TODO: get this in reg domain format */
|
|
|
+ pos = skb_put(skb, 2 * sband->n_channels + 2);
|
|
|
+ *pos++ = WLAN_EID_SUPPORTED_CHANNELS;
|
|
|
+ *pos++ = 2 * sband->n_channels;
|
|
|
+ for (i = 0; i < sband->n_channels; i++) {
|
|
|
+ *pos++ = ieee80211_frequency_to_channel(
|
|
|
+ sband->channels[i].center_freq);
|
|
|
+ *pos++ = 1; /* one channel in the subband*/
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* if present, add any custom IEs that go before HT */
|
|
|
+ if (assoc_data->ie_len && assoc_data->ie) {
|
|
|
+ static const u8 before_ht[] = {
|
|
|
+ WLAN_EID_SSID,
|
|
|
+ WLAN_EID_SUPP_RATES,
|
|
|
+ WLAN_EID_EXT_SUPP_RATES,
|
|
|
+ WLAN_EID_PWR_CAPABILITY,
|
|
|
+ WLAN_EID_SUPPORTED_CHANNELS,
|
|
|
+ WLAN_EID_RSN,
|
|
|
+ WLAN_EID_QOS_CAPA,
|
|
|
+ WLAN_EID_RRM_ENABLED_CAPABILITIES,
|
|
|
+ WLAN_EID_MOBILITY_DOMAIN,
|
|
|
+ WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
|
|
|
+ };
|
|
|
+ noffset = ieee80211_ie_split(assoc_data->ie, assoc_data->ie_len,
|
|
|
+ before_ht, ARRAY_SIZE(before_ht),
|
|
|
+ offset);
|
|
|
+ pos = skb_put(skb, noffset - offset);
|
|
|
+ memcpy(pos, assoc_data->ie + offset, noffset - offset);
|
|
|
+ offset = noffset;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N) &&
|
|
|
+ bss->wmm_used && local->hw.queues >= 4)
|
|
|
+ ieee80211_add_ht_ie(sdata, skb, assoc_data->ht_information_ie,
|
|
|
+ sband, local->oper_channel, ifmgd->ap_smps);
|
|
|
+
|
|
|
+ /* if present, add any custom non-vendor IEs that go after HT */
|
|
|
+ if (assoc_data->ie_len && assoc_data->ie) {
|
|
|
+ noffset = ieee80211_ie_split_vendor(assoc_data->ie,
|
|
|
+ assoc_data->ie_len,
|
|
|
+ offset);
|
|
|
+ pos = skb_put(skb, noffset - offset);
|
|
|
+ memcpy(pos, assoc_data->ie + offset, noffset - offset);
|
|
|
+ offset = noffset;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (assoc_data->wmm_used && local->hw.queues >= 4) {
|
|
|
+ if (assoc_data->uapsd_used) {
|
|
|
+ qos_info = local->uapsd_queues;
|
|
|
+ qos_info |= (local->uapsd_max_sp_len <<
|
|
|
+ IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT);
|
|
|
+ } else {
|
|
|
+ qos_info = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ pos = skb_put(skb, 9);
|
|
|
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
|
|
|
+ *pos++ = 7; /* len */
|
|
|
+ *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
|
|
|
+ *pos++ = 0x50;
|
|
|
+ *pos++ = 0xf2;
|
|
|
+ *pos++ = 2; /* WME */
|
|
|
+ *pos++ = 0; /* WME info */
|
|
|
+ *pos++ = 1; /* WME ver */
|
|
|
+ *pos++ = qos_info;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* add any remaining custom (i.e. vendor specific here) IEs */
|
|
|
+ if (assoc_data->ie_len && assoc_data->ie) {
|
|
|
+ noffset = assoc_data->ie_len;
|
|
|
+ pos = skb_put(skb, noffset - offset);
|
|
|
+ memcpy(pos, assoc_data->ie + offset, noffset - offset);
|
|
|
+ }
|
|
|
+
|
|
|
+ IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
|
|
+ ieee80211_tx_skb(sdata, skb);
|
|
|
+}
|
|
|
+
|
|
|
static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
|
|
|
const u8 *bssid, u16 stype, u16 reason,
|
|
|
void *cookie, bool send_frame)
|
|
|
@@ -1423,6 +1750,135 @@ void ieee80211_connection_loss(struct ieee80211_vif *vif)
|
|
|
EXPORT_SYMBOL(ieee80211_connection_loss);
|
|
|
|
|
|
|
|
|
+static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata,
|
|
|
+ bool assoc)
|
|
|
+{
|
|
|
+ struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data;
|
|
|
+
|
|
|
+ lockdep_assert_held(&sdata->u.mgd.mtx);
|
|
|
+
|
|
|
+ if (auth_data->synced)
|
|
|
+ drv_finish_tx_sync(sdata->local, sdata,
|
|
|
+ auth_data->bss->bssid,
|
|
|
+ IEEE80211_TX_SYNC_AUTH);
|
|
|
+
|
|
|
+ if (!assoc) {
|
|
|
+ sta_info_destroy_addr(sdata, auth_data->bss->bssid);
|
|
|
+
|
|
|
+ memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
|
|
|
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
|
|
|
+ }
|
|
|
+
|
|
|
+ cfg80211_put_bss(auth_data->bss);
|
|
|
+ kfree(auth_data);
|
|
|
+ sdata->u.mgd.auth_data = NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static void ieee80211_auth_challenge(struct ieee80211_sub_if_data *sdata,
|
|
|
+ struct ieee80211_mgmt *mgmt, size_t len)
|
|
|
+{
|
|
|
+ struct ieee80211_mgd_auth_data *auth_data = sdata->u.mgd.auth_data;
|
|
|
+ u8 *pos;
|
|
|
+ struct ieee802_11_elems elems;
|
|
|
+
|
|
|
+ pos = mgmt->u.auth.variable;
|
|
|
+ ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
|
|
|
+ if (!elems.challenge)
|
|
|
+ return;
|
|
|
+ auth_data->expected_transaction = 4;
|
|
|
+ ieee80211_send_auth(sdata, 3, auth_data->algorithm,
|
|
|
+ elems.challenge - 2, elems.challenge_len + 2,
|
|
|
+ auth_data->bss->bssid, auth_data->bss->bssid,
|
|
|
+ auth_data->key, auth_data->key_len,
|
|
|
+ auth_data->key_idx);
|
|
|
+}
|
|
|
+
|
|
|
+static enum rx_mgmt_action __must_check
|
|
|
+ieee80211_rx_mgmt_auth(struct ieee80211_sub_if_data *sdata,
|
|
|
+ struct ieee80211_mgmt *mgmt, size_t len)
|
|
|
+{
|
|
|
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
+ u8 bssid[ETH_ALEN];
|
|
|
+ u16 auth_alg, auth_transaction, status_code;
|
|
|
+ struct sta_info *sta;
|
|
|
+
|
|
|
+ lockdep_assert_held(&ifmgd->mtx);
|
|
|
+
|
|
|
+ if (len < 24 + 6)
|
|
|
+ return RX_MGMT_NONE;
|
|
|
+
|
|
|
+ if (!ifmgd->auth_data || ifmgd->auth_data->done)
|
|
|
+ return RX_MGMT_NONE;
|
|
|
+
|
|
|
+ memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN);
|
|
|
+
|
|
|
+ if (memcmp(bssid, mgmt->bssid, ETH_ALEN))
|
|
|
+ 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_data->algorithm ||
|
|
|
+ auth_transaction != ifmgd->auth_data->expected_transaction)
|
|
|
+ return RX_MGMT_NONE;
|
|
|
+
|
|
|
+ if (status_code != WLAN_STATUS_SUCCESS) {
|
|
|
+ printk(KERN_DEBUG "%s: %pM denied authentication (status %d)\n",
|
|
|
+ sdata->name, mgmt->sa, status_code);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (ifmgd->auth_data->algorithm) {
|
|
|
+ case WLAN_AUTH_OPEN:
|
|
|
+ case WLAN_AUTH_LEAP:
|
|
|
+ case WLAN_AUTH_FT:
|
|
|
+ break;
|
|
|
+ case WLAN_AUTH_SHARED_KEY:
|
|
|
+ if (ifmgd->auth_data->expected_transaction != 4) {
|
|
|
+ ieee80211_auth_challenge(sdata, mgmt, len);
|
|
|
+ /* need another frame */
|
|
|
+ return RX_MGMT_NONE;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ WARN_ONCE(1, "invalid auth alg %d",
|
|
|
+ ifmgd->auth_data->algorithm);
|
|
|
+ return RX_MGMT_NONE;
|
|
|
+ }
|
|
|
+
|
|
|
+ printk(KERN_DEBUG "%s: authenticated\n", sdata->name);
|
|
|
+ out:
|
|
|
+ if (ifmgd->auth_data->synced)
|
|
|
+ drv_finish_tx_sync(sdata->local, sdata, bssid,
|
|
|
+ IEEE80211_TX_SYNC_AUTH);
|
|
|
+ ifmgd->auth_data->synced = false;
|
|
|
+ ifmgd->auth_data->done = true;
|
|
|
+ ifmgd->auth_data->timeout = jiffies + IEEE80211_AUTH_WAIT_ASSOC;
|
|
|
+ run_again(ifmgd, ifmgd->auth_data->timeout);
|
|
|
+
|
|
|
+ /* move station state to auth */
|
|
|
+ mutex_lock(&sdata->local->sta_mtx);
|
|
|
+ sta = sta_info_get(sdata, bssid);
|
|
|
+ if (!sta) {
|
|
|
+ WARN_ONCE(1, "%s: STA %pM not found", sdata->name, bssid);
|
|
|
+ goto out_err;
|
|
|
+ }
|
|
|
+ if (sta_info_move_state(sta, IEEE80211_STA_AUTH)) {
|
|
|
+ printk(KERN_DEBUG "%s: failed moving %pM to auth\n",
|
|
|
+ sdata->name, bssid);
|
|
|
+ goto out_err;
|
|
|
+ }
|
|
|
+ mutex_unlock(&sdata->local->sta_mtx);
|
|
|
+
|
|
|
+ return RX_MGMT_CFG80211_RX_AUTH;
|
|
|
+ out_err:
|
|
|
+ mutex_unlock(&sdata->local->sta_mtx);
|
|
|
+ /* ignore frame -- wait for timeout */
|
|
|
+ return RX_MGMT_NONE;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
static enum rx_mgmt_action __must_check
|
|
|
ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
|
|
|
struct ieee80211_mgmt *mgmt, size_t len)
|
|
|
@@ -1431,10 +1887,14 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
|
|
|
const u8 *bssid = NULL;
|
|
|
u16 reason_code;
|
|
|
|
|
|
+ lockdep_assert_held(&ifmgd->mtx);
|
|
|
+
|
|
|
if (len < 24 + 2)
|
|
|
return RX_MGMT_NONE;
|
|
|
|
|
|
- ASSERT_MGD_MTX(ifmgd);
|
|
|
+ if (!ifmgd->associated ||
|
|
|
+ memcmp(mgmt->bssid, ifmgd->associated->bssid, ETH_ALEN))
|
|
|
+ return RX_MGMT_NONE;
|
|
|
|
|
|
bssid = ifmgd->associated->bssid;
|
|
|
|
|
|
@@ -1459,15 +1919,13 @@ ieee80211_rx_mgmt_disassoc(struct ieee80211_sub_if_data *sdata,
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
u16 reason_code;
|
|
|
|
|
|
- if (len < 24 + 2)
|
|
|
- return RX_MGMT_NONE;
|
|
|
-
|
|
|
- ASSERT_MGD_MTX(ifmgd);
|
|
|
+ lockdep_assert_held(&ifmgd->mtx);
|
|
|
|
|
|
- if (WARN_ON(!ifmgd->associated))
|
|
|
+ if (len < 24 + 2)
|
|
|
return RX_MGMT_NONE;
|
|
|
|
|
|
- if (WARN_ON(memcmp(ifmgd->associated->bssid, mgmt->sa, ETH_ALEN)))
|
|
|
+ if (!ifmgd->associated ||
|
|
|
+ memcmp(mgmt->bssid, ifmgd->associated->bssid, ETH_ALEN))
|
|
|
return RX_MGMT_NONE;
|
|
|
|
|
|
reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code);
|
|
|
@@ -1524,15 +1982,37 @@ static void ieee80211_get_rates(struct ieee80211_supported_band *sband,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static bool ieee80211_assoc_success(struct ieee80211_work *wk,
|
|
|
+static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata,
|
|
|
+ bool assoc)
|
|
|
+{
|
|
|
+ struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
|
|
|
+
|
|
|
+ lockdep_assert_held(&sdata->u.mgd.mtx);
|
|
|
+
|
|
|
+ if (assoc_data->synced)
|
|
|
+ drv_finish_tx_sync(sdata->local, sdata,
|
|
|
+ assoc_data->bss->bssid,
|
|
|
+ IEEE80211_TX_SYNC_ASSOC);
|
|
|
+
|
|
|
+ if (!assoc) {
|
|
|
+ sta_info_destroy_addr(sdata, assoc_data->bss->bssid);
|
|
|
+
|
|
|
+ memset(sdata->u.mgd.bssid, 0, ETH_ALEN);
|
|
|
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
|
|
|
+ }
|
|
|
+
|
|
|
+ kfree(assoc_data);
|
|
|
+ sdata->u.mgd.assoc_data = NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
|
|
|
+ struct cfg80211_bss *cbss,
|
|
|
struct ieee80211_mgmt *mgmt, size_t len)
|
|
|
{
|
|
|
- struct ieee80211_sub_if_data *sdata = wk->sdata;
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
struct ieee80211_supported_band *sband;
|
|
|
struct sta_info *sta;
|
|
|
- struct cfg80211_bss *cbss = wk->assoc.bss;
|
|
|
u8 *pos;
|
|
|
u32 rates, basic_rates;
|
|
|
u16 capab_info, aid;
|
|
|
@@ -1589,7 +2069,7 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
|
|
|
|
|
|
rates = 0;
|
|
|
basic_rates = 0;
|
|
|
- sband = local->hw.wiphy->bands[wk->chan->band];
|
|
|
+ sband = local->hw.wiphy->bands[local->oper_channel->band];
|
|
|
|
|
|
ieee80211_get_rates(sband, elems.supp_rates, elems.supp_rates_len,
|
|
|
&rates, &basic_rates, &have_higher_than_11mbit,
|
|
|
@@ -1610,11 +2090,11 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
|
|
|
basic_rates = BIT(min_rate_index);
|
|
|
}
|
|
|
|
|
|
- sta->sta.supp_rates[wk->chan->band] = rates;
|
|
|
+ sta->sta.supp_rates[local->oper_channel->band] = rates;
|
|
|
sdata->vif.bss_conf.basic_rates = basic_rates;
|
|
|
|
|
|
/* cf. IEEE 802.11 9.2.12 */
|
|
|
- if (wk->chan->band == IEEE80211_BAND_2GHZ &&
|
|
|
+ if (local->oper_channel->band == IEEE80211_BAND_2GHZ &&
|
|
|
have_higher_than_11mbit)
|
|
|
sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE;
|
|
|
else
|
|
|
@@ -1664,8 +2144,6 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
|
|
|
else
|
|
|
ieee80211_set_wmm_default(sdata);
|
|
|
|
|
|
- local->oper_channel = wk->chan;
|
|
|
-
|
|
|
if (elems.ht_info_elem && elems.wmm_param &&
|
|
|
(sdata->local->hw.queues >= 4) &&
|
|
|
!(ifmgd->flags & IEEE80211_STA_DISABLE_11N))
|
|
|
@@ -1696,7 +2174,82 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk,
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+static enum rx_mgmt_action __must_check
|
|
|
+ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
|
|
|
+ struct ieee80211_mgmt *mgmt, size_t len,
|
|
|
+ struct cfg80211_bss **bss)
|
|
|
+{
|
|
|
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
+ struct ieee80211_mgd_assoc_data *assoc_data = ifmgd->assoc_data;
|
|
|
+ u16 capab_info, status_code, aid;
|
|
|
+ struct ieee802_11_elems elems;
|
|
|
+ u8 *pos;
|
|
|
+ bool reassoc;
|
|
|
+
|
|
|
+ lockdep_assert_held(&ifmgd->mtx);
|
|
|
+
|
|
|
+ if (!assoc_data)
|
|
|
+ return RX_MGMT_NONE;
|
|
|
+ if (memcmp(assoc_data->bss->bssid, mgmt->bssid, ETH_ALEN))
|
|
|
+ return RX_MGMT_NONE;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * AssocResp and ReassocResp have identical structure, so process both
|
|
|
+ * of them in this function.
|
|
|
+ */
|
|
|
+
|
|
|
+ if (len < 24 + 6)
|
|
|
+ return RX_MGMT_NONE;
|
|
|
+
|
|
|
+ reassoc = ieee80211_is_reassoc_req(mgmt->frame_control);
|
|
|
+ capab_info = le16_to_cpu(mgmt->u.assoc_resp.capab_info);
|
|
|
+ status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code);
|
|
|
+ aid = le16_to_cpu(mgmt->u.assoc_resp.aid);
|
|
|
+
|
|
|
+ printk(KERN_DEBUG "%s: RX %sssocResp from %pM (capab=0x%x "
|
|
|
+ "status=%d aid=%d)\n",
|
|
|
+ sdata->name, reassoc ? "Rea" : "A", mgmt->sa,
|
|
|
+ capab_info, status_code, (u16)(aid & ~(BIT(15) | BIT(14))));
|
|
|
+
|
|
|
+ pos = mgmt->u.assoc_resp.variable;
|
|
|
+ ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems);
|
|
|
+
|
|
|
+ if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
|
|
|
+ elems.timeout_int && elems.timeout_int_len == 5 &&
|
|
|
+ elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) {
|
|
|
+ u32 tu, ms;
|
|
|
+ tu = get_unaligned_le32(elems.timeout_int + 1);
|
|
|
+ ms = tu * 1024 / 1000;
|
|
|
+ printk(KERN_DEBUG "%s: %pM rejected association temporarily; "
|
|
|
+ "comeback duration %u TU (%u ms)\n",
|
|
|
+ sdata->name, mgmt->sa, tu, ms);
|
|
|
+ assoc_data->timeout = jiffies + msecs_to_jiffies(ms);
|
|
|
+ if (ms > IEEE80211_ASSOC_TIMEOUT)
|
|
|
+ run_again(ifmgd, assoc_data->timeout);
|
|
|
+ return RX_MGMT_NONE;
|
|
|
+ }
|
|
|
+
|
|
|
+ *bss = assoc_data->bss;
|
|
|
+
|
|
|
+ if (status_code != WLAN_STATUS_SUCCESS) {
|
|
|
+ printk(KERN_DEBUG "%s: %pM denied association (code=%d)\n",
|
|
|
+ sdata->name, mgmt->sa, status_code);
|
|
|
+ ieee80211_destroy_assoc_data(sdata, false);
|
|
|
+ } else {
|
|
|
+ printk(KERN_DEBUG "%s: associated\n", sdata->name);
|
|
|
+
|
|
|
+ ieee80211_destroy_assoc_data(sdata, true);
|
|
|
|
|
|
+ if (!ieee80211_assoc_success(sdata, *bss, mgmt, len)) {
|
|
|
+ /* oops -- internal error -- send timeout for now */
|
|
|
+ sta_info_destroy_addr(sdata, mgmt->bssid);
|
|
|
+ cfg80211_put_bss(*bss);
|
|
|
+ return RX_MGMT_CFG80211_ASSOC_TIMEOUT;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return RX_MGMT_CFG80211_RX_ASSOC;
|
|
|
+}
|
|
|
static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
|
|
|
struct ieee80211_mgmt *mgmt,
|
|
|
size_t len,
|
|
|
@@ -1710,7 +2263,9 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata,
|
|
|
struct ieee80211_channel *channel;
|
|
|
bool need_ps = false;
|
|
|
|
|
|
- if (sdata->u.mgd.associated) {
|
|
|
+ if (sdata->u.mgd.associated &&
|
|
|
+ memcmp(mgmt->bssid, sdata->u.mgd.associated->bssid,
|
|
|
+ ETH_ALEN) == 0) {
|
|
|
bss = (void *)sdata->u.mgd.associated->priv;
|
|
|
/* not previously set so we may need to recalc */
|
|
|
need_ps = !bss->dtim_period;
|
|
|
@@ -1780,6 +2335,15 @@ static void ieee80211_rx_mgmt_probe_resp(struct ieee80211_sub_if_data *sdata,
|
|
|
if (ifmgd->associated &&
|
|
|
memcmp(mgmt->bssid, ifmgd->associated->bssid, ETH_ALEN) == 0)
|
|
|
ieee80211_reset_ap_probe(sdata);
|
|
|
+
|
|
|
+ if (ifmgd->auth_data && !ifmgd->auth_data->bss->proberesp_ies &&
|
|
|
+ memcmp(mgmt->bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN) == 0) {
|
|
|
+ /* got probe response, continue with auth */
|
|
|
+ printk(KERN_DEBUG "%s: direct probe responded\n", sdata->name);
|
|
|
+ ifmgd->auth_data->tries = 0;
|
|
|
+ ifmgd->auth_data->timeout = jiffies;
|
|
|
+ run_again(ifmgd, ifmgd->auth_data->timeout);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
@@ -1819,7 +2383,7 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
u32 ncrc;
|
|
|
u8 *bssid;
|
|
|
|
|
|
- ASSERT_MGD_MTX(ifmgd);
|
|
|
+ lockdep_assert_held(&ifmgd->mtx);
|
|
|
|
|
|
/* Process beacon from the current BSS */
|
|
|
baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
|
|
|
@@ -1829,21 +2393,25 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
|
|
|
if (rx_status->freq != local->hw.conf.channel->center_freq)
|
|
|
return;
|
|
|
|
|
|
- /*
|
|
|
- * We might have received a number of frames, among them a
|
|
|
- * disassoc frame and a beacon...
|
|
|
- */
|
|
|
- if (!ifmgd->associated)
|
|
|
- return;
|
|
|
+ if (ifmgd->assoc_data && !ifmgd->assoc_data->have_beacon &&
|
|
|
+ memcmp(mgmt->bssid, ifmgd->assoc_data->bss->bssid, ETH_ALEN) == 0) {
|
|
|
+ ieee802_11_parse_elems(mgmt->u.beacon.variable,
|
|
|
+ len - baselen, &elems);
|
|
|
|
|
|
- bssid = ifmgd->associated->bssid;
|
|
|
+ ieee80211_rx_bss_info(sdata, mgmt, len, rx_status, &elems,
|
|
|
+ false);
|
|
|
+ ifmgd->assoc_data->have_beacon = true;
|
|
|
+ ifmgd->assoc_data->sent_assoc = false;
|
|
|
+ /* continue assoc process */
|
|
|
+ ifmgd->assoc_data->timeout = jiffies;
|
|
|
+ run_again(ifmgd, ifmgd->assoc_data->timeout);
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- /*
|
|
|
- * And in theory even frames from a different AP we were just
|
|
|
- * associated to a split-second ago!
|
|
|
- */
|
|
|
- if (memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0)
|
|
|
+ if (!ifmgd->associated ||
|
|
|
+ memcmp(mgmt->bssid, ifmgd->associated->bssid, ETH_ALEN))
|
|
|
return;
|
|
|
+ bssid = ifmgd->associated->bssid;
|
|
|
|
|
|
/* Track average RSSI from the Beacon frames of the current AP */
|
|
|
ifmgd->last_beacon_signal = rx_status->signal;
|
|
|
@@ -2027,6 +2595,7 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
struct ieee80211_rx_status *rx_status;
|
|
|
struct ieee80211_mgmt *mgmt;
|
|
|
+ struct cfg80211_bss *bss = NULL;
|
|
|
enum rx_mgmt_action rma = RX_MGMT_NONE;
|
|
|
u16 fc;
|
|
|
|
|
|
@@ -2036,92 +2605,59 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
|
|
|
|
|
|
mutex_lock(&ifmgd->mtx);
|
|
|
|
|
|
- if (ifmgd->associated &&
|
|
|
- memcmp(ifmgd->associated->bssid, mgmt->bssid, ETH_ALEN) == 0) {
|
|
|
- switch (fc & IEEE80211_FCTL_STYPE) {
|
|
|
- case IEEE80211_STYPE_BEACON:
|
|
|
- ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len,
|
|
|
- rx_status);
|
|
|
- break;
|
|
|
- case IEEE80211_STYPE_PROBE_RESP:
|
|
|
- ieee80211_rx_mgmt_probe_resp(sdata, skb);
|
|
|
- break;
|
|
|
- case IEEE80211_STYPE_DEAUTH:
|
|
|
- rma = ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
|
|
|
- break;
|
|
|
- case IEEE80211_STYPE_DISASSOC:
|
|
|
- rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
|
|
|
- break;
|
|
|
- case IEEE80211_STYPE_ACTION:
|
|
|
- switch (mgmt->u.action.category) {
|
|
|
- case WLAN_CATEGORY_SPECTRUM_MGMT:
|
|
|
- ieee80211_sta_process_chanswitch(sdata,
|
|
|
- &mgmt->u.action.u.chan_switch.sw_elem,
|
|
|
- (void *)ifmgd->associated->priv,
|
|
|
- rx_status->mactime);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- mutex_unlock(&ifmgd->mtx);
|
|
|
-
|
|
|
- switch (rma) {
|
|
|
- case RX_MGMT_NONE:
|
|
|
- /* no action */
|
|
|
- break;
|
|
|
- case RX_MGMT_CFG80211_DEAUTH:
|
|
|
- cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
|
|
|
- break;
|
|
|
- case RX_MGMT_CFG80211_DISASSOC:
|
|
|
- cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len);
|
|
|
+ switch (fc & IEEE80211_FCTL_STYPE) {
|
|
|
+ case IEEE80211_STYPE_BEACON:
|
|
|
+ ieee80211_rx_mgmt_beacon(sdata, mgmt, skb->len, rx_status);
|
|
|
+ break;
|
|
|
+ case IEEE80211_STYPE_PROBE_RESP:
|
|
|
+ ieee80211_rx_mgmt_probe_resp(sdata, skb);
|
|
|
+ break;
|
|
|
+ case IEEE80211_STYPE_AUTH:
|
|
|
+ rma = ieee80211_rx_mgmt_auth(sdata, mgmt, skb->len);
|
|
|
+ break;
|
|
|
+ case IEEE80211_STYPE_DEAUTH:
|
|
|
+ rma = ieee80211_rx_mgmt_deauth(sdata, mgmt, skb->len);
|
|
|
+ break;
|
|
|
+ case IEEE80211_STYPE_DISASSOC:
|
|
|
+ rma = ieee80211_rx_mgmt_disassoc(sdata, mgmt, skb->len);
|
|
|
+ break;
|
|
|
+ case IEEE80211_STYPE_ASSOC_RESP:
|
|
|
+ case IEEE80211_STYPE_REASSOC_RESP:
|
|
|
+ rma = ieee80211_rx_mgmt_assoc_resp(sdata, mgmt, skb->len, &bss);
|
|
|
+ break;
|
|
|
+ case IEEE80211_STYPE_ACTION:
|
|
|
+ switch (mgmt->u.action.category) {
|
|
|
+ case WLAN_CATEGORY_SPECTRUM_MGMT:
|
|
|
+ ieee80211_sta_process_chanswitch(sdata,
|
|
|
+ &mgmt->u.action.u.chan_switch.sw_elem,
|
|
|
+ (void *)ifmgd->associated->priv,
|
|
|
+ rx_status->mactime);
|
|
|
break;
|
|
|
- default:
|
|
|
- WARN(1, "unexpected: %d", rma);
|
|
|
}
|
|
|
- return;
|
|
|
}
|
|
|
-
|
|
|
mutex_unlock(&ifmgd->mtx);
|
|
|
|
|
|
- if (skb->len >= 24 + 2 /* mgmt + deauth reason */ &&
|
|
|
- (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_DEAUTH) {
|
|
|
- struct ieee80211_local *local = sdata->local;
|
|
|
- struct ieee80211_work *wk;
|
|
|
-
|
|
|
- mutex_lock(&local->mtx);
|
|
|
- list_for_each_entry(wk, &local->work_list, list) {
|
|
|
- if (wk->sdata != sdata)
|
|
|
- continue;
|
|
|
-
|
|
|
- if (wk->type != IEEE80211_WORK_ASSOC &&
|
|
|
- wk->type != IEEE80211_WORK_ASSOC_BEACON_WAIT)
|
|
|
- continue;
|
|
|
-
|
|
|
- if (memcmp(mgmt->bssid, wk->filter_ta, ETH_ALEN))
|
|
|
- continue;
|
|
|
- if (memcmp(mgmt->sa, wk->filter_ta, ETH_ALEN))
|
|
|
- continue;
|
|
|
-
|
|
|
- /*
|
|
|
- * Printing the message only here means we can't
|
|
|
- * spuriously print it, but it also means that it
|
|
|
- * won't be printed when the frame comes in before
|
|
|
- * we even tried to associate or in similar cases.
|
|
|
- *
|
|
|
- * Ultimately, I suspect cfg80211 should print the
|
|
|
- * messages instead.
|
|
|
- */
|
|
|
- printk(KERN_DEBUG
|
|
|
- "%s: deauthenticated from %pM (Reason: %u)\n",
|
|
|
- sdata->name, mgmt->bssid,
|
|
|
- le16_to_cpu(mgmt->u.deauth.reason_code));
|
|
|
-
|
|
|
- list_del_rcu(&wk->list);
|
|
|
- free_work(wk);
|
|
|
- break;
|
|
|
- }
|
|
|
- mutex_unlock(&local->mtx);
|
|
|
-
|
|
|
+ switch (rma) {
|
|
|
+ case RX_MGMT_NONE:
|
|
|
+ /* no action */
|
|
|
+ break;
|
|
|
+ case RX_MGMT_CFG80211_DEAUTH:
|
|
|
cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
|
|
|
+ break;
|
|
|
+ case RX_MGMT_CFG80211_DISASSOC:
|
|
|
+ cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len);
|
|
|
+ break;
|
|
|
+ case RX_MGMT_CFG80211_RX_AUTH:
|
|
|
+ cfg80211_send_rx_auth(sdata->dev, (u8 *)mgmt, skb->len);
|
|
|
+ break;
|
|
|
+ case RX_MGMT_CFG80211_RX_ASSOC:
|
|
|
+ cfg80211_send_rx_assoc(sdata->dev, bss, (u8 *)mgmt, skb->len);
|
|
|
+ break;
|
|
|
+ case RX_MGMT_CFG80211_ASSOC_TIMEOUT:
|
|
|
+ cfg80211_send_assoc_timeout(sdata->dev, mgmt->bssid);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ WARN(1, "unexpected: %d", rma);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -2166,14 +2702,160 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
|
|
|
mutex_lock(&ifmgd->mtx);
|
|
|
}
|
|
|
|
|
|
+static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
|
|
|
+{
|
|
|
+ struct ieee80211_local *local = sdata->local;
|
|
|
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
+ struct ieee80211_mgd_auth_data *auth_data = ifmgd->auth_data;
|
|
|
+
|
|
|
+ lockdep_assert_held(&ifmgd->mtx);
|
|
|
+
|
|
|
+ if (WARN_ON_ONCE(!auth_data))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (!auth_data->synced) {
|
|
|
+ int ret = drv_tx_sync(local, sdata, auth_data->bss->bssid,
|
|
|
+ IEEE80211_TX_SYNC_AUTH);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ auth_data->synced = true;
|
|
|
+
|
|
|
+ auth_data->tries++;
|
|
|
+
|
|
|
+ if (auth_data->tries > IEEE80211_AUTH_MAX_TRIES) {
|
|
|
+ printk(KERN_DEBUG "%s: authentication with %pM timed out\n",
|
|
|
+ sdata->name, auth_data->bss->bssid);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Most likely AP is not in the range so remove the
|
|
|
+ * bss struct for that AP.
|
|
|
+ */
|
|
|
+ cfg80211_unlink_bss(local->hw.wiphy, auth_data->bss);
|
|
|
+
|
|
|
+ return -ETIMEDOUT;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (auth_data->bss->proberesp_ies) {
|
|
|
+ printk(KERN_DEBUG "%s: send auth to %pM (try %d/%d)\n",
|
|
|
+ sdata->name, auth_data->bss->bssid, auth_data->tries,
|
|
|
+ IEEE80211_AUTH_MAX_TRIES);
|
|
|
+
|
|
|
+ auth_data->expected_transaction = 2;
|
|
|
+ ieee80211_send_auth(sdata, 1, auth_data->algorithm,
|
|
|
+ auth_data->ie, auth_data->ie_len,
|
|
|
+ auth_data->bss->bssid,
|
|
|
+ auth_data->bss->bssid, NULL, 0, 0);
|
|
|
+ } else {
|
|
|
+ const u8 *ssidie;
|
|
|
+
|
|
|
+ printk(KERN_DEBUG "%s: direct probe to %pM (try %d/%i)\n",
|
|
|
+ sdata->name, auth_data->bss->bssid, auth_data->tries,
|
|
|
+ IEEE80211_AUTH_MAX_TRIES);
|
|
|
+
|
|
|
+ ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID);
|
|
|
+ if (!ssidie)
|
|
|
+ return -EINVAL;
|
|
|
+ /*
|
|
|
+ * 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, ssidie + 2, ssidie[1],
|
|
|
+ NULL, 0, (u32) -1, true, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
|
|
|
+ run_again(ifmgd, auth_data->timeout);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int ieee80211_do_assoc(struct ieee80211_sub_if_data *sdata)
|
|
|
+{
|
|
|
+ struct ieee80211_mgd_assoc_data *assoc_data = sdata->u.mgd.assoc_data;
|
|
|
+ struct ieee80211_local *local = sdata->local;
|
|
|
+
|
|
|
+ lockdep_assert_held(&sdata->u.mgd.mtx);
|
|
|
+
|
|
|
+ if (!assoc_data->synced) {
|
|
|
+ int ret = drv_tx_sync(local, sdata, assoc_data->bss->bssid,
|
|
|
+ IEEE80211_TX_SYNC_ASSOC);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+ assoc_data->synced = true;
|
|
|
+
|
|
|
+ assoc_data->tries++;
|
|
|
+ if (assoc_data->tries > IEEE80211_ASSOC_MAX_TRIES) {
|
|
|
+ printk(KERN_DEBUG "%s: association with %pM timed out\n",
|
|
|
+ sdata->name, assoc_data->bss->bssid);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Most likely AP is not in the range so remove the
|
|
|
+ * bss struct for that AP.
|
|
|
+ */
|
|
|
+ cfg80211_unlink_bss(local->hw.wiphy, assoc_data->bss);
|
|
|
+
|
|
|
+ return -ETIMEDOUT;
|
|
|
+ }
|
|
|
+
|
|
|
+ printk(KERN_DEBUG "%s: associate with %pM (try %d/%d)\n",
|
|
|
+ sdata->name, assoc_data->bss->bssid, assoc_data->tries,
|
|
|
+ IEEE80211_ASSOC_MAX_TRIES);
|
|
|
+ ieee80211_send_assoc(sdata);
|
|
|
+
|
|
|
+ assoc_data->timeout = jiffies + IEEE80211_ASSOC_TIMEOUT;
|
|
|
+ run_again(&sdata->u.mgd, assoc_data->timeout);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
|
|
|
{
|
|
|
struct ieee80211_local *local = sdata->local;
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
|
|
|
- /* then process the rest of the work */
|
|
|
mutex_lock(&ifmgd->mtx);
|
|
|
|
|
|
+ if (ifmgd->auth_data &&
|
|
|
+ time_after(jiffies, ifmgd->auth_data->timeout)) {
|
|
|
+ if (ifmgd->auth_data->done) {
|
|
|
+ /*
|
|
|
+ * ok ... we waited for assoc but userspace didn't,
|
|
|
+ * so let's just kill the auth data
|
|
|
+ */
|
|
|
+ ieee80211_destroy_auth_data(sdata, false);
|
|
|
+ } else if (ieee80211_probe_auth(sdata)) {
|
|
|
+ u8 bssid[ETH_ALEN];
|
|
|
+
|
|
|
+ memcpy(bssid, ifmgd->auth_data->bss->bssid, ETH_ALEN);
|
|
|
+
|
|
|
+ ieee80211_destroy_auth_data(sdata, false);
|
|
|
+
|
|
|
+ mutex_unlock(&ifmgd->mtx);
|
|
|
+ cfg80211_send_auth_timeout(sdata->dev, bssid);
|
|
|
+ mutex_lock(&ifmgd->mtx);
|
|
|
+ }
|
|
|
+ } else if (ifmgd->auth_data)
|
|
|
+ run_again(ifmgd, ifmgd->auth_data->timeout);
|
|
|
+
|
|
|
+ if (ifmgd->assoc_data &&
|
|
|
+ time_after(jiffies, ifmgd->assoc_data->timeout)) {
|
|
|
+ if (!ifmgd->assoc_data->have_beacon ||
|
|
|
+ ieee80211_do_assoc(sdata)) {
|
|
|
+ u8 bssid[ETH_ALEN];
|
|
|
+
|
|
|
+ memcpy(bssid, ifmgd->assoc_data->bss->bssid, ETH_ALEN);
|
|
|
+
|
|
|
+ ieee80211_destroy_assoc_data(sdata, false);
|
|
|
+
|
|
|
+ mutex_unlock(&ifmgd->mtx);
|
|
|
+ cfg80211_send_assoc_timeout(sdata->dev, bssid);
|
|
|
+ mutex_lock(&ifmgd->mtx);
|
|
|
+ }
|
|
|
+ } else if (ifmgd->assoc_data)
|
|
|
+ run_again(ifmgd, ifmgd->assoc_data->timeout);
|
|
|
+
|
|
|
if (ifmgd->flags & (IEEE80211_STA_BEACON_POLL |
|
|
|
IEEE80211_STA_CONNECTION_POLL) &&
|
|
|
ifmgd->associated) {
|
|
|
@@ -2249,6 +2931,10 @@ void ieee80211_sta_work(struct ieee80211_sub_if_data *sdata)
|
|
|
}
|
|
|
|
|
|
mutex_unlock(&ifmgd->mtx);
|
|
|
+
|
|
|
+ mutex_lock(&local->mtx);
|
|
|
+ ieee80211_recalc_idle(local);
|
|
|
+ mutex_unlock(&local->mtx);
|
|
|
}
|
|
|
|
|
|
static void ieee80211_sta_bcn_mon_timer(unsigned long data)
|
|
|
@@ -2421,50 +3107,24 @@ int ieee80211_max_network_latency(struct notifier_block *nb,
|
|
|
}
|
|
|
|
|
|
/* config hooks */
|
|
|
-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);
|
|
|
- goto destroy;
|
|
|
- }
|
|
|
-
|
|
|
- if (wk->type == IEEE80211_WORK_AUTH) {
|
|
|
- cfg80211_send_rx_auth(wk->sdata->dev, skb->data, skb->len);
|
|
|
- goto destroy;
|
|
|
- }
|
|
|
-
|
|
|
- mutex_lock(&wk->sdata->u.mgd.mtx);
|
|
|
- ieee80211_rx_mgmt_probe_resp(wk->sdata, skb);
|
|
|
- mutex_unlock(&wk->sdata->u.mgd.mtx);
|
|
|
-
|
|
|
- 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,
|
|
|
struct cfg80211_auth_request *req)
|
|
|
{
|
|
|
- const u8 *ssid;
|
|
|
- struct ieee80211_work *wk;
|
|
|
+ struct ieee80211_local *local = sdata->local;
|
|
|
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
+ struct ieee80211_mgd_auth_data *auth_data;
|
|
|
+ struct sta_info *sta;
|
|
|
u16 auth_alg;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ /* prepare auth data structure */
|
|
|
|
|
|
switch (req->auth_type) {
|
|
|
case NL80211_AUTHTYPE_OPEN_SYSTEM:
|
|
|
auth_alg = WLAN_AUTH_OPEN;
|
|
|
break;
|
|
|
case NL80211_AUTHTYPE_SHARED_KEY:
|
|
|
- if (IS_ERR(sdata->local->wep_tx_tfm))
|
|
|
+ if (IS_ERR(local->wep_tx_tfm))
|
|
|
return -EOPNOTSUPP;
|
|
|
auth_alg = WLAN_AUTH_SHARED_KEY;
|
|
|
break;
|
|
|
@@ -2478,169 +3138,142 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
|
|
|
return -EOPNOTSUPP;
|
|
|
}
|
|
|
|
|
|
- wk = kzalloc(sizeof(*wk) + req->ie_len, GFP_KERNEL);
|
|
|
- if (!wk)
|
|
|
+ auth_data = kzalloc(sizeof(*auth_data) + req->ie_len, GFP_KERNEL);
|
|
|
+ if (!auth_data)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN);
|
|
|
+ auth_data->bss = req->bss;
|
|
|
|
|
|
if (req->ie && req->ie_len) {
|
|
|
- memcpy(wk->ie, req->ie, req->ie_len);
|
|
|
- wk->ie_len = req->ie_len;
|
|
|
+ memcpy(auth_data->ie, req->ie, req->ie_len);
|
|
|
+ auth_data->ie_len = req->ie_len;
|
|
|
}
|
|
|
|
|
|
if (req->key && req->key_len) {
|
|
|
- wk->probe_auth.key_len = req->key_len;
|
|
|
- wk->probe_auth.key_idx = req->key_idx;
|
|
|
- memcpy(wk->probe_auth.key, req->key, req->key_len);
|
|
|
+ auth_data->key_len = req->key_len;
|
|
|
+ auth_data->key_idx = req->key_idx;
|
|
|
+ memcpy(auth_data->key, req->key, req->key_len);
|
|
|
}
|
|
|
|
|
|
- ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
|
|
|
- memcpy(wk->probe_auth.ssid, ssid + 2, ssid[1]);
|
|
|
- wk->probe_auth.ssid_len = ssid[1];
|
|
|
-
|
|
|
- wk->probe_auth.algorithm = auth_alg;
|
|
|
- wk->probe_auth.privacy = req->bss->capability & WLAN_CAPABILITY_PRIVACY;
|
|
|
-
|
|
|
- /* if we already have a probe, don't probe again */
|
|
|
- if (req->bss->proberesp_ies)
|
|
|
- wk->type = IEEE80211_WORK_AUTH;
|
|
|
- else
|
|
|
- wk->type = IEEE80211_WORK_DIRECT_PROBE;
|
|
|
- wk->chan = req->bss->channel;
|
|
|
- wk->chan_type = NL80211_CHAN_NO_HT;
|
|
|
- wk->sdata = sdata;
|
|
|
- wk->done = ieee80211_probe_auth_done;
|
|
|
-
|
|
|
- ieee80211_add_work(wk);
|
|
|
- return 0;
|
|
|
-}
|
|
|
+ auth_data->algorithm = auth_alg;
|
|
|
|
|
|
-/* 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;
|
|
|
+ /* try to authenticate/probe */
|
|
|
|
|
|
- sta = sta_info_alloc(sdata, bssid, GFP_KERNEL);
|
|
|
- if (!sta)
|
|
|
- return -ENOMEM;
|
|
|
+ mutex_lock(&ifmgd->mtx);
|
|
|
|
|
|
- err = sta_info_insert(sta);
|
|
|
- sta = NULL;
|
|
|
- if (err) {
|
|
|
- printk(KERN_DEBUG "%s: failed to insert STA entry for"
|
|
|
- " the AP (error %d)\n", sdata->name, err);
|
|
|
- return err;
|
|
|
+ if ((ifmgd->auth_data && !ifmgd->auth_data->done) ||
|
|
|
+ ifmgd->assoc_data) {
|
|
|
+ err = -EBUSY;
|
|
|
+ goto err_free;
|
|
|
}
|
|
|
|
|
|
- return 0;
|
|
|
-}
|
|
|
+ if (ifmgd->auth_data)
|
|
|
+ ieee80211_destroy_auth_data(sdata, false);
|
|
|
|
|
|
-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;
|
|
|
+ /* prep auth_data so we don't go into idle on disassoc */
|
|
|
+ ifmgd->auth_data = auth_data;
|
|
|
|
|
|
- if (!skb) {
|
|
|
- sta_info_destroy_addr(wk->sdata, cbss->bssid);
|
|
|
- cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta);
|
|
|
- goto destroy;
|
|
|
- }
|
|
|
+ if (ifmgd->associated)
|
|
|
+ ieee80211_set_disassoc(sdata, true, false);
|
|
|
|
|
|
- if (wk->type == IEEE80211_WORK_ASSOC_BEACON_WAIT) {
|
|
|
- mutex_lock(&wk->sdata->u.mgd.mtx);
|
|
|
- rx_status = (void *) skb->cb;
|
|
|
- ieee802_11_parse_elems(skb->data + 24 + 12, skb->len - 24 - 12, &elems);
|
|
|
- ieee80211_rx_bss_info(wk->sdata, (void *)skb->data, skb->len, rx_status,
|
|
|
- &elems, true);
|
|
|
- mutex_unlock(&wk->sdata->u.mgd.mtx);
|
|
|
+ printk(KERN_DEBUG "%s: authenticate with %pM\n",
|
|
|
+ sdata->name, req->bss->bssid);
|
|
|
|
|
|
- wk->type = IEEE80211_WORK_ASSOC;
|
|
|
- /* not really done yet */
|
|
|
- return WORK_DONE_REQUEUE;
|
|
|
- }
|
|
|
+ mutex_lock(&local->mtx);
|
|
|
+ ieee80211_recalc_idle(sdata->local);
|
|
|
+ mutex_unlock(&local->mtx);
|
|
|
|
|
|
- mgmt = (void *)skb->data;
|
|
|
- status = le16_to_cpu(mgmt->u.assoc_resp.status_code);
|
|
|
+ /* switch to the right channel */
|
|
|
+ local->oper_channel = req->bss->channel;
|
|
|
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
|
|
|
|
|
|
- if (status == WLAN_STATUS_SUCCESS) {
|
|
|
- if (wk->assoc.synced)
|
|
|
- drv_finish_tx_sync(local, wk->sdata, wk->filter_ta,
|
|
|
- IEEE80211_TX_SYNC_ASSOC);
|
|
|
+ /* set BSSID */
|
|
|
+ memcpy(ifmgd->bssid, req->bss->bssid, ETH_ALEN);
|
|
|
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
|
|
|
|
|
|
- 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;
|
|
|
- }
|
|
|
+ /* add station entry */
|
|
|
+ sta = sta_info_alloc(sdata, req->bss->bssid, GFP_KERNEL);
|
|
|
+ if (!sta) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto err_clear;
|
|
|
+ }
|
|
|
|
|
|
- mutex_unlock(&wk->sdata->u.mgd.mtx);
|
|
|
- } else {
|
|
|
- /* assoc failed - destroy the dummy station entry */
|
|
|
- sta_info_destroy_addr(wk->sdata, cbss->bssid);
|
|
|
+ err = sta_info_insert(sta);
|
|
|
+ if (err) {
|
|
|
+ printk(KERN_DEBUG
|
|
|
+ "%s: failed to insert STA entry for the AP %pM (error %d)\n",
|
|
|
+ sdata->name, req->bss->bssid, err);
|
|
|
+ goto err_clear;
|
|
|
}
|
|
|
|
|
|
- cfg80211_send_rx_assoc(wk->sdata->dev, cbss, skb->data, skb->len);
|
|
|
- destroy:
|
|
|
- if (wk->assoc.synced)
|
|
|
- drv_finish_tx_sync(local, wk->sdata, wk->filter_ta,
|
|
|
- IEEE80211_TX_SYNC_ASSOC);
|
|
|
+ err = ieee80211_probe_auth(sdata);
|
|
|
+ if (err) {
|
|
|
+ if (auth_data->synced)
|
|
|
+ drv_finish_tx_sync(local, sdata, req->bss->bssid,
|
|
|
+ IEEE80211_TX_SYNC_AUTH);
|
|
|
+ sta_info_destroy_addr(sdata, req->bss->bssid);
|
|
|
+ goto err_clear;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* hold our own reference */
|
|
|
+ cfg80211_ref_bss(auth_data->bss);
|
|
|
+ err = 0;
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+ err_clear:
|
|
|
+ ifmgd->auth_data = NULL;
|
|
|
+ err_free:
|
|
|
+ kfree(auth_data);
|
|
|
+ out_unlock:
|
|
|
+ mutex_unlock(&ifmgd->mtx);
|
|
|
|
|
|
- return WORK_DONE_DESTROY;
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
|
|
|
struct cfg80211_assoc_request *req)
|
|
|
{
|
|
|
+ struct ieee80211_local *local = sdata->local;
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
struct ieee80211_bss *bss = (void *)req->bss->priv;
|
|
|
- struct ieee80211_work *wk;
|
|
|
- const u8 *ssid;
|
|
|
+ struct ieee80211_mgd_assoc_data *assoc_data;
|
|
|
+ struct sta_info *sta;
|
|
|
+ const u8 *ssidie;
|
|
|
int i, err;
|
|
|
|
|
|
+ ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
|
|
|
+ if (!ssidie)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL);
|
|
|
+ if (!assoc_data)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
mutex_lock(&ifmgd->mtx);
|
|
|
- if (ifmgd->associated) {
|
|
|
- if (!req->prev_bssid ||
|
|
|
- memcmp(req->prev_bssid, ifmgd->associated->bssid,
|
|
|
- ETH_ALEN)) {
|
|
|
- /*
|
|
|
- * We are already associated and the request was not a
|
|
|
- * reassociation request from the current BSS, so
|
|
|
- * reject it.
|
|
|
- */
|
|
|
- mutex_unlock(&ifmgd->mtx);
|
|
|
- return -EALREADY;
|
|
|
- }
|
|
|
|
|
|
- /* Trying to reassociate - clear previous association state */
|
|
|
+ if (ifmgd->associated)
|
|
|
ieee80211_set_disassoc(sdata, true, false);
|
|
|
+
|
|
|
+ if (ifmgd->auth_data && !ifmgd->auth_data->done) {
|
|
|
+ err = -EBUSY;
|
|
|
+ goto err_free;
|
|
|
}
|
|
|
- mutex_unlock(&ifmgd->mtx);
|
|
|
|
|
|
- wk = kzalloc(sizeof(*wk) + req->ie_len, GFP_KERNEL);
|
|
|
- if (!wk)
|
|
|
- return -ENOMEM;
|
|
|
+ if (ifmgd->assoc_data) {
|
|
|
+ err = -EBUSY;
|
|
|
+ goto err_free;
|
|
|
+ }
|
|
|
|
|
|
- /*
|
|
|
- * 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;
|
|
|
+ if (ifmgd->auth_data) {
|
|
|
+ bool match;
|
|
|
+
|
|
|
+ /* keep sta info, bssid if matching */
|
|
|
+ match = memcmp(ifmgd->bssid, req->bss->bssid, ETH_ALEN) == 0;
|
|
|
+ ieee80211_destroy_auth_data(sdata, match);
|
|
|
}
|
|
|
|
|
|
+ /* prepare assoc data */
|
|
|
+
|
|
|
ifmgd->flags &= ~IEEE80211_STA_DISABLE_11N;
|
|
|
ifmgd->flags &= ~IEEE80211_STA_NULLFUNC_ACKED;
|
|
|
|
|
|
@@ -2652,7 +3285,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;
|
|
|
|
|
|
-
|
|
|
if (req->flags & ASSOC_REQ_DISABLE_HT)
|
|
|
ifmgd->flags |= IEEE80211_STA_DISABLE_11N;
|
|
|
|
|
|
@@ -2661,16 +3293,12 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
|
|
|
sizeof(ifmgd->ht_capa_mask));
|
|
|
|
|
|
if (req->ie && req->ie_len) {
|
|
|
- memcpy(wk->ie, req->ie, req->ie_len);
|
|
|
- wk->ie_len = req->ie_len;
|
|
|
- } else
|
|
|
- wk->ie_len = 0;
|
|
|
-
|
|
|
- wk->assoc.bss = req->bss;
|
|
|
+ memcpy(assoc_data->ie, req->ie, req->ie_len);
|
|
|
+ assoc_data->ie_len = req->ie_len;
|
|
|
+ }
|
|
|
|
|
|
- memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN);
|
|
|
+ assoc_data->bss = req->bss;
|
|
|
|
|
|
- /* new association always uses requested smps mode */
|
|
|
if (ifmgd->req_smps == IEEE80211_SMPS_AUTOMATIC) {
|
|
|
if (ifmgd->powersave)
|
|
|
ifmgd->ap_smps = IEEE80211_SMPS_DYNAMIC;
|
|
|
@@ -2679,7 +3307,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
|
|
|
} else
|
|
|
ifmgd->ap_smps = ifmgd->req_smps;
|
|
|
|
|
|
- wk->assoc.smps = ifmgd->ap_smps;
|
|
|
/*
|
|
|
* IEEE802.11n does not allow TKIP/WEP as pairwise ciphers in HT mode.
|
|
|
* We still associate in non-HT mode (11a/b/g) if any one of these
|
|
|
@@ -2687,39 +3314,27 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
|
|
|
* We can set this to true for non-11n hardware, that'll be checked
|
|
|
* separately along with the peer capabilities.
|
|
|
*/
|
|
|
- wk->assoc.use_11n = !(ifmgd->flags & IEEE80211_STA_DISABLE_11N);
|
|
|
- wk->assoc.capability = req->bss->capability;
|
|
|
- wk->assoc.wmm_used = bss->wmm_used;
|
|
|
- wk->assoc.supp_rates = bss->supp_rates;
|
|
|
- wk->assoc.supp_rates_len = bss->supp_rates_len;
|
|
|
- wk->assoc.ht_information_ie =
|
|
|
+ assoc_data->capability = req->bss->capability;
|
|
|
+ assoc_data->wmm_used = bss->wmm_used;
|
|
|
+ assoc_data->supp_rates = bss->supp_rates;
|
|
|
+ assoc_data->supp_rates_len = bss->supp_rates_len;
|
|
|
+ assoc_data->ht_information_ie =
|
|
|
ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_INFORMATION);
|
|
|
|
|
|
if (bss->wmm_used && bss->uapsd_supported &&
|
|
|
(sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
|
|
|
- wk->assoc.uapsd_used = true;
|
|
|
+ assoc_data->uapsd_used = true;
|
|
|
ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED;
|
|
|
} else {
|
|
|
- wk->assoc.uapsd_used = false;
|
|
|
+ assoc_data->uapsd_used = false;
|
|
|
ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED;
|
|
|
}
|
|
|
|
|
|
- ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
|
|
|
- memcpy(wk->assoc.ssid, ssid + 2, ssid[1]);
|
|
|
- wk->assoc.ssid_len = ssid[1];
|
|
|
+ memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]);
|
|
|
+ assoc_data->ssid_len = ssidie[1];
|
|
|
|
|
|
if (req->prev_bssid)
|
|
|
- memcpy(wk->assoc.prev_bssid, req->prev_bssid, ETH_ALEN);
|
|
|
-
|
|
|
- wk->chan = req->bss->channel;
|
|
|
- wk->chan_type = NL80211_CHAN_NO_HT;
|
|
|
- wk->sdata = sdata;
|
|
|
- wk->done = ieee80211_assoc_done;
|
|
|
- if (!bss->dtim_period &&
|
|
|
- sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD)
|
|
|
- wk->type = IEEE80211_WORK_ASSOC_BEACON_WAIT;
|
|
|
- else
|
|
|
- wk->type = IEEE80211_WORK_ASSOC;
|
|
|
+ memcpy(assoc_data->prev_bssid, req->prev_bssid, ETH_ALEN);
|
|
|
|
|
|
if (req->use_mfp) {
|
|
|
ifmgd->mfp = IEEE80211_MFP_REQUIRED;
|
|
|
@@ -2737,15 +3352,79 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
|
|
|
sdata->control_port_protocol = req->crypto.control_port_ethertype;
|
|
|
sdata->control_port_no_encrypt = req->crypto.control_port_no_encrypt;
|
|
|
|
|
|
- ieee80211_add_work(wk);
|
|
|
- return 0;
|
|
|
+ /* kick off associate process */
|
|
|
+
|
|
|
+ ifmgd->assoc_data = assoc_data;
|
|
|
+
|
|
|
+ mutex_lock(&local->mtx);
|
|
|
+ ieee80211_recalc_idle(sdata->local);
|
|
|
+ mutex_unlock(&local->mtx);
|
|
|
+
|
|
|
+ /* switch to the right channel */
|
|
|
+ local->oper_channel = req->bss->channel;
|
|
|
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ sta = sta_info_get(sdata, req->bss->bssid);
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
+ if (!sta) {
|
|
|
+ /* set BSSID */
|
|
|
+ memcpy(ifmgd->bssid, req->bss->bssid, ETH_ALEN);
|
|
|
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID);
|
|
|
+
|
|
|
+ sta = sta_info_alloc(sdata, req->bss->bssid, GFP_KERNEL);
|
|
|
+ if (!sta) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto err_clear;
|
|
|
+ }
|
|
|
+
|
|
|
+ sta_info_pre_move_state(sta, IEEE80211_STA_AUTH);
|
|
|
+
|
|
|
+ err = sta_info_insert(sta);
|
|
|
+ sta = NULL;
|
|
|
+ if (err) {
|
|
|
+ printk(KERN_DEBUG
|
|
|
+ "%s: failed to insert STA entry for the AP (error %d)\n",
|
|
|
+ sdata->name, err);
|
|
|
+ goto err_clear;
|
|
|
+ }
|
|
|
+ } else
|
|
|
+ WARN_ON_ONCE(memcmp(ifmgd->bssid, req->bss->bssid, ETH_ALEN));
|
|
|
+
|
|
|
+ if (!bss->dtim_period &&
|
|
|
+ sdata->local->hw.flags & IEEE80211_HW_NEED_DTIM_PERIOD) {
|
|
|
+ /*
|
|
|
+ * Wait up to one beacon interval ...
|
|
|
+ * should this be more if we miss one?
|
|
|
+ */
|
|
|
+ printk(KERN_DEBUG "%s: waiting for beacon from %pM\n",
|
|
|
+ sdata->name, ifmgd->bssid);
|
|
|
+ assoc_data->timeout = jiffies +
|
|
|
+ TU_TO_EXP_TIME(req->bss->beacon_interval);
|
|
|
+ } else {
|
|
|
+ assoc_data->have_beacon = true;
|
|
|
+ assoc_data->sent_assoc = false;
|
|
|
+ assoc_data->timeout = jiffies;
|
|
|
+ }
|
|
|
+ run_again(ifmgd, assoc_data->timeout);
|
|
|
+
|
|
|
+ err = 0;
|
|
|
+ goto out;
|
|
|
+ err_clear:
|
|
|
+ ifmgd->assoc_data = NULL;
|
|
|
+ err_free:
|
|
|
+ kfree(assoc_data);
|
|
|
+ out:
|
|
|
+ mutex_unlock(&ifmgd->mtx);
|
|
|
+
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
|
|
|
struct cfg80211_deauth_request *req,
|
|
|
void *cookie)
|
|
|
{
|
|
|
- struct ieee80211_local *local = sdata->local;
|
|
|
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
|
|
|
bool assoc_bss = false;
|
|
|
|
|
|
@@ -2754,62 +3433,13 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
|
|
|
if (ifmgd->associated &&
|
|
|
memcmp(ifmgd->associated->bssid, req->bssid, ETH_ALEN) == 0) {
|
|
|
ieee80211_set_disassoc(sdata, false, true);
|
|
|
- mutex_unlock(&ifmgd->mtx);
|
|
|
assoc_bss = true;
|
|
|
- } else {
|
|
|
- bool not_auth_yet = false;
|
|
|
- struct ieee80211_work *tmp, *wk = NULL;
|
|
|
-
|
|
|
+ } else if (ifmgd->auth_data) {
|
|
|
+ ieee80211_destroy_auth_data(sdata, false);
|
|
|
mutex_unlock(&ifmgd->mtx);
|
|
|
-
|
|
|
- mutex_lock(&local->mtx);
|
|
|
- list_for_each_entry(tmp, &local->work_list, list) {
|
|
|
- if (tmp->sdata != sdata)
|
|
|
- continue;
|
|
|
-
|
|
|
- if (tmp->type != IEEE80211_WORK_DIRECT_PROBE &&
|
|
|
- tmp->type != IEEE80211_WORK_AUTH &&
|
|
|
- tmp->type != IEEE80211_WORK_ASSOC &&
|
|
|
- tmp->type != IEEE80211_WORK_ASSOC_BEACON_WAIT)
|
|
|
- continue;
|
|
|
-
|
|
|
- if (memcmp(req->bssid, tmp->filter_ta, ETH_ALEN))
|
|
|
- continue;
|
|
|
-
|
|
|
- not_auth_yet = tmp->type == IEEE80211_WORK_DIRECT_PROBE;
|
|
|
- list_del_rcu(&tmp->list);
|
|
|
- synchronize_rcu();
|
|
|
- wk = tmp;
|
|
|
- break;
|
|
|
- }
|
|
|
- mutex_unlock(&local->mtx);
|
|
|
-
|
|
|
- if (wk && wk->type == IEEE80211_WORK_ASSOC) {
|
|
|
- /* clean up dummy sta & TX sync */
|
|
|
- sta_info_destroy_addr(wk->sdata, wk->filter_ta);
|
|
|
- if (wk->assoc.synced)
|
|
|
- drv_finish_tx_sync(local, wk->sdata,
|
|
|
- wk->filter_ta,
|
|
|
- IEEE80211_TX_SYNC_ASSOC);
|
|
|
- } else if (wk && wk->type == IEEE80211_WORK_AUTH) {
|
|
|
- if (wk->probe_auth.synced)
|
|
|
- drv_finish_tx_sync(local, wk->sdata,
|
|
|
- wk->filter_ta,
|
|
|
- IEEE80211_TX_SYNC_AUTH);
|
|
|
- }
|
|
|
- kfree(wk);
|
|
|
-
|
|
|
- /*
|
|
|
- * If somebody requests authentication and we haven't
|
|
|
- * sent out an auth frame yet there's no need to send
|
|
|
- * out a deauth frame either. If the state was PROBE,
|
|
|
- * then this is the case. If it's AUTH we have sent a
|
|
|
- * frame, and if it's IDLE we have completed the auth
|
|
|
- * process already.
|
|
|
- */
|
|
|
- if (not_auth_yet)
|
|
|
- return 0;
|
|
|
+ return 0;
|
|
|
}
|
|
|
+ mutex_unlock(&ifmgd->mtx);
|
|
|
|
|
|
printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n",
|
|
|
sdata->name, req->bssid, req->reason_code);
|