Search Linux Wireless

[RFC 5/5] cfg80211: scan before connect if we don't have the bss

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

 



The connect_result callback relies on cfg80211 having the bss
information (via beacons/probes). For a fullmac driver, this information
is only likely to be present after a scan. Userspace is not guaranteed
to scan before connecting (e.g. wpa_supplicant ap_scan=2 in wext mode),
so before calling ->connect do an explicit ->scan if we don't have the
bss in our list.

Signed-off-by: David Kilroy <kilroyd@xxxxxxxxxxxxxx>
---
 net/wireless/sme.c |  150 ++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 110 insertions(+), 40 deletions(-)

diff --git a/net/wireless/sme.c b/net/wireless/sme.c
index 9ddc00e..6dc7981 100644
--- a/net/wireless/sme.c
+++ b/net/wireless/sme.c
@@ -26,11 +26,13 @@ struct cfg80211_conn {
 		CFG80211_CONN_AUTHENTICATING,
 		CFG80211_CONN_ASSOCIATE_NEXT,
 		CFG80211_CONN_ASSOCIATING,
+		CFG80211_CONN_DELAYED_CONNECT,
 	} state;
 	u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
 	u8 *ie;
 	size_t ie_len;
 	bool auto_auth, prev_bssid_valid;
+	bool connect_after_scan;
 };
 
 
@@ -148,6 +150,19 @@ static int cfg80211_conn_do_work(struct wireless_dev *wdev)
 					       NULL, 0,
 					       WLAN_REASON_DEAUTH_LEAVING);
 		return err;
+	case CFG80211_CONN_DELAYED_CONNECT:
+		BUG_ON(!rdev->ops->connect);
+		err = rdev->ops->connect(&rdev->wiphy, wdev->netdev,
+					 &wdev->conn->params);
+		kfree(wdev->conn->ie);
+		kfree(wdev->conn);
+		wdev->conn = NULL;
+		if (err) {
+			wdev->connect_keys = NULL;
+			wdev->sme_state = CFG80211_SME_IDLE;
+			wdev->ssid_len = 0;
+		}
+		return err;
 	default:
 		return 0;
 	}
@@ -234,6 +249,12 @@ static void __cfg80211_sme_scan_done(struct net_device *dev)
 	    wdev->conn->state != CFG80211_CONN_SCAN_AGAIN)
 		return;
 
+	if (wdev->conn->connect_after_scan) {
+		wdev->conn->state = CFG80211_CONN_DELAYED_CONNECT;
+		schedule_work(&rdev->conn_work);
+		return;
+	}
+
 	if (!cfg80211_get_conn_bss(wdev)) {
 		/* not found */
 		if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)
@@ -655,6 +676,52 @@ void cfg80211_disconnected(struct net_device *dev, u16 reason,
 }
 EXPORT_SYMBOL(cfg80211_disconnected);
 
+static int cfg80211_conn_clone(struct wireless_dev *wdev,
+			       struct cfg80211_connect_params *connect)
+{
+	if (WARN_ON(wdev->conn))
+		return -EINPROGRESS;
+
+	wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
+	if (!wdev->conn)
+		return -ENOMEM;
+
+	memcpy(&wdev->conn->params, connect, sizeof(*connect));
+	if (connect->bssid) {
+		wdev->conn->params.bssid = wdev->conn->bssid;
+		memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
+	}
+
+	if (connect->ie) {
+		wdev->conn->ie = kmemdup(connect->ie, connect->ie_len,
+					 GFP_KERNEL);
+		wdev->conn->params.ie = wdev->conn->ie;
+		if (!wdev->conn->ie) {
+			kfree(wdev->conn);
+			wdev->conn = NULL;
+			return -ENOMEM;
+		}
+	}
+
+	if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
+		wdev->conn->auto_auth = true;
+		/* start with open system ... should mostly work */
+		wdev->conn->params.auth_type =
+			NL80211_AUTHTYPE_OPEN_SYSTEM;
+	} else {
+		wdev->conn->auto_auth = false;
+	}
+
+	memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
+	wdev->ssid_len = connect->ssid_len;
+	wdev->conn->params.ssid = wdev->ssid;
+	wdev->conn->params.ssid_len = connect->ssid_len;
+
+	wdev->conn->connect_after_scan = false;
+
+	return 0;
+}
+
 int __cfg80211_connect(struct cfg80211_registered_device *rdev,
 		       struct net_device *dev,
 		       struct cfg80211_connect_params *connect,
@@ -710,46 +777,12 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
 		if (!rdev->ops->auth || !rdev->ops->assoc)
 			return -EOPNOTSUPP;
 
-		if (WARN_ON(wdev->conn))
-			return -EINPROGRESS;
-
-		wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
-		if (!wdev->conn)
-			return -ENOMEM;
-
 		/*
 		 * Copy all parameters, and treat explicitly IEs, BSSID, SSID.
 		 */
-		memcpy(&wdev->conn->params, connect, sizeof(*connect));
-		if (connect->bssid) {
-			wdev->conn->params.bssid = wdev->conn->bssid;
-			memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
-		}
-
-		if (connect->ie) {
-			wdev->conn->ie = kmemdup(connect->ie, connect->ie_len,
-						GFP_KERNEL);
-			wdev->conn->params.ie = wdev->conn->ie;
-			if (!wdev->conn->ie) {
-				kfree(wdev->conn);
-				wdev->conn = NULL;
-				return -ENOMEM;
-			}
-		}
-
-		if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
-			wdev->conn->auto_auth = true;
-			/* start with open system ... should mostly work */
-			wdev->conn->params.auth_type =
-				NL80211_AUTHTYPE_OPEN_SYSTEM;
-		} else {
-			wdev->conn->auto_auth = false;
-		}
-
-		memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
-		wdev->ssid_len = connect->ssid_len;
-		wdev->conn->params.ssid = wdev->ssid;
-		wdev->conn->params.ssid_len = connect->ssid_len;
+		err = cfg80211_conn_clone(wdev, connect);
+		if (err)
+			return err;
 
 		/* don't care about result -- but fill bssid & channel */
 		if (!wdev->conn->params.bssid || !wdev->conn->params.channel)
@@ -791,18 +824,55 @@ int __cfg80211_connect(struct cfg80211_registered_device *rdev,
 
 		return err;
 	} else {
+		struct cfg80211_bss *bss;
+
 		wdev->sme_state = CFG80211_SME_CONNECTING;
 		wdev->connect_keys = connkeys;
+
+		bss = cfg80211_get_bss(wdev->wiphy, NULL, connect->bssid,
+				       connect->ssid, connect->ssid_len,
+				       WLAN_CAPABILITY_ESS,
+				       WLAN_CAPABILITY_ESS);
+		if (!bss) {
+			/* We don't have a matching BSS.
+			 *
+			 * This means __connect_result will fail,
+			 * unless the driver provides scan results
+			 * between now and then. So do an explicit
+			 * scan, and try connect later.
+			 */
+			err = cfg80211_conn_clone(wdev, connect);
+			if (!err) {
+				wdev->conn->connect_after_scan = true;
+				err = cfg80211_conn_scan(wdev);
+
+				if (!err)
+					return 0;
+			}
+
+			/* Failed to clone (or scan), so we can't
+			 * delay the connect. Free everything up and
+			 * go ahead with the connect */
+			if (wdev->conn)
+				kfree(wdev->conn->ie);
+			kfree(wdev->conn);
+			wdev->conn = NULL;
+
+		} else {
+			cfg80211_put_bss(bss);
+
+			memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
+			wdev->ssid_len = connect->ssid_len;
+		}
+
 		err = rdev->ops->connect(&rdev->wiphy, dev, connect);
 		if (err) {
 			wdev->connect_keys = NULL;
 			wdev->sme_state = CFG80211_SME_IDLE;
+			wdev->ssid_len = 0;
 			return err;
 		}
 
-		memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
-		wdev->ssid_len = connect->ssid_len;
-
 		return 0;
 	}
 }
-- 
1.6.3.3

--
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