[RFC] Roam correctly through cfg80211 without SME

Christopher Wiley wiley at chromium.org
Wed Oct 10 17:51:04 EDT 2012


Change the nl80211 driver in wpa_supplicant to correctly handle
connecting to a new AP through cfg80211 without SME capability.  As
before, the driver will disconnect from the previously associated AP,
but now we attempt to immediately connect to our intended AP.  This
prevents us from blacklisting the AP we were trying to connect to
because of a semantic mismatch between cfg80211 and wpa_supplicant.  The
disconnect/connect patch generates a local disconnect nl80211 event
which we discard because we're already correctly tracking the pending
association request.

In detail:

cfg80211 does not support connecting to a new BSS while already
connected to another BSS, if the underlying driver doesn't support
separate authenticate and associate commands.  wpa_supplicant is written
to expect that this is a supported operation, except for a little error
handling that disconnects from the current BSS when roaming fails and
relies on autoconnect logic to reconnect later.  However, this failure
to connect is incorrectly attributed to the new AP we attempted to
associate with, rather than a local condition in cfg80211.

The combined effect of these two conditions is that full-mac drivers
accessible through cfg80211 but without SME capability take a long time
to roam across BSS's because wpa_supplicant will:
1) Fail to associate for local reasons
2) Disconnect and return that the association request failed
3) Blacklist the association target (incorrectly)
4) Do a scan
5) Pick a less desirable AP to associate with
---
 src/drivers/driver_nl80211.c |   51 +++++++++++++++++++++++++++++++++--------
 1 files changed, 41 insertions(+), 10 deletions(-)

diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 58ede8e..129b0e0 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -250,6 +250,7 @@ struct wpa_driver_nl80211_data {
 	unsigned int scan_for_auth:1;
 	unsigned int retry_auth:1;
 	unsigned int use_monitor:1;
+	unsigned int ignore_next_local_disconnect:1;
 
 	u64 remain_on_chan_cookie;
 	u64 send_action_cookie;
@@ -1182,6 +1183,7 @@ static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
 				  struct nlattr *by_ap)
 {
 	union wpa_event_data data;
+	unsigned int locally_generated = by_ap == NULL;
 
 	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
 		/*
@@ -1192,6 +1194,18 @@ static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
 			   "event when using userspace SME");
 		return;
 	}
+	if (drv->ignore_next_local_disconnect) {
+		drv->ignore_next_local_disconnect = 0;
+		if (locally_generated) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
+				   "event triggered during reassociation");
+			return;
+		}
+		wpa_printf(MSG_WARNING, "nl80211: Was expecting local "
+			   "disconnect but got another disconnect "
+			   "event first");
+	}
+
 
 	drv->associated = 0;
 	os_memset(&data, 0, sizeof(data));
@@ -4480,6 +4494,7 @@ static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
 {
 	wpa_printf(MSG_DEBUG, "%s(reason_code=%d)", __func__, reason_code);
 	drv->associated = 0;
+	drv->ignore_next_local_disconnect = 0;
 	/* Disconnect command doesn't need BSSID - it uses cached value */
 	return wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT,
 				       reason_code, 0);
@@ -6626,7 +6641,7 @@ nla_put_failure:
 }
 
 
-static int wpa_driver_nl80211_connect(
+static int wpa_driver_nl80211_try_connect(
 	struct wpa_driver_nl80211_data *drv,
 	struct wpa_driver_associate_params *params)
 {
@@ -6806,15 +6821,6 @@ skip_auth_type:
 	if (ret) {
 		wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
 			   "(%s)", ret, strerror(-ret));
-		/*
-		 * cfg80211 does not currently accept new connection if we are
-		 * already connected. As a workaround, force disconnection and
-		 * try again once the driver indicates it completed
-		 * disconnection.
-		 */
-		if (ret == -EALREADY)
-			wpa_driver_nl80211_disconnect(
-				drv, WLAN_REASON_PREV_AUTH_NOT_VALID);
 		goto nla_put_failure;
 	}
 	ret = 0;
@@ -6827,6 +6833,31 @@ nla_put_failure:
 }
 
 
+static int wpa_driver_nl80211_connect(
+       struct wpa_driver_nl80211_data *drv,
+       struct wpa_driver_associate_params *params)
+{
+       int ret = wpa_driver_nl80211_try_connect(drv, params);
+       if (ret == -EALREADY) {
+               /*
+                * cfg80211 does not currently accept new connections if
+                * we are already connected. As a workaround, force
+                * disconnection and try again.
+                */
+               wpa_printf(MSG_DEBUG, "nl80211: Explicitly "
+                          "disconnecting before reassociation "
+                          "attempt");
+               if (wpa_driver_nl80211_disconnect(
+				drv, WLAN_REASON_PREV_AUTH_NOT_VALID))
+                       return -1;
+               /* Ignore the next local disconnect message. */
+               drv->ignore_next_local_disconnect = 1;
+               ret = wpa_driver_nl80211_try_connect(drv, params);
+       }
+       return ret;
+}
+
+
 static int wpa_driver_nl80211_associate(
 	void *priv, struct wpa_driver_associate_params *params)
 {
-- 
1.7.7.3



More information about the HostAP mailing list