Search Linux Wireless

[RFC] mac80211: wait for beacon before associating

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch makes mac80211 wait for a beacon
before associating to have proper information
from it, in particular the DTIM period. It
also makes it not probe the AP if it already
has a probe response, so that only one of
probing and waiting for beacon can be done,
one of them must have been received to know
about the BSS.

Note that I don't think this helps the
synchronisation issue Kalle pointed out since
we do not tell the driver about the bssid, in
fact in this patch I explicitly open up the
filters to receive the beacon.

The default timeout is 112.5% of the beacon
interval, so a single lost beacon could cause
the association to fail, I'm not sure if that
is desirable or if the default timeout should
be longer.
---
This requires "mac80211: track work started through callbacks"

 drivers/net/wireless/mac80211_hwsim.c |    4 -
 net/mac80211/ieee80211_i.h            |    2 
 net/mac80211/main.c                   |    2 
 net/mac80211/mlme.c                   |   45 +++++++++++++++++---
 net/mac80211/work.c                   |   73 ++++++++++++++++++++++++++++++----
 5 files changed, 109 insertions(+), 17 deletions(-)

--- wireless-testing.orig/net/mac80211/mlme.c	2010-01-25 12:52:58.000000000 +0100
+++ wireless-testing/net/mac80211/mlme.c	2010-01-25 13:28:29.000000000 +0100
@@ -27,10 +27,6 @@
 #include "rate.h"
 #include "led.h"
 
