diff mbox

[V2] nl80211 connect API support

Message ID 1250489084-13368-1-git-send-email-yi.zhu@intel.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Zhu Yi Aug. 17, 2009, 6:04 a.m. UTC
nl80211 driver connect API support.

Cc: Johannes Berg <johannes@sipsolutions.net>
Cc: Samuel Ortiz <sameo@linux.intel.com>
Signed-off-by: Zhu Yi <yi.zhu@intel.com>
---
V2: Use auth/assoc as the default operations if they are supported.

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

Comments

Jouni Malinen Sept. 3, 2009, 6:44 p.m. UTC | #1
On Mon, Aug 17, 2009 at 02:04:44PM +0800, Zhu Yi wrote:
> nl80211 driver connect API support.

> V2: Use auth/assoc as the default operations if they are supported.

Thanks!

It looks like there is still some misunderstanding regarding the SME
design in wpa_supplicant. It was designed to be used when the driver
does provide the separate authentication and association commands. There
is not much point in trying to convert the SME implementation to work
with the case where connect command is used. It is just duplicating
functionality that is already present in the old associate
(pre-auth/assoc SME case) operation.

I took about half of the patch, i.e., the changes to add driver
capability query, connect events, and actual connect/disconnect
functions. Instead of the changes in sme.c and driver interface, I glued
the new functions together within driver_nl80211.c since rest of
wpa_supplicant do not really need to (nor should) know about yet another
driver operation for connecting in a single step. The driver
capabilities are used to select which functions to use within
driver_nl80211.c. Since the WPA_DRIVER_FLAGS_SME is now set based on
driver capabilities, core wpa_supplicant code is able to switch between
the SME (separate auth/assoc calls) and single associate ("connect" in
nl80211 terminology) call.

I did not go through the details of sme_connect() function, but I would
expect that it was providing the same conversion from configuration to
driver parameters that was already done in wpa_supplicant.c. If there
was something else there, it should be added to the version in
wpa_supplicant.c instead of introducing a new, duplicated function.

It looks like the end result works fine at least with mac80211_hwsim
(with hardcoded driver capability detection that does not find the
separate authenticate command). This version is now in my git repository
and it would be good if you could verify that it works with your driver,
too.
Zhu Yi Sept. 4, 2009, 3:21 a.m. UTC | #2
On Fri, 2009-09-04 at 02:44 +0800, Jouni Malinen wrote:
> It looks like there is still some misunderstanding regarding the SME
> design in wpa_supplicant. It was designed to be used when the driver
> does provide the separate authentication and association commands. There
> is not much point in trying to convert the SME implementation to work
> with the case where connect command is used. It is just duplicating
> functionality that is already present in the old associate
> (pre-auth/assoc SME case) operation.

I searched WPA_DRIVER_FLAGS_SME is set only by driver_nl80211. So I
thought you might also want the connect API more visible for the whole
wpa_supplicant. But it turns I was wrong.

> I took about half of the patch, i.e., the changes to add driver
> capability query, connect events, and actual connect/disconnect
> functions. Instead of the changes in sme.c and driver interface, I glued
> the new functions together within driver_nl80211.c since rest of
> wpa_supplicant do not really need to (nor should) know about yet another
> driver operation for connecting in a single step. The driver
> capabilities are used to select which functions to use within
> driver_nl80211.c. Since the WPA_DRIVER_FLAGS_SME is now set based on
> driver capabilities, core wpa_supplicant code is able to switch between
> the SME (separate auth/assoc calls) and single associate ("connect" in
> nl80211 terminology) call.
> 
> I did not go through the details of sme_connect() function, but I would
> expect that it was providing the same conversion from configuration to
> driver parameters that was already done in wpa_supplicant.c. If there
> was something else there, it should be added to the version in
> wpa_supplicant.c instead of introducing a new, duplicated function.
> 
> It looks like the end result works fine at least with mac80211_hwsim
> (with hardcoded driver capability detection that does not find the
> separate authenticate command). This version is now in my git repository
> and it would be good if you could verify that it works with your driver,
> too.

I confirm it also works for iwmc3200wifi. Tested for WEP, PSK and EAP.

Thanks,
-yi

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

Patch

diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index b4c804e..e1bd487 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -679,4 +679,19 @@  enum {
 
 #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
 
+/* cipher suite selectors */
+#define WLAN_CIPHER_SUITE_USE_GROUP	0x000FAC00
+#define WLAN_CIPHER_SUITE_WEP40		0x000FAC01
+#define WLAN_CIPHER_SUITE_TKIP		0x000FAC02
+/* reserved: 				0x000FAC03 */
+#define WLAN_CIPHER_SUITE_CCMP		0x000FAC04
+#define WLAN_CIPHER_SUITE_WEP104	0x000FAC05
+#define WLAN_CIPHER_SUITE_AES_CMAC	0x000FAC06
+
+/* AKM suite selectors */
+#define WLAN_AKM_SUITE_8021X		0x000FAC01
+#define WLAN_AKM_SUITE_PSK		0x000FAC02
+
+#define WLAN_MAX_KEY_LEN		32
+
 #endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/nl80211_copy.h b/src/common/nl80211_copy.h
index dbea93b..c019a16 100644
--- a/src/common/nl80211_copy.h
+++ b/src/common/nl80211_copy.h
@@ -310,6 +310,14 @@  enum nl80211_commands {
 	NL80211_CMD_JOIN_IBSS,
 	NL80211_CMD_LEAVE_IBSS,
 
+	NL80211_CMD_TESTMODE,
+
+	NL80211_CMD_CONNECT,
+	NL80211_CMD_ROAM,
+	NL80211_CMD_DISCONNECT,
+
+	NL80211_CMD_SET_WIPHY_NETNS,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -619,6 +627,28 @@  enum nl80211_attrs {
 
 	NL80211_ATTR_CONTROL_PORT,
 
+	NL80211_ATTR_TESTDATA,
+
+	NL80211_ATTR_PRIVACY,
+
+	NL80211_ATTR_DISCONNECTED_BY_AP,
+	NL80211_ATTR_STATUS_CODE,
+
+	NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
+	NL80211_ATTR_CIPHER_SUITE_GROUP,
+	NL80211_ATTR_WPA_VERSIONS,
+	NL80211_ATTR_AKM_SUITES,
+
+	NL80211_ATTR_REQ_IE,
+	NL80211_ATTR_RESP_IE,
+
+	NL80211_ATTR_PREV_BSSID,
+
+	NL80211_ATTR_KEY,
+	NL80211_ATTR_KEYS,
+
+	NL80211_ATTR_PID,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -1224,4 +1254,39 @@  enum nl80211_mfp {
 	NL80211_MFP_REQUIRED,
 };
 
+enum nl80211_wpa_versions {
+	NL80211_WPA_VERSION_1 = 1 << 0,
+	NL80211_WPA_VERSION_2 = 1 << 1,
+};
+
+/**
+ * enum nl80211_key_attributes - key attributes
+ * @__NL80211_KEY_INVALID: invalid
+ * @NL80211_KEY_DATA: (temporal) key data; for TKIP this consists of
+ *	16 bytes encryption key followed by 8 bytes each for TX and RX MIC
+ *	keys
+ * @NL80211_KEY_IDX: key ID (u8, 0-3)
+ * @NL80211_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11
+ *	section 7.3.2.25.1, e.g. 0x000FAC04)
+ * @NL80211_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and
+ *	CCMP keys, each six bytes in little endian
+ * @NL80211_KEY_DEFAULT: flag indicating default key
+ * @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key
+ * @__NL80211_KEY_AFTER_LAST: internal
+ * @NL80211_KEY_MAX: highest key attribute
+ */
+enum nl80211_key_attributes {
+	__NL80211_KEY_INVALID,
+	NL80211_KEY_DATA,
+	NL80211_KEY_IDX,
+	NL80211_KEY_CIPHER,
+	NL80211_KEY_SEQ,
+	NL80211_KEY_DEFAULT,
+	NL80211_KEY_DEFAULT_MGMT,
+
+	/* keep last */
+	__NL80211_KEY_AFTER_LAST,
+	NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1
+};
+
 #endif /* __LINUX_NL80211_H */
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 2ae5b1a..fcb602b 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -441,9 +441,11 @@  struct wpa_driver_capa {
 #define WPA_DRIVER_FLAGS_WIRED		0x00000010
 /* Driver provides separate commands for authentication and association (SME in
  * wpa_supplicant). */
-#define WPA_DRIVER_FLAGS_SME		0x00000020
+#define WPA_DRIVER_FLAGS_SME_AUTH	0x00000020
 /* Driver supports AP mode */
 #define WPA_DRIVER_FLAGS_AP		0x00000040
+/* Driver supports connect API */
+#define WPA_DRIVER_FLAGS_SME_CONNECT	0x00000080
 	unsigned int flags;
 
 	int max_scan_ssids;
@@ -1333,6 +1335,21 @@  struct wpa_driver_ops {
 	 * Returns: 0 on success, -1 on failure
 	 */
 	int (*set_supp_port)(void *priv, int authorized);
+
+	/**
+	 * connect - Request driver to connect
+	 * @priv: private driver interface data
+	 * @params: connect parameters
+	 * Returns: 0 on success, -1 on failure
+	 *
+	 * This is an optional function that can be used with drivers that
+	 * support connect (both authentication and association) API. If
+	 * driver SME is not supported (both connect and authenticate are
+	 * not implemented), associate() function is expected to take care
+	 * of IEEE 802.11 authentication and association, too.
+	 */
+	int (*connect)(void *priv, struct wpa_driver_associate_params *params);
+	int (*disconnect)(void *priv, u8 *addr, int reason_code);
 };
 
 /**
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 9b2bf7c..c032ddc 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -739,6 +739,40 @@  static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
 	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
 }
 
+static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
+			       enum nl80211_commands cmd, struct nlattr *status,
+			       struct nlattr *addr, struct nlattr *req_ie,
+			       struct nlattr *resp_ie)
+{
+	union wpa_event_data event;
+
+	os_memset(&event, 0, sizeof(event));
+	if (cmd == NL80211_CMD_CONNECT &&
+	    nla_get_u16(status) != WLAN_STATUS_SUCCESS) {
+		if (resp_ie) {
+			event.assoc_reject.resp_ies = nla_data(resp_ie);
+			event.assoc_reject.resp_ies_len = nla_len(resp_ie);
+		}
+		event.assoc_reject.status_code = nla_get_u16(status);
+		wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
+		return;
+	}
+
+	drv->associated = 1;
+	if (addr)
+		os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN);
+
+	if (req_ie) {
+		event.assoc_info.req_ies = nla_data(req_ie);
+		event.assoc_info.req_ies_len = nla_len(req_ie);
+	}
+	if (resp_ie) {
+		event.assoc_info.resp_ies = nla_data(resp_ie);
+		event.assoc_info.resp_ies_len = nla_len(resp_ie);
+	}
+
+	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+}
 
 static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
 			       enum nl80211_commands cmd, struct nlattr *addr)
@@ -895,6 +929,17 @@  static int process_event(struct nl_msg *msg, void *arg)
 		mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME],
 			   tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT]);
 		break;
+	case NL80211_CMD_CONNECT:
+	case NL80211_CMD_ROAM:
+		mlme_event_connect(drv, gnlh->cmd, tb[NL80211_ATTR_STATUS_CODE],
+				   tb[NL80211_ATTR_MAC],
+				   tb[NL80211_ATTR_REQ_IE],
+				   tb[NL80211_ATTR_RESP_IE]);
+		break;
+	case NL80211_CMD_DISCONNECT:
+		drv->associated = 0;
+		wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+		break;
 #endif /* HOSTAPD */
 	case NL80211_CMD_MICHAEL_MIC_FAILURE:
 		mlme_event_michael_mic_failure(drv, tb);
@@ -1003,6 +1048,8 @@  nla_put_failure:
 struct wiphy_info_data {
 	int max_scan_ssids;
 	int ap_supported;
+	int auth_supported;
+	int connect_supported;
 };
 
 
@@ -1031,6 +1078,19 @@  static int wiphy_info_handler(struct nl_msg *msg, void *arg)
 		}
 	}
 
+	if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) {
+		struct nlattr *nl_cmd;
+		int i;
+
+		nla_for_each_nested(nl_cmd,
+				    tb[NL80211_ATTR_SUPPORTED_COMMANDS], i) {
+			if (nla_get_u32(nl_cmd) == NL80211_CMD_AUTHENTICATE)
+				info->auth_supported = 1;
+			if (nla_get_u32(nl_cmd) == NL80211_CMD_CONNECT)
+				info->connect_supported = 1;
+		}
+	}
+
 	return NL_SKIP;
 }
 
@@ -1078,6 +1138,12 @@  static void wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
 	drv->capa.max_scan_ssids = info.max_scan_ssids;
 	if (info.ap_supported)
 		drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
+
+	if (info.auth_supported)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_SME_AUTH;
+
+	if (info.connect_supported)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_SME_CONNECT;
 }
 #endif /* HOSTAPD */
 
@@ -1227,8 +1293,6 @@  static void * wpa_driver_nl80211_init(void *ctx, const char *ifname)
 		return NULL;
 	}
 
-	drv->capa.flags |= WPA_DRIVER_FLAGS_SME;
-
 	drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
 	if (drv->ioctl_sock < 0) {
 		perror("socket(PF_INET,SOCK_DGRAM)");
@@ -1658,19 +1722,22 @@  static int nl_set_encr(int ifindex, struct wpa_driver_nl80211_data *drv,
 		case WPA_ALG_WEP:
 			if (key_len == 5)
 				NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
-					    0x000FAC01);
+					    WLAN_CIPHER_SUITE_WEP40);
 			else
 				NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
-					    0x000FAC05);
+					    WLAN_CIPHER_SUITE_WEP104);
 			break;
 		case WPA_ALG_TKIP:
-			NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC02);
+			NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+				    WLAN_CIPHER_SUITE_TKIP);
 			break;
 		case WPA_ALG_CCMP:
-			NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC04);
+			NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+				    WLAN_CIPHER_SUITE_CCMP);
 			break;
 		case WPA_ALG_IGTK:
-			NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC06);
+			NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+				    WLAN_CIPHER_SUITE_AES_CMAC);
 			break;
 		default:
 			wpa_printf(MSG_ERROR, "%s: Unsupported encryption "
@@ -1737,6 +1804,60 @@  nla_put_failure:
 
 #ifndef HOSTAPD
 
+static int nl80211_set_conn_keys(void *priv,
+				  struct wpa_driver_associate_params *params,
+				  struct nl_msg *msg)
+{
+	int i, privacy = 0;
+	struct nlattr *nl_keys, *nl_key;
+
+	for (i = 0; i < 4; i++) {
+		if (!params->wep_key[i])
+			continue;
+		privacy = 1;
+		break;
+	}
+	if (!privacy)
+		return 0;
+
+	NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY);
+
+	nl_keys = nla_nest_start(msg, NL80211_ATTR_KEYS);
+	if (!nl_keys)
+		goto nla_put_failure;
+
+	for (i = 0; i < 4; i++) {
+		if (!params->wep_key[i])
+			continue;
+
+		nl_key = nla_nest_start(msg, i);
+		if (!nl_key)
+			goto nla_put_failure;
+
+		NLA_PUT(msg, NL80211_KEY_DATA, params->wep_key_len[i],
+			params->wep_key[i]);
+		if (params->wep_key_len[i] == 5)
+			NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+				    WLAN_CIPHER_SUITE_WEP40);
+		else
+			NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+				    WLAN_CIPHER_SUITE_WEP104);
+
+		NLA_PUT_U8(msg, NL80211_KEY_IDX, i);
+
+		if (i == params->wep_tx_keyidx)
+			NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT);
+
+		nla_nest_end(msg, nl_key);
+	}
+	nla_nest_end(msg, nl_keys);
+
+	return 0;
+
+nla_put_failure:
+	return -ENOBUFS;
+}
+
 static int wpa_driver_nl80211_set_key(void *priv, wpa_alg alg,
 				      const u8 *addr, int key_idx,
 				      int set_tx, const u8 *seq,
@@ -1883,6 +2004,164 @@  nla_put_failure:
 	return ret;
 }
 
+static int wpa_driver_nl80211_connect(
+	void *priv, struct wpa_driver_associate_params *params)
+{
+	struct wpa_driver_nl80211_data *drv = priv;
+	struct nl_msg *msg;
+	enum nl80211_auth_type type;
+	int ret = 0;
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME_CONNECT))
+		return -EOPNOTSUPP;
+
+	msg = nlmsg_alloc();
+	if (!msg)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
+	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+		    NL80211_CMD_CONNECT, 0);
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+	if (params->bssid) {
+		wpa_printf(MSG_DEBUG, "  * bssid=" MACSTR,
+			   MAC2STR(params->bssid));
+		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
+	}
+	if (params->freq) {
+		wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
+		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
+	}
+	if (params->ssid) {
+		wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
+				  params->ssid, params->ssid_len);
+		NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
+			params->ssid);
+		if (params->ssid_len > sizeof(drv->ssid))
+			goto nla_put_failure;
+		os_memcpy(drv->ssid, params->ssid, params->ssid_len);
+		drv->ssid_len = params->ssid_len;
+	}
+	wpa_hexdump(MSG_DEBUG, "  * IEs", params->wpa_ie, params->wpa_ie_len);
+	if (params->wpa_ie)
+		NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+			params->wpa_ie);
+
+	if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM)
+		type = NL80211_AUTHTYPE_OPEN_SYSTEM;
+	else if (params->auth_alg & AUTH_ALG_SHARED_KEY)
+		type = NL80211_AUTHTYPE_SHARED_KEY;
+	else if (params->auth_alg & AUTH_ALG_LEAP)
+		type = NL80211_AUTHTYPE_NETWORK_EAP;
+	else if (params->auth_alg & AUTH_ALG_FT)
+		type = NL80211_AUTHTYPE_FT;
+	else
+		goto nla_put_failure;
+
+	wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
+	NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type);
+
+	if (params->wpa_ie && params->wpa_ie_len) {
+		enum nl80211_wpa_versions ver;
+
+		if (params->wpa_ie[0] == WLAN_EID_RSN)
+			ver = NL80211_WPA_VERSION_2;
+		else
+			ver = NL80211_WPA_VERSION_1;
+
+		wpa_printf(MSG_DEBUG, "  * WPA Version %d", ver);
+		NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver);
+	}
+
+	if (params->pairwise_suite != CIPHER_NONE) {
+		int cipher = IW_AUTH_CIPHER_NONE;
+
+		switch (params->pairwise_suite) {
+		case CIPHER_WEP40:
+			cipher = WLAN_CIPHER_SUITE_WEP40;
+			break;
+		case CIPHER_WEP104:
+			cipher = WLAN_CIPHER_SUITE_WEP104;
+			break;
+		case CIPHER_CCMP:
+			cipher = WLAN_CIPHER_SUITE_CCMP;
+			break;
+		case CIPHER_TKIP:
+		default:
+			cipher = WLAN_CIPHER_SUITE_TKIP;
+			break;
+		}
+		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher);
+	}
+
+	if (params->group_suite != CIPHER_NONE) {
+		int cipher = IW_AUTH_CIPHER_NONE;
+
+		switch (params->group_suite) {
+		case CIPHER_WEP40:
+			cipher = WLAN_CIPHER_SUITE_WEP40;
+			break;
+		case CIPHER_WEP104:
+			cipher = WLAN_CIPHER_SUITE_WEP104;
+			break;
+		case CIPHER_CCMP:
+			cipher = WLAN_CIPHER_SUITE_CCMP;
+			break;
+		case CIPHER_TKIP:
+		default:
+			cipher = WLAN_CIPHER_SUITE_TKIP;
+			break;
+		}
+		NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher);
+	}
+
+	if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
+	    params->key_mgmt_suite == KEY_MGMT_PSK) {
+		int mgmt = WLAN_AKM_SUITE_PSK;
+
+		switch (params->key_mgmt_suite) {
+		case KEY_MGMT_802_1X:
+			mgmt = WLAN_AKM_SUITE_8021X;
+			break;
+		case KEY_MGMT_PSK:
+		default:
+			mgmt = WLAN_AKM_SUITE_PSK;
+			break;
+		}
+		NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt);
+	}
+
+	ret = nl80211_set_conn_keys(drv, params, msg);
+	if (ret)
+		goto nla_put_failure;
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
+			   "(%s)", ret, strerror(-ret));
+		goto nla_put_failure;
+	}
+	ret = 0;
+	wpa_printf(MSG_DEBUG, "nl80211: Connect request send successfully");
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+
+}
+
+static int wpa_driver_nl80211_disconnect(void *priv, u8 *addr, int reason_code)
+{
+	struct wpa_driver_nl80211_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+	drv->associated = 0;
+	return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISCONNECT,
+				       reason_code);
+}
+
 #endif /* HOSTAPD */
 
 
@@ -4060,6 +4339,8 @@  const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.get_capa = wpa_driver_nl80211_get_capa,
 	.set_operstate = wpa_driver_nl80211_set_operstate,
 	.set_supp_port = wpa_driver_nl80211_set_supp_port,
+	.connect = wpa_driver_nl80211_connect,
+	.disconnect = wpa_driver_nl80211_disconnect,
 #endif /* HOSTAPD */
 	.set_country = wpa_driver_nl80211_set_country,
 	.set_mode = wpa_driver_nl80211_set_mode,
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 4cb5372..e5a8ef4 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -448,4 +448,22 @@  static inline int wpa_drv_set_supp_port(struct wpa_supplicant *wpa_s,
 	return 0;
 }
 
+static inline int wpa_drv_connect(struct wpa_supplicant *wpa_s,
+				  struct wpa_driver_associate_params *params)
+{
+	if (wpa_s->driver->connect)
+		return wpa_s->driver->connect(wpa_s->drv_priv, params);
+
+	return -1;
+}
+
+static inline int wpa_drv_disconnect(struct wpa_supplicant *wpa_s, u8 *addr,
+				     int reason_code)
+{
+	if (wpa_s->driver->disconnect)
+		return wpa_s->driver->disconnect(wpa_s->drv_priv, addr,
+						 reason_code);
+	return -1;
+}
+
 #endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 0d729ef..426afd2 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -238,6 +238,175 @@  void sme_authenticate(struct wpa_supplicant *wpa_s,
 	 */
 }
 
+void sme_connect(struct wpa_supplicant *wpa_s, struct wpa_scan_res *bss,
+		 struct wpa_ssid *ssid)
+{
+	struct wpa_driver_associate_params params;
+	const u8 *ie;
+	int i;
+
+	if (bss == NULL) {
+		wpa_printf(MSG_ERROR, "SME: No scan result available for the "
+			   "network");
+		return;
+	}
+
+	os_memset(&params, 0, sizeof(params));
+	wpa_s->reassociate = 0;
+
+	params.freq = bss->freq;
+	params.bssid = bss->bssid;
+	ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
+	if (ie == NULL) {
+		wpa_printf(MSG_ERROR, "SME: SSID not available for the BSS");
+		return;
+	}
+	params.ssid = ie + 2;
+	params.ssid_len = ie[1];
+
+	wpa_s->sme.freq = params.freq;
+	os_memcpy(wpa_s->sme.ssid, params.ssid, params.ssid_len);
+	wpa_s->sme.ssid_len = params.ssid_len;
+
+	params.auth_alg = AUTH_ALG_OPEN_SYSTEM;
+#ifdef IEEE8021X_EAPOL
+	if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+		if (ssid->leap) {
+			if (ssid->non_leap == 0)
+				params.auth_alg = AUTH_ALG_LEAP;
+			else
+				params.auth_alg |= AUTH_ALG_LEAP;
+		}
+	}
+#endif /* IEEE8021X_EAPOL */
+	wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x",
+		   params.auth_alg);
+	if (ssid->auth_alg) {
+		params.auth_alg = 0;
+		if (ssid->auth_alg & WPA_AUTH_ALG_OPEN)
+			params.auth_alg |= AUTH_ALG_OPEN_SYSTEM;
+		if (ssid->auth_alg & WPA_AUTH_ALG_SHARED)
+			params.auth_alg |= AUTH_ALG_SHARED_KEY;
+		if (ssid->auth_alg & WPA_AUTH_ALG_LEAP)
+			params.auth_alg |= AUTH_ALG_LEAP;
+		wpa_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x",
+			   params.auth_alg);
+	}
+
+	for (i = 0; i < NUM_WEP_KEYS; i++) {
+		if (ssid->wep_key_len[i])
+			params.wep_key[i] = ssid->wep_key[i];
+		params.wep_key_len[i] = ssid->wep_key_len[i];
+	}
+	params.wep_tx_keyidx = ssid->wep_tx_keyidx;
+
+	os_memset(wpa_s->bssid, 0, ETH_ALEN);
+	os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
+
+	if (bss && (wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) ||
+		    wpa_scan_get_ie(bss, WLAN_EID_RSN)) &&
+	    (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK |
+			       WPA_KEY_MGMT_FT_IEEE8021X |
+			       WPA_KEY_MGMT_FT_PSK |
+			       WPA_KEY_MGMT_IEEE8021X_SHA256 |
+			       WPA_KEY_MGMT_PSK_SHA256))) {
+		int try_opportunistic;
+		try_opportunistic = ssid->proactive_key_caching &&
+			(ssid->proto & WPA_PROTO_RSN);
+		if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+					    wpa_s->current_ssid,
+					    try_opportunistic) == 0)
+			eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
+		wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
+		if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
+					      wpa_s->sme.assoc_req_ie,
+					      &wpa_s->sme.assoc_req_ie_len)) {
+			wpa_printf(MSG_WARNING, "SME: Failed to set WPA key "
+				   "management and encryption suites");
+			return;
+		}
+	} else if (ssid->key_mgmt &
+		   (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X |
+		    WPA_KEY_MGMT_WPA_NONE | WPA_KEY_MGMT_FT_PSK |
+		    WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_PSK_SHA256 |
+		    WPA_KEY_MGMT_IEEE8021X_SHA256)) {
+		wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
+		if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
+					      wpa_s->sme.assoc_req_ie,
+					      &wpa_s->sme.assoc_req_ie_len)) {
+			wpa_printf(MSG_WARNING, "SME: Failed to set WPA key "
+				   "management and encryption suites (no scan "
+				   "results)");
+			return;
+		}
+#ifdef CONFIG_WPS
+	} else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+		struct wpabuf *wps_ie;
+		wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid));
+		if (wps_ie && wpabuf_len(wps_ie) <=
+		    sizeof(wpa_s->sme.assoc_req_ie)) {
+			wpa_s->sme.assoc_req_ie_len = wpabuf_len(wps_ie);
+			os_memcpy(wpa_s->sme.assoc_req_ie, wpabuf_head(wps_ie),
+				  wpa_s->sme.assoc_req_ie_len);
+		} else
+			wpa_s->sme.assoc_req_ie_len = 0;
+		wpabuf_free(wps_ie);
+		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+#endif /* CONFIG_WPS */
+	} else {
+		wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+		wpa_s->sme.assoc_req_ie_len = 0;
+	}
+
+#ifdef CONFIG_IEEE80211W
+	switch (ssid->ieee80211w) {
+	case NO_IEEE80211W:
+		wpa_s->sme.mfp = NO_MGMT_FRAME_PROTECTION;
+		break;
+	case IEEE80211W_OPTIONAL:
+		wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_OPTIONAL;
+		break;
+	case IEEE80211W_REQUIRED:
+		wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_REQUIRED;
+		break;
+	}
+	if (ssid->ieee80211w != NO_IEEE80211W && bss) {
+		const u8 *rsn = wpa_scan_get_ie(bss, WLAN_EID_RSN);
+		struct wpa_ie_data _ie;
+		if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &_ie) == 0 &&
+		    _ie.capabilities &
+		    (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) {
+			wpa_printf(MSG_DEBUG, "WPA: Selected AP supports MFP: "
+				   "require MFP");
+			wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_REQUIRED;
+		}
+	}
+#endif /* CONFIG_IEEE80211W */
+
+	wpa_supplicant_cancel_scan(wpa_s);
+
+	wpa_msg(wpa_s, MSG_INFO, "Trying to connect with " MACSTR
+		" (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
+		wpa_ssid_txt(params.ssid, params.ssid_len), params.freq);
+
+	wpa_clear_keys(wpa_s, bss->bssid);
+	wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
+	wpa_s->current_ssid = ssid;
+	wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
+	wpa_supplicant_initiate_eapol(wpa_s);
+
+	params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt);
+	params.wpa_ie = wpa_s->sme.assoc_req_ie;
+	params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
+	params.pairwise_suite = cipher_suite2driver(wpa_s->pairwise_cipher);
+	params.group_suite = cipher_suite2driver(wpa_s->group_cipher);
+
+	if (wpa_drv_connect(wpa_s, &params) < 0) {
+		wpa_msg(wpa_s, MSG_INFO, "Connect to the driver failed");
+		return;
+	}
+}
+
 
 void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
 {
diff --git a/wpa_supplicant/sme.h b/wpa_supplicant/sme.h
index 2780041..fd5ea9c 100644
--- a/wpa_supplicant/sme.h
+++ b/wpa_supplicant/sme.h
@@ -19,6 +19,8 @@ 
 
 void sme_authenticate(struct wpa_supplicant *wpa_s,
 		      struct wpa_scan_res *bss, struct wpa_ssid *ssid);
+void sme_connect(struct wpa_supplicant *wpa_s, struct wpa_scan_res *bss,
+		 struct wpa_ssid *ssid);
 void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data);
 int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
 		      const u8 *ies, size_t ies_len);
@@ -37,6 +39,12 @@  static inline void sme_authenticate(struct wpa_supplicant *wpa_s,
 {
 }
 
+static inline void sme_connect(struct wpa_supplicant *wpa_s,
+			       struct wpa_scan_res *bss,
+			       struct wpa_ssid *ssid)
+{
+}
+
 static inline void sme_event_auth(struct wpa_supplicant *wpa_s,
 				  union wpa_event_data *data)
 {
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index f1f929a..4282b14 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -640,7 +640,7 @@  static void wpa_supplicant_reconfig(int sig, void *eloop_ctx,
 }
 
 
-static wpa_cipher cipher_suite2driver(int cipher)
+wpa_cipher cipher_suite2driver(int cipher)
 {
 	switch (cipher) {
 	case WPA_CIPHER_NONE:
@@ -658,7 +658,7 @@  static wpa_cipher cipher_suite2driver(int cipher)
 }
 
 
-static wpa_key_mgmt key_mgmt2driver(int key_mgmt)
+wpa_key_mgmt key_mgmt2driver(int key_mgmt)
 {
 	switch (key_mgmt) {
 	case WPA_KEY_MGMT_NONE:
@@ -965,11 +965,16 @@  void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
 		return;
 	}
 
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) {
+	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME_AUTH) {
 		sme_authenticate(wpa_s, bss, ssid);
 		return;
 	}
 
+	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME_CONNECT) {
+		sme_connect(wpa_s, bss, ssid);
+		return;
+	}
+
 	wpa_s->reassociate = 0;
 	if (bss) {
 #ifdef CONFIG_IEEE80211R
@@ -1303,9 +1308,12 @@  void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
 	if (!is_zero_ether_addr(wpa_s->bssid)) {
 		if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
 			ieee80211_sta_deauthenticate(wpa_s, reason_code);
-		else
+		else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME_AUTH)
 			wpa_drv_deauthenticate(wpa_s, wpa_s->bssid,
 					       reason_code);
+		else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME_CONNECT)
+			wpa_drv_disconnect(wpa_s, wpa_s->bssid, reason_code);
+
 		addr = wpa_s->bssid;
 	}
 	wpa_clear_keys(wpa_s, addr);
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 63984d8..13896d9 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -392,6 +392,8 @@  int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s);
 
 const char * wpa_supplicant_state_txt(int state);
 int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s);
+wpa_cipher cipher_suite2driver(int cipher);
+wpa_key_mgmt key_mgmt2driver(int key_mgmt);
 int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 			      struct wpa_scan_res *bss,
 			      struct wpa_ssid *ssid,
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index b1f8bf8..3cfcd32 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -478,7 +478,7 @@  static int wpa_supplicant_update_ft_ies(void *ctx, const u8 *md,
 	struct wpa_supplicant *wpa_s = ctx;
 	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
 		return ieee80211_sta_update_ft_ies(wpa_s, md, ies, ies_len);
-	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+	if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME_AUTH)
 		return sme_update_ft_ies(wpa_s, md, ies, ies_len);
 	return wpa_drv_update_ft_ies(wpa_s, md, ies, ies_len);
 }