summaryrefslogtreecommitdiff
path: root/net/mac80211/mlme.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r--net/mac80211/mlme.c189
1 files changed, 117 insertions, 72 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 52133da..caf97f5 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -88,6 +88,8 @@ MODULE_PARM_DESC(probe_wait_ms,
#define TMR_RUNNING_TIMER 0
#define TMR_RUNNING_CHANSW 1
+#define DEAUTH_DISASSOC_LEN (24 /* hdr */ + 2 /* reason */)
+
/*
* All cfg80211 functions have to be called outside a locked
* section so that they can acquire a lock themselves... This
@@ -612,47 +614,42 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata)
}
static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata,
- const u8 *bssid, u16 stype, u16 reason,
- void *cookie, bool send_frame)
+ const u8 *bssid, u16 stype,
+ u16 reason, bool send_frame,
+ u8 *frame_buf)
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct sk_buff *skb;
- struct ieee80211_mgmt *mgmt;
-
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt));
- if (!skb)
- return;
-
- skb_reserve(skb, local->hw.extra_tx_headroom);
+ struct ieee80211_mgmt *mgmt = (void *)frame_buf;
- mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24);
- memset(mgmt, 0, 24);
+ /* build frame */
+ mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | stype);
+ mgmt->duration = 0; /* initialize only */
+ mgmt->seq_ctrl = 0; /* initialize only */
memcpy(mgmt->da, bssid, ETH_ALEN);
memcpy(mgmt->sa, sdata->vif.addr, 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 */
mgmt->u.deauth.reason_code = cpu_to_le16(reason);
- if (stype == IEEE80211_STYPE_DEAUTH)
- if (cookie)
- __cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
- else
- cfg80211_send_deauth(sdata->dev, (u8 *)mgmt, skb->len);
- else
- if (cookie)
- __cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len);
- else
- cfg80211_send_disassoc(sdata->dev, (u8 *)mgmt, skb->len);
- if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED))
- IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;
+ if (send_frame) {
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom +
+ DEAUTH_DISASSOC_LEN);
+ if (!skb)
+ return;
- if (send_frame)
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+
+ /* copy in frame */
+ memcpy(skb_put(skb, DEAUTH_DISASSOC_LEN),
+ mgmt, DEAUTH_DISASSOC_LEN);
+
+ if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED))
+ IEEE80211_SKB_CB(skb)->flags |=
+ IEEE80211_TX_INTFL_DONT_ENCRYPT;
ieee80211_tx_skb(sdata, skb);
- else
- kfree_skb(skb);
+ }
}
void ieee80211_send_pspoll(struct ieee80211_local *local,
@@ -1392,7 +1389,8 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
}
static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
- bool remove_sta, bool tx)
+ u16 stype, u16 reason, bool tx,
+ u8 *frame_buf)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
@@ -1402,6 +1400,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
ASSERT_MGD_MTX(ifmgd);
+ if (WARN_ON_ONCE(tx && !frame_buf))
+ return;
+
if (WARN_ON(!ifmgd->associated))
return;
@@ -1435,6 +1436,19 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
}
mutex_unlock(&local->sta_mtx);
+ /* deauthenticate/disassociate now */
+ if (tx || frame_buf)
+ ieee80211_send_deauth_disassoc(sdata, bssid, stype, reason,
+ tx, frame_buf);
+
+ /* flush out frame */
+ if (tx)
+ drv_flush(local, false);
+
+ /* remove AP and TDLS peers */
+ sta_info_flush(local, sdata);
+
+ /* finally reset all BSS / config parameters */
changed |= ieee80211_reset_erp_info(sdata);
ieee80211_led_assoc(local, 0);
@@ -1474,10 +1488,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT;
ieee80211_bss_info_change_notify(sdata, changed);
- /* remove AP and TDLS peers */
- if (remove_sta)
- sta_info_flush(local, sdata);
-
del_timer_sync(&sdata->u.mgd.conn_mon_timer);
del_timer_sync(&sdata->u.mgd.bcn_mon_timer);
del_timer_sync(&sdata->u.mgd.timer);
@@ -1674,6 +1684,7 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
u8 bssid[ETH_ALEN];
+ u8 frame_buf[DEAUTH_DISASSOC_LEN];
mutex_lock(&ifmgd->mtx);
if (!ifmgd->associated) {
@@ -1686,17 +1697,16 @@ static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
printk(KERN_DEBUG "%s: Connection to AP %pM lost.\n",
sdata->name, bssid);
- ieee80211_set_disassoc(sdata, true, true);
+ ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
+ WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
+ false, frame_buf);
mutex_unlock(&ifmgd->mtx);
/*
* must be outside lock due to cfg80211,
* but that's not a problem.
*/
- ieee80211_send_deauth_disassoc(sdata, bssid,
- IEEE80211_STYPE_DEAUTH,
- WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
- NULL, true);
+ cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
mutex_lock(&local->mtx);
ieee80211_recalc_idle(local);
@@ -1903,7 +1913,8 @@ ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
printk(KERN_DEBUG "%s: deauthenticated from %pM (Reason: %u)\n",
sdata->name, bssid, reason_code);
- ieee80211_set_disassoc(sdata, true, false);
+ ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
+
mutex_lock(&sdata->local->mtx);
ieee80211_recalc_idle(sdata->local);
mutex_unlock(&sdata->local->mtx);
@@ -1933,10 +1944,12 @@ 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, true, false);
+ ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
+
mutex_lock(&sdata->local->mtx);
ieee80211_recalc_idle(sdata->local);
mutex_unlock(&sdata->local->mtx);
+
return RX_MGMT_CFG80211_DISASSOC;
}
@@ -2238,14 +2251,28 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee80211_sub_if_data *sdata,
} else {
printk(KERN_DEBUG "%s: associated\n", sdata->name);
- ieee80211_destroy_assoc_data(sdata, true);
+ /* tell driver about sync done first */
+ if (assoc_data->synced) {
+ drv_finish_tx_sync(sdata->local, sdata,
+ assoc_data->bss->bssid,
+ IEEE80211_TX_SYNC_ASSOC);
+ assoc_data->synced = false;
+ }
if (!ieee80211_assoc_success(sdata, *bss, mgmt, len)) {
/* oops -- internal error -- send timeout for now */
+ ieee80211_destroy_assoc_data(sdata, true);
sta_info_destroy_addr(sdata, mgmt->bssid);
cfg80211_put_bss(*bss);
return RX_MGMT_CFG80211_ASSOC_TIMEOUT;
}
+
+ /*
+ * destroy assoc_data afterwards, as otherwise an idle
+ * recalc after assoc_data is NULL but before associated
+ * is set can cause the interface to go idle
+ */
+ ieee80211_destroy_assoc_data(sdata, true);
}
return RX_MGMT_CFG80211_RX_ASSOC;
@@ -2681,19 +2708,20 @@ static void ieee80211_sta_connection_lost(struct ieee80211_sub_if_data *sdata,
{
struct ieee80211_local *local = sdata->local;
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ u8 frame_buf[DEAUTH_DISASSOC_LEN];
ifmgd->flags &= ~(IEEE80211_STA_CONNECTION_POLL |
IEEE80211_STA_BEACON_POLL);
- ieee80211_set_disassoc(sdata, true, true);
+ ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, reason,
+ false, frame_buf);
mutex_unlock(&ifmgd->mtx);
+
/*
* must be outside lock due to cfg80211,
* but that's not a problem.
*/
- ieee80211_send_deauth_disassoc(sdata, bssid,
- IEEE80211_STYPE_DEAUTH, reason,
- NULL, true);
+ cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
mutex_lock(&local->mtx);
ieee80211_recalc_idle(local);
@@ -2974,13 +3002,17 @@ static void ieee80211_sta_monitor_work(struct work_struct *work)
static void ieee80211_restart_sta_timer(struct ieee80211_sub_if_data *sdata)
{
+ u32 flags;
+
if (sdata->vif.type == NL80211_IFTYPE_STATION) {
sdata->u.mgd.flags &= ~(IEEE80211_STA_BEACON_POLL |
IEEE80211_STA_CONNECTION_POLL);
/* let's probe the connection once */
- ieee80211_queue_work(&sdata->local->hw,
- &sdata->u.mgd.monitor_work);
+ flags = sdata->local->hw.flags;
+ if (!(flags & IEEE80211_HW_CONNECTION_MONITOR))
+ ieee80211_queue_work(&sdata->local->hw,
+ &sdata->u.mgd.monitor_work);
/* and do all the other regular work too */
ieee80211_queue_work(&sdata->local->hw, &sdata->work);
}
@@ -3044,7 +3076,6 @@ void ieee80211_sta_restart(struct ieee80211_sub_if_data *sdata)
add_timer(&ifmgd->chswitch_timer);
ieee80211_sta_reset_beacon_monitor(sdata);
ieee80211_restart_sta_timer(sdata);
- ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.monitor_work);
}
#endif
@@ -3174,7 +3205,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata,
ifmgd->auth_data = auth_data;
if (ifmgd->associated)
- ieee80211_set_disassoc(sdata, true, false);
+ ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
printk(KERN_DEBUG "%s: authenticate with %pM\n",
sdata->name, req->bss->bssid);
@@ -3252,7 +3283,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
mutex_lock(&ifmgd->mtx);
if (ifmgd->associated)
- ieee80211_set_disassoc(sdata, true, false);
+ ieee80211_set_disassoc(sdata, 0, 0, false, NULL);
if (ifmgd->auth_data && !ifmgd->auth_data->done) {
err = -EBUSY;
@@ -3422,32 +3453,35 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
}
int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
- struct cfg80211_deauth_request *req,
- void *cookie)
+ struct cfg80211_deauth_request *req)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
- bool assoc_bss = false;
+ u8 frame_buf[DEAUTH_DISASSOC_LEN];
mutex_lock(&ifmgd->mtx);
- if (ifmgd->associated &&
- memcmp(ifmgd->associated->bssid, req->bssid, ETH_ALEN) == 0) {
- ieee80211_set_disassoc(sdata, false, true);
- assoc_bss = true;
- } else if (ifmgd->auth_data) {
+ if (ifmgd->auth_data) {
ieee80211_destroy_auth_data(sdata, false);
mutex_unlock(&ifmgd->mtx);
return 0;
}
- mutex_unlock(&ifmgd->mtx);
- printk(KERN_DEBUG "%s: deauthenticating from %pM by local choice (reason=%d)\n",
+ printk(KERN_DEBUG
+ "%s: deauthenticating from %pM by local choice (reason=%d)\n",
sdata->name, req->bssid, req->reason_code);
- ieee80211_send_deauth_disassoc(sdata, req->bssid, IEEE80211_STYPE_DEAUTH,
- req->reason_code, cookie, true);
- if (assoc_bss)
- sta_info_flush(sdata->local, sdata);
+ if (ifmgd->associated &&
+ memcmp(ifmgd->associated->bssid, req->bssid, ETH_ALEN) == 0)
+ ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH,
+ req->reason_code, true, frame_buf);
+ else
+ ieee80211_send_deauth_disassoc(sdata, req->bssid,
+ IEEE80211_STYPE_DEAUTH,
+ req->reason_code, true,
+ frame_buf);
+ mutex_unlock(&ifmgd->mtx);
+
+ __cfg80211_send_deauth(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
mutex_lock(&sdata->local->mtx);
ieee80211_recalc_idle(sdata->local);
@@ -3457,11 +3491,11 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata,
}
int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
- struct cfg80211_disassoc_request *req,
- void *cookie)
+ struct cfg80211_disassoc_request *req)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
u8 bssid[ETH_ALEN];
+ u8 frame_buf[DEAUTH_DISASSOC_LEN];
mutex_lock(&ifmgd->mtx);
@@ -3480,14 +3514,12 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
sdata->name, req->bss->bssid, req->reason_code);
memcpy(bssid, req->bss->bssid, ETH_ALEN);
- ieee80211_set_disassoc(sdata, false, true);
-
+ ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DISASSOC,
+ req->reason_code, !req->local_state_change,
+ frame_buf);
mutex_unlock(&ifmgd->mtx);
- ieee80211_send_deauth_disassoc(sdata, req->bss->bssid,
- IEEE80211_STYPE_DISASSOC, req->reason_code,
- cookie, !req->local_state_change);
- sta_info_flush(sdata->local, sdata);
+ __cfg80211_send_disassoc(sdata->dev, frame_buf, DEAUTH_DISASSOC_LEN);
mutex_lock(&sdata->local->mtx);
ieee80211_recalc_idle(sdata->local);
@@ -3496,6 +3528,19 @@ int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
return 0;
}
+void ieee80211_mgd_teardown(struct ieee80211_sub_if_data *sdata)
+{
+ struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+
+ mutex_lock(&ifmgd->mtx);
+ if (ifmgd->assoc_data)
+ ieee80211_destroy_assoc_data(sdata, false);
+ if (ifmgd->auth_data)
+ ieee80211_destroy_auth_data(sdata, false);
+ del_timer_sync(&ifmgd->timer);
+ mutex_unlock(&ifmgd->mtx);
+}
+
void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
enum nl80211_cqm_rssi_threshold_event rssi_event,
gfp_t gfp)