-#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_MAX_PROBE_TRIES 5
 
 /*
@@ -1823,7 +1819,11 @@ int ieee80211_mgd_auth(struct ieee80211_
 	wk->probe_auth.algorithm = auth_alg;
 	wk->probe_auth.privacy = req->bss->capability & WLAN_CAPABILITY_PRIVACY;
 
-	wk->type = IEEE80211_WORK_DIRECT_PROBE;
+	/* 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->sdata = sdata;
 	wk->done = ieee80211_probe_auth_done;
@@ -1862,6 +1862,32 @@ static enum work_done_result ieee80211_a
 	return WORK_DONE_DESTROY;
 }
 
+static enum work_done_result ieee80211_beacon_done(struct ieee80211_work *wk,
+						   struct sk_buff *skb)
+{
+	struct ieee80211_mgmt *mgmt;
+	struct ieee80211_rx_status *rx_status;
+	size_t baselen, len;
+	struct ieee802_11_elems elems;
+
+	if (!skb) {
+		cfg80211_send_assoc_timeout(wk->sdata->dev, wk->filter_ta);
+		return WORK_DONE_DESTROY;
+	}
+
+	mgmt = (void *)skb->data;
+	rx_status = (void *) skb->cb;
+	len = skb->len;
+	baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
+
+	ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen, &elems);
+	ieee80211_rx_bss_info(wk->sdata, mgmt, len, rx_status, &elems, true);
+
+	wk->type = IEEE80211_WORK_ASSOC;
+	wk->done = ieee80211_assoc_done;
+	return WORK_DONE_REQUEUE;
+}
+
 int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
 			struct cfg80211_assoc_request *req)
 {
@@ -1942,10 +1968,15 @@ int ieee80211_mgd_assoc(struct ieee80211
 	if (req->prev_bssid)
 		memcpy(wk->assoc.prev_bssid, req->prev_bssid, ETH_ALEN);
 
-	wk->type = IEEE80211_WORK_ASSOC;
+	if (req->bss->beacon_ies) {
+		wk->type = IEEE80211_WORK_ASSOC;
+		wk->done = ieee80211_assoc_done;
+	} else {
+		wk->type = IEEE80211_WORK_WAIT_BEACON;
+		wk->done = ieee80211_beacon_done;
+	}
 	wk->chan = req->bss->channel;
 	wk->sdata = sdata;
-	wk->done = ieee80211_assoc_done;
 
 	if (req->use_mfp) {
 		ifmgd->mfp = IEEE80211_MFP_REQUIRED;
--- wireless-testing.orig/net/mac80211/ieee80211_i.h	2010-01-25 12:53:11.000000000 +0100
+++ wireless-testing/net/mac80211/ieee80211_i.h	2010-01-25 13:01:59.000000000 +0100
@@ -240,6 +240,7 @@ enum ieee80211_work_type {
 	IEEE80211_WORK_AUTH,
 	IEEE80211_WORK_ASSOC,
 	IEEE80211_WORK_REMAIN_ON_CHANNEL,
+	IEEE80211_WORK_WAIT_BEACON,
 };
 
 /**
@@ -618,6 +619,7 @@ struct ieee80211_local {
 	struct timer_list work_timer;
 	struct work_struct work_work;
 	struct sk_buff_head work_skb_queue;
+	int bcnwait;
 
 	/*
 	 * private workqueue to mac80211. mac80211 makes this accessible
--- wireless-testing.orig/net/mac80211/main.c	2010-01-25 12:52:58.000000000 +0100
+++ wireless-testing/net/mac80211/main.c	2010-01-25 12:59:48.000000000 +0100
@@ -50,7 +50,7 @@ void ieee80211_configure_filter(struct i
 	if (atomic_read(&local->iff_allmultis))
 		new_flags |= FIF_ALLMULTI;
 
-	if (local->monitors || local->scanning)
+	if (local->monitors || local->scanning || local->bcnwait)
 		new_flags |= FIF_BCN_PRBRESP_PROMISC;
 
 	if (local->fif_fcsfail)
--- wireless-testing.orig/net/mac80211/work.c	2010-01-25 12:56:46.000000000 +0100
+++ wireless-testing/net/mac80211/work.c	2010-01-25 13:24:48.000000000 +0100
@@ -548,6 +548,37 @@ ieee80211_remain_on_channel_timeout(stru
 	return WORK_ACT_TIMEOUT;
 }
 
+static enum work_action __must_check
+ieee80211_wait_beacon(struct ieee80211_work *wk)
+{
+	struct cfg80211_bss *cbss =
+		container_of((void *)wk->assoc.bss, struct cfg80211_bss, priv);
+
+	if (WARN_ON(!wk->assoc.bss))
+		return WORK_ACT_TIMEOUT;
+
+	/*
+	 * First time we run, just configure the filters -- the
+	 * generic code will have switched to the right channel
+	 * on which we will have to wait for the beacon.
+	 */
+	if (!wk->started) {
+		u32 bcnint = cbss->beacon_interval;
+
+		wk->sdata->local->bcnwait++;
+		ieee80211_configure_filter(wk->sdata->local);
+		/* add 1/8th of the beacon interval */
+		wk->timeout = TU_TO_EXP_TIME(bcnint + (bcnint >> 3));
+		return WORK_ACT_NONE;
+	}
+
+	/*
+	 * Next time around we were unsuccessful waiting
+	 * for a beacon. Strange.
+	 */
+	return WORK_ACT_TIMEOUT;
+}
+
 static void ieee80211_auth_challenge(struct ieee80211_work *wk,
 				     struct ieee80211_mgmt *mgmt,
 				     size_t len)
@@ -671,8 +702,7 @@ ieee80211_rx_mgmt_assoc_resp(struct ieee
 
 static enum work_action __must_check
 ieee80211_rx_mgmt_probe_resp(struct ieee80211_work *wk,
-			     struct ieee80211_mgmt *mgmt, size_t len,
-			     struct ieee80211_rx_status *rx_status)
+			     struct ieee80211_mgmt *mgmt, size_t len)
 {
 	struct ieee80211_sub_if_data *sdata = wk->sdata;
 	struct ieee80211_local *local = sdata->local;
@@ -688,6 +718,29 @@ ieee80211_rx_mgmt_probe_resp(struct ieee
 	return WORK_ACT_DONE;
 }
 
+static enum work_action __must_check
+ieee80211_rx_mgmt_beacon(struct ieee80211_work *wk,
+			 struct ieee80211_mgmt *mgmt, size_t len,
+			 struct ieee80211_rx_status *rx_status)
+{
+	struct ieee80211_sub_if_data *sdata = wk->sdata;
+	struct ieee80211_local *local = sdata->local;
+	size_t baselen;
+
+	ASSERT_WORK_MTX(local);
+
+	baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
+	if (baselen > len)
+		return WORK_ACT_NONE;
+
+	printk(KERN_DEBUG "%s: beacon received\n", sdata->name);
+
+	local->bcnwait--;
+	ieee80211_configure_filter(local);
+
+	return WORK_ACT_DONE;
+}
+
 static void ieee80211_work_rx_queued_mgmt(struct ieee80211_local *local,
 					  struct sk_buff *skb)
 {
@@ -710,6 +763,7 @@ static void ieee80211_work_rx_queued_mgm
 		case IEEE80211_WORK_DIRECT_PROBE:
 		case IEEE80211_WORK_AUTH:
 		case IEEE80211_WORK_ASSOC:
+		case IEEE80211_WORK_WAIT_BEACON:
 			bssid = wk->filter_ta;
 			break;
 		default:
@@ -725,8 +779,7 @@ static void ieee80211_work_rx_queued_mgm
 
 		switch (fc & IEEE80211_FCTL_STYPE) {
 		case IEEE80211_STYPE_PROBE_RESP:
-			rma = ieee80211_rx_mgmt_probe_resp(wk, mgmt, skb->len,
-							   rx_status);
+			rma = ieee80211_rx_mgmt_probe_resp(wk, mgmt, skb->len);
 			break;
 		case IEEE80211_STYPE_AUTH:
 			rma = ieee80211_rx_mgmt_auth(wk, mgmt, skb->len);
@@ -739,8 +792,12 @@ static void ieee80211_work_rx_queued_mgm
 			rma = ieee80211_rx_mgmt_assoc_resp(wk, mgmt,
 							   skb->len, true);
 			break;
+		case IEEE80211_STYPE_BEACON:
+			rma = ieee80211_rx_mgmt_beacon(wk, mgmt, skb->len,
+						       rx_status);
+			break;
 		default:
-			WARN_ON(1);
+			WARN(1, "unexpected frame type %#x", fc);
 		}
 		/*
 		 * We've processed this frame for that work, so it can't
@@ -881,6 +938,9 @@ static void ieee80211_work_work(struct w
 		case IEEE80211_WORK_REMAIN_ON_CHANNEL:
 			rma = ieee80211_remain_on_channel_timeout(wk);
 			break;
+		case IEEE80211_WORK_WAIT_BEACON:
+			rma = ieee80211_wait_beacon(wk);
+			break;
 		}
 
 		wk->started = started;
@@ -1025,8 +1085,7 @@ ieee80211_rx_result ieee80211_work_rx_mg
 		case IEEE80211_STYPE_PROBE_RESP:
 		case IEEE80211_STYPE_ASSOC_RESP:
 		case IEEE80211_STYPE_REASSOC_RESP:
-		case IEEE80211_STYPE_DEAUTH:
-		case IEEE80211_STYPE_DISASSOC:
+		case IEEE80211_STYPE_BEACON:
 			skb_queue_tail(&local->work_skb_queue, skb);
 			ieee80211_queue_work(&local->hw, &local->work_work);
 			return RX_QUEUED;
--- wireless-testing.orig/drivers/net/wireless/mac80211_hwsim.c	2010-01-25 13:26:12.000000000 +0100
+++ wireless-testing/drivers/net/wireless/mac80211_hwsim.c	2010-01-25 13:27:15.000000000 +0100
@@ -723,8 +723,8 @@ static void mac80211_hwsim_bss_info_chan
 	}
 
 	if (changed & BSS_CHANGED_ASSOC) {
-		printk(KERN_DEBUG "  %s: ASSOC: assoc=%d aid=%d\n",
-		       wiphy_name(hw->wiphy), info->assoc, info->aid);
+		printk(KERN_DEBUG "  %s: ASSOC: assoc=%d aid=%d dtimper=%d\n",
+		       wiphy_name(hw->wiphy), info->assoc, info->aid, info->dtim_period);
 		vp->assoc = info->assoc;
 		vp->aid = info->aid;
 	}


--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux