[RFC] wpa_supplicant: add fast reconnect support

Sam Leffler sleffler
Wed Mar 23 18:07:22 PDT 2011


When dropped by an AP for inactivity or because the session has timed
out, try to re-join directly before falling back to a full scan and the
normal work.  This reduces the time to re-join a network from 5+ seconds
to ~300ms (not counting any time to renew DHCP).

Note that for mac80211 we cannot directly re-authenticate because by the
time we issue a request mac80211 has discarded the BSS state which is
required to process an MLME request.  Instead we scan the channel where
we last saw the AP and then use the normal join mechanism.  Since this
is not true for all drivers it might be worthwhile to try a direct Auth
first and only then fall back to scanning.  Also, note that if the scan
request collides with another scan then we just piggyback on that scan.

Change-Id: I31c869c565e36cc41f7698cad2a3e0a455316eeb
---
 wpa_supplicant/events.c           |  117 +++++++++++++++++++++++++++++++-----
 wpa_supplicant/mlme.c             |    4 +-
 wpa_supplicant/wpa_supplicant_i.h |    1 +
 3 files changed, 103 insertions(+), 19 deletions(-)

diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 41c5534..69d2191 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -856,7 +856,7 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
 	struct wpa_bss *selected;
 	struct wpa_ssid *ssid = NULL;
 	struct wpa_scan_results *scan_res;
-	int ap = 0;
+	int ap = 0, was_fast_reconnect;
 
 #ifdef CONFIG_AP
 	if (wpa_s->ap_iface)
@@ -865,6 +865,9 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
 
 	wpa_supplicant_notify_scanning(wpa_s, 0);
 
+	was_fast_reconnect = wpa_s->fast_reconnect;
+	wpa_s->fast_reconnect = FALSE;
+
 	scan_res = wpa_supplicant_get_scan_results(wpa_s,
 						   data ? &data->scan_info :
 						   NULL, 1);
@@ -950,6 +953,14 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
 		if (skip)
 			return 0;
 		wpa_supplicant_connect(wpa_s, selected, ssid);
+	} else if (was_fast_reconnect) {
+		/*
+		 * Process deferred disassoc work on a failed
+		 * fast reconnect.
+		 */
+		wpa_printf(MSG_INFO, "Fast reconnect failed");
+		wpa_scan_results_free(scan_res);
+		wpa_supplicant_mark_disassoc(wpa_s);
 	} else {
 		wpa_scan_results_free(scan_res);
 		wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found");
@@ -1365,11 +1376,47 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
 }
 
 
+/*
+ * Try to return to the same AP immediately.  Directly scan
+ * on the current channel for the current BSSID and try to
+ * reassociate.  If the station is not present we will clock
+ * the state machine to DISCONNECTED in wpa_supplicant_event_scan_results
+ * which will notify external agents.
+ */
+static int wpa_supplicant_fast_reconnect(struct wpa_supplicant *wpa_s,
+					 const u8 *bssid)
+{
+	struct wpa_driver_scan_params params;
+	int freqs[2];
+
+	wpa_s->reassociate = 1;
+	wpa_s->fast_reconnect = 1;
+
+	os_memset(&params, 0, sizeof(params));
+	params.num_ssids = 1;
+	params.ssids[0].ssid = wpa_s->current_ssid->ssid;
+	params.ssids[0].ssid_len = wpa_s->current_ssid->ssid_len;
+	freqs[0] = wpa_s->assoc_freq;
+	freqs[1] = 0;
+	params.freqs = freqs;
+
+	wpa_msg(wpa_s, MSG_INFO, "Fast reconnect bssid=" MACSTR
+		" ssid=%.*s freq=%d", MAC2STR(bssid),
+		params.ssids[0].ssid_len, params.ssids[0].ssid,
+		params.freqs[0]);
+
+	bgscan_deinit(wpa_s);
+	wpa_s->bgscan_ssid = NULL;
+
+	return wpa_supplicant_trigger_scan(wpa_s, &params);
+}
+
+
 static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s,
 					  u16 reason_code)
 {
 	const u8 *bssid;
-	int authenticating;
+	int authenticating, fast_reconnect;
 	u8 prev_pending_bssid[ETH_ALEN];
 
 	authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING;
@@ -1391,23 +1438,40 @@ static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s,
 		wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - "
 			"pre-shared key may be incorrect");
 	}
-	if (!wpa_s->auto_reconnect_disabled ||
-	    wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Auto connect enabled: try to "
-			"reconnect (wps=%d)",
-			wpa_s->key_mgmt == WPA_KEY_MGMT_WPS);
-		if (wpa_s->wpa_state >= WPA_ASSOCIATING)
-			wpa_supplicant_req_scan(wpa_s, 0, 100000);
-	} else {
-		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Auto connect disabled: do not "
-			"try to re-connect");
-		wpa_s->reassociate = 0;
-		wpa_s->disconnected = 1;
-	}
+	/*
+	 * If in a COMPLETED state and we were dropped for a reason
+	 * we can recover from directly re-join the AP without clocking
+	 * the state machine and/or notifying external agents.
+	 *
+	 * TODO(sleffler) guard against looping (should be only if AP is busted)
+	 */
+	fast_reconnect =
+	    wpa_s->wpa_state == WPA_COMPLETED &&
+	    (reason_code == WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY ||
+	     reason_code == WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA ||
+	     reason_code == WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
 	bssid = wpa_s->bssid;
-	if (is_zero_ether_addr(bssid))
+	if (is_zero_ether_addr(bssid)) {
 		bssid = wpa_s->pending_bssid;
-	wpas_connection_failed(wpa_s, bssid);
+		fast_reconnect = FALSE;
+		wpa_printf(MSG_DEBUG, "Do not reconnect; pending bssid switch");
+	}
+	if (!fast_reconnect) {
+		if (!wpa_s->auto_reconnect_disabled ||
+		    wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Auto connect enabled: "
+				"try to reconnect (wps=%d)",
+				wpa_s->key_mgmt == WPA_KEY_MGMT_WPS);
+			if (wpa_s->wpa_state >= WPA_ASSOCIATING)
+				wpa_supplicant_req_scan(wpa_s, 0, 100000);
+		} else {
+			wpa_dbg(wpa_s, MSG_DEBUG, "WPA: Auto connect disabled: "
+				"do not try to re-connect");
+			wpa_s->reassociate = 0;
+			wpa_s->disconnected = 1;
+		}
+		wpas_connection_failed(wpa_s, bssid);
+	}
 	wpa_sm_notify_disassoc(wpa_s->wpa);
 	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
 		" reason=%d",
@@ -1417,6 +1481,25 @@ static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s,
 		wpa_s->keys_cleared = 0;
 		wpa_clear_keys(wpa_s, wpa_s->bssid);
 	}
+	if (fast_reconnect) {
+		int ret = wpa_supplicant_fast_reconnect(wpa_s, bssid);
+		if (ret == 0)
+			return;
+		if (ret == -EBUSY) {
+			/*
+			 * NB: EBUSY indicates a scan is already in progress.
+			 * Just wait for it to complete; it will do the same
+			 * reconnect work just a bit delayed (since it's likely
+			 * scanning more than 1 channel).
+			 */
+			wpa_printf(MSG_INFO, "Fast reconnect: piggyback on "
+				   "existing scan");
+			return;
+		}
+		/* NB: fall through to slow path */
+		wpa_printf(MSG_DEBUG, "Fast reconnect: Failed to trigger scan");
+		wpa_supplicant_req_scan(wpa_s, 0, 100000);
+	}
 	wpa_supplicant_mark_disassoc(wpa_s);
 
 	if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
diff --git a/wpa_supplicant/mlme.c b/wpa_supplicant/mlme.c
index 49713f5..0bd6f59 100644
--- a/wpa_supplicant/mlme.c
+++ b/wpa_supplicant/mlme.c
@@ -2812,7 +2812,7 @@ int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s,
 	size_t ssid_len = params->ssids[0].ssid_len;
 
 	if (ssid_len > MAX_SSID_LEN)
-		return -1;
+		return -EINVAL;
 
 	/* MLME-SCAN.request (page 118)  page 144 (11.1.3.1)
 	 * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS
@@ -2835,7 +2835,7 @@ int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s,
 	 * scan */
 
 	if (wpa_s->mlme.sta_scanning)
-		return -1;
+		return -EBUSY;
 
 	wpa_printf(MSG_DEBUG, "MLME: starting scan");
 
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 859a65b..eaabe0f 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -347,6 +347,7 @@ struct wpa_supplicant {
 	int reassociate; /* reassociation requested */
 	int disconnected; /* all connections disabled; i.e., do no reassociate
 			   * before this has been cleared */
+	int fast_reconnect; /* reassociate or go to disconnected */
 	struct wpa_ssid *current_ssid;
 	struct wpa_bss *current_bss;
 	int ap_ies_from_associnfo;
-- 
1.7.3.1




More information about the Hostap mailing list