sme.c revision a9a11622c5c742c115fad371c0397ae86dd3bb67
1/*
2 * SME code for cfg80211's connect emulation.
3 *
4 * Copyright 2009	Johannes Berg <johannes@sipsolutions.net>
5 * Copyright (C) 2009   Intel Corporation. All rights reserved.
6 */
7
8#include <linux/etherdevice.h>
9#include <linux/if_arp.h>
10#include <linux/workqueue.h>
11#include <linux/wireless.h>
12#include <net/iw_handler.h>
13#include <net/cfg80211.h>
14#include <net/rtnetlink.h>
15#include "nl80211.h"
16
17struct cfg80211_conn {
18	struct cfg80211_connect_params params;
19	/* these are sub-states of the _CONNECTING sme_state */
20	enum {
21		CFG80211_CONN_IDLE,
22		CFG80211_CONN_SCANNING,
23		CFG80211_CONN_SCAN_AGAIN,
24		CFG80211_CONN_AUTHENTICATE_NEXT,
25		CFG80211_CONN_AUTHENTICATING,
26		CFG80211_CONN_ASSOCIATE_NEXT,
27		CFG80211_CONN_ASSOCIATING,
28	} state;
29	u8 bssid[ETH_ALEN];
30	u8 *ie;
31	size_t ie_len;
32	bool auto_auth;
33};
34
35
36static int cfg80211_conn_scan(struct wireless_dev *wdev)
37{
38	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
39	struct cfg80211_scan_request *request;
40	int n_channels, err;
41
42	ASSERT_RTNL();
43	ASSERT_RDEV_LOCK(rdev);
44	ASSERT_WDEV_LOCK(wdev);
45
46	if (rdev->scan_req)
47		return -EBUSY;
48
49	if (wdev->conn->params.channel) {
50		n_channels = 1;
51	} else {
52		enum ieee80211_band band;
53		n_channels = 0;
54
55		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
56			if (!wdev->wiphy->bands[band])
57				continue;
58			n_channels += wdev->wiphy->bands[band]->n_channels;
59		}
60	}
61	request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) +
62			  sizeof(request->channels[0]) * n_channels,
63			  GFP_KERNEL);
64	if (!request)
65		return -ENOMEM;
66
67	request->channels = (void *)((char *)request + sizeof(*request));
68	if (wdev->conn->params.channel)
69		request->channels[0] = wdev->conn->params.channel;
70	else {
71		int i = 0, j;
72		enum ieee80211_band band;
73
74		for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
75			if (!wdev->wiphy->bands[band])
76				continue;
77			for (j = 0; j < wdev->wiphy->bands[band]->n_channels;
78			     i++, j++)
79				request->channels[i] =
80					&wdev->wiphy->bands[band]->channels[j];
81		}
82	}
83	request->n_channels = n_channels;
84	request->ssids = (void *)(request->channels + n_channels);
85	request->n_ssids = 1;
86
87	memcpy(request->ssids[0].ssid, wdev->conn->params.ssid,
88		wdev->conn->params.ssid_len);
89	request->ssids[0].ssid_len = wdev->conn->params.ssid_len;
90
91	request->dev = wdev->netdev;
92	request->wiphy = &rdev->wiphy;
93
94	rdev->scan_req = request;
95
96	err = rdev->ops->scan(wdev->wiphy, wdev->netdev, request);
97	if (!err) {
98		wdev->conn->state = CFG80211_CONN_SCANNING;
99		nl80211_send_scan_start(rdev, wdev->netdev);
100		dev_hold(wdev->netdev);
101	} else {
102		rdev->scan_req = NULL;
103		kfree(request);
104	}
105	return err;
106}
107
108static int cfg80211_conn_do_work(struct wireless_dev *wdev)
109{
110	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
111	struct cfg80211_connect_params *params;
112	int err;
113
114	ASSERT_WDEV_LOCK(wdev);
115
116	if (!wdev->conn)
117		return 0;
118
119	params = &wdev->conn->params;
120
121	switch (wdev->conn->state) {
122	case CFG80211_CONN_SCAN_AGAIN:
123		return cfg80211_conn_scan(wdev);
124	case CFG80211_CONN_AUTHENTICATE_NEXT:
125		BUG_ON(!rdev->ops->auth);
126		wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
127		return __cfg80211_mlme_auth(rdev, wdev->netdev,
128					    params->channel, params->auth_type,
129					    params->bssid,
130					    params->ssid, params->ssid_len,
131					    NULL, 0,
132					    params->key, params->key_len,
133					    params->key_idx);
134	case CFG80211_CONN_ASSOCIATE_NEXT:
135		BUG_ON(!rdev->ops->assoc);
136		wdev->conn->state = CFG80211_CONN_ASSOCIATING;
137		/*
138		 * We could, later, implement roaming here and then actually
139		 * set prev_bssid to non-NULL. But then we need to be aware
140		 * that some APs don't like that -- so we'd need to retry
141		 * the association.
142		 */
143		err = __cfg80211_mlme_assoc(rdev, wdev->netdev,
144					    params->channel, params->bssid,
145					    NULL,
146					    params->ssid, params->ssid_len,
147					    params->ie, params->ie_len,
148					    false, &params->crypto);
149		if (err)
150			__cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
151					       NULL, 0,
152					       WLAN_REASON_DEAUTH_LEAVING);
153		return err;
154	default:
155		return 0;
156	}
157}
158
159void cfg80211_conn_work(struct work_struct *work)
160{
161	struct cfg80211_registered_device *rdev =
162		container_of(work, struct cfg80211_registered_device, conn_work);
163	struct wireless_dev *wdev;
164
165	rtnl_lock();
166	cfg80211_lock_rdev(rdev);
167	mutex_lock(&rdev->devlist_mtx);
168
169	list_for_each_entry(wdev, &rdev->netdev_list, list) {
170		wdev_lock(wdev);
171		if (!netif_running(wdev->netdev)) {
172			wdev_unlock(wdev);
173			continue;
174		}
175		if (wdev->sme_state != CFG80211_SME_CONNECTING) {
176			wdev_unlock(wdev);
177			continue;
178		}
179		if (cfg80211_conn_do_work(wdev))
180			__cfg80211_connect_result(
181					wdev->netdev,
182					wdev->conn->params.bssid,
183					NULL, 0, NULL, 0,
184					WLAN_STATUS_UNSPECIFIED_FAILURE,
185					false);
186		wdev_unlock(wdev);
187	}
188
189	mutex_unlock(&rdev->devlist_mtx);
190	cfg80211_unlock_rdev(rdev);
191	rtnl_unlock();
192}
193
194static bool cfg80211_get_conn_bss(struct wireless_dev *wdev)
195{
196	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
197	struct cfg80211_bss *bss;
198	u16 capa = WLAN_CAPABILITY_ESS;
199
200	ASSERT_WDEV_LOCK(wdev);
201
202	if (wdev->conn->params.privacy)
203		capa |= WLAN_CAPABILITY_PRIVACY;
204
205	bss = cfg80211_get_bss(wdev->wiphy, NULL, wdev->conn->params.bssid,
206			       wdev->conn->params.ssid,
207			       wdev->conn->params.ssid_len,
208			       WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY,
209			       capa);
210	if (!bss)
211		return false;
212
213	memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN);
214	wdev->conn->params.bssid = wdev->conn->bssid;
215	wdev->conn->params.channel = bss->channel;
216	wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
217	schedule_work(&rdev->conn_work);
218
219	cfg80211_put_bss(bss);
220	return true;
221}
222
223static void __cfg80211_sme_scan_done(struct net_device *dev)
224{
225	struct wireless_dev *wdev = dev->ieee80211_ptr;
226	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
227
228	ASSERT_WDEV_LOCK(wdev);
229
230	if (wdev->sme_state != CFG80211_SME_CONNECTING)
231		return;
232
233	if (!wdev->conn)
234		return;
235
236	if (wdev->conn->state != CFG80211_CONN_SCANNING &&
237	    wdev->conn->state != CFG80211_CONN_SCAN_AGAIN)
238		return;
239
240	if (!cfg80211_get_conn_bss(wdev)) {
241		/* not found */
242		if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)
243			schedule_work(&rdev->conn_work);
244		else
245			__cfg80211_connect_result(
246					wdev->netdev,
247					wdev->conn->params.bssid,
248					NULL, 0, NULL, 0,
249					WLAN_STATUS_UNSPECIFIED_FAILURE,
250					false);
251	}
252}
253
254void cfg80211_sme_scan_done(struct net_device *dev)
255{
256	struct wireless_dev *wdev = dev->ieee80211_ptr;
257
258	wdev_lock(wdev);
259	__cfg80211_sme_scan_done(dev);
260	wdev_unlock(wdev);
261}
262
263void cfg80211_sme_rx_auth(struct net_device *dev,
264			  const u8 *buf, size_t len)
265{
266	struct wireless_dev *wdev = dev->ieee80211_ptr;
267	struct wiphy *wiphy = wdev->wiphy;
268	struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
269	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
270	u16 status_code = le16_to_cpu(mgmt->u.auth.status_code);
271
272	ASSERT_WDEV_LOCK(wdev);
273
274	/* should only RX auth frames when connecting */
275	if (wdev->sme_state != CFG80211_SME_CONNECTING)
276		return;
277
278	if (WARN_ON(!wdev->conn))
279		return;
280
281	if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
282	    wdev->conn->auto_auth &&
283	    wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) {
284		/* select automatically between only open, shared, leap */
285		switch (wdev->conn->params.auth_type) {
286		case NL80211_AUTHTYPE_OPEN_SYSTEM:
287			if (wdev->connect_keys)
288				wdev->conn->params.auth_type =
289					NL80211_AUTHTYPE_SHARED_KEY;
290			else
291				wdev->conn->params.auth_type =
292					NL80211_AUTHTYPE_NETWORK_EAP;
293			break;
294		case NL80211_AUTHTYPE_SHARED_KEY:
295			wdev->conn->params.auth_type =
296				NL80211_AUTHTYPE_NETWORK_EAP;
297			break;
298		default:
299			/* huh? */
300			wdev->conn->params.auth_type =
301				NL80211_AUTHTYPE_OPEN_SYSTEM;
302			break;
303		}
304		wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
305		schedule_work(&rdev->conn_work);
306	} else if (status_code != WLAN_STATUS_SUCCESS) {
307		__cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0,
308					  status_code, false);
309	} else if (wdev->sme_state == CFG80211_SME_CONNECTING &&
310		 wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
311		wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
312		schedule_work(&rdev->conn_work);
313	}
314}
315
316void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
317			       const u8 *req_ie, size_t req_ie_len,
318			       const u8 *resp_ie, size_t resp_ie_len,
319			       u16 status, bool wextev)
320{
321	struct wireless_dev *wdev = dev->ieee80211_ptr;
322	struct cfg80211_bss *bss;
323#ifdef CONFIG_WIRELESS_EXT
324	union iwreq_data wrqu;
325#endif
326
327	ASSERT_WDEV_LOCK(wdev);
328
329	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
330		return;
331
332	if (wdev->sme_state == CFG80211_SME_CONNECTED)
333		nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev,
334				    bssid, req_ie, req_ie_len,
335				    resp_ie, resp_ie_len, GFP_KERNEL);
336	else
337		nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev,
338					    bssid, req_ie, req_ie_len,
339					    resp_ie, resp_ie_len,
340					    status, GFP_KERNEL);
341
342#ifdef CONFIG_WIRELESS_EXT
343	if (wextev) {
344		if (req_ie && status == WLAN_STATUS_SUCCESS) {
345			memset(&wrqu, 0, sizeof(wrqu));
346			wrqu.data.length = req_ie_len;
347			wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie);
348		}
349
350		if (resp_ie && status == WLAN_STATUS_SUCCESS) {
351			memset(&wrqu, 0, sizeof(wrqu));
352			wrqu.data.length = resp_ie_len;
353			wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie);
354		}
355
356		memset(&wrqu, 0, sizeof(wrqu));
357		wrqu.ap_addr.sa_family = ARPHRD_ETHER;
358		if (bssid && status == WLAN_STATUS_SUCCESS)
359			memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
360		wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
361	}
362#endif
363
364	if (status == WLAN_STATUS_SUCCESS &&
365	    wdev->sme_state == CFG80211_SME_IDLE)
366		goto success;
367
368	if (wdev->sme_state != CFG80211_SME_CONNECTING)
369		return;
370
371	if (wdev->current_bss) {
372		cfg80211_unhold_bss(wdev->current_bss);
373		cfg80211_put_bss(&wdev->current_bss->pub);
374		wdev->current_bss = NULL;
375	}
376
377	if (wdev->conn)
378		wdev->conn->state = CFG80211_CONN_IDLE;
379
380	if (status != WLAN_STATUS_SUCCESS) {
381		wdev->sme_state = CFG80211_SME_IDLE;
382		kfree(wdev->conn);
383		wdev->conn = NULL;
384		kfree(wdev->connect_keys);
385		wdev->connect_keys = NULL;
386		return;
387	}
388
389	bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
390			       wdev->ssid, wdev->ssid_len,
391			       WLAN_CAPABILITY_ESS,
392			       WLAN_CAPABILITY_ESS);
393
394	if (WARN_ON(!bss))
395		return;
396
397	cfg80211_hold_bss(bss_from_pub(bss));
398	wdev->current_bss = bss_from_pub(bss);
399
400 success:
401	wdev->sme_state = CFG80211_SME_CONNECTED;
402	cfg80211_upload_connect_keys(wdev);
403}
404
405void cfg80211_connect_result(struct net_device *dev, const u8 *bssid,
406			     const u8 *req_ie, size_t req_ie_len,
407			     const u8 *resp_ie, size_t resp_ie_len,
408			     u16 status, gfp_t gfp)
409{
410	struct wireless_dev *wdev = dev->ieee80211_ptr;
411	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
412	struct cfg80211_event *ev;
413	unsigned long flags;
414
415	ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
416	if (!ev)
417		return;
418
419	ev->type = EVENT_CONNECT_RESULT;
420	memcpy(ev->cr.bssid, bssid, ETH_ALEN);
421	ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev);
422	ev->cr.req_ie_len = req_ie_len;
423	memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len);
424	ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
425	ev->cr.resp_ie_len = resp_ie_len;
426	memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len);
427	ev->cr.status = status;
428
429	spin_lock_irqsave(&wdev->event_lock, flags);
430	list_add_tail(&ev->list, &wdev->event_list);
431	spin_unlock_irqrestore(&wdev->event_lock, flags);
432	schedule_work(&rdev->event_work);
433}
434EXPORT_SYMBOL(cfg80211_connect_result);
435
436void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid,
437		       const u8 *req_ie, size_t req_ie_len,
438		       const u8 *resp_ie, size_t resp_ie_len)
439{
440	struct cfg80211_bss *bss;
441#ifdef CONFIG_WIRELESS_EXT
442	union iwreq_data wrqu;
443#endif
444
445	ASSERT_WDEV_LOCK(wdev);
446
447	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
448		return;
449
450	if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED))
451		return;
452
453	/* internal error -- how did we get to CONNECTED w/o BSS? */
454	if (WARN_ON(!wdev->current_bss)) {
455		return;
456	}
457
458	cfg80211_unhold_bss(wdev->current_bss);
459	cfg80211_put_bss(&wdev->current_bss->pub);
460	wdev->current_bss = NULL;
461
462	bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid,
463			       wdev->ssid, wdev->ssid_len,
464			       WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS);
465
466	if (WARN_ON(!bss))
467		return;
468
469	cfg80211_hold_bss(bss_from_pub(bss));
470	wdev->current_bss = bss_from_pub(bss);
471
472	nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bssid,
473			    req_ie, req_ie_len, resp_ie, resp_ie_len,
474			    GFP_KERNEL);
475
476#ifdef CONFIG_WIRELESS_EXT
477	if (req_ie) {
478		memset(&wrqu, 0, sizeof(wrqu));
479		wrqu.data.length = req_ie_len;
480		wireless_send_event(wdev->netdev, IWEVASSOCREQIE,
481				    &wrqu, req_ie);
482	}
483
484	if (resp_ie) {
485		memset(&wrqu, 0, sizeof(wrqu));
486		wrqu.data.length = resp_ie_len;
487		wireless_send_event(wdev->netdev, IWEVASSOCRESPIE,
488				    &wrqu, resp_ie);
489	}
490
491	memset(&wrqu, 0, sizeof(wrqu));
492	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
493	memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN);
494	wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL);
495#endif
496}
497
498void cfg80211_roamed(struct net_device *dev, const u8 *bssid,
499		     const u8 *req_ie, size_t req_ie_len,
500		     const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
501{
502	struct wireless_dev *wdev = dev->ieee80211_ptr;
503	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
504	struct cfg80211_event *ev;
505	unsigned long flags;
506
507	ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp);
508	if (!ev)
509		return;
510
511	ev->type = EVENT_ROAMED;
512	memcpy(ev->rm.bssid, bssid, ETH_ALEN);
513	ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev);
514	ev->rm.req_ie_len = req_ie_len;
515	memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len);
516	ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len;
517	ev->rm.resp_ie_len = resp_ie_len;
518	memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len);
519
520	spin_lock_irqsave(&wdev->event_lock, flags);
521	list_add_tail(&ev->list, &wdev->event_list);
522	spin_unlock_irqrestore(&wdev->event_lock, flags);
523	schedule_work(&rdev->event_work);
524}
525EXPORT_SYMBOL(cfg80211_roamed);
526
527void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
528			     size_t ie_len, u16 reason, bool from_ap)
529{
530	struct wireless_dev *wdev = dev->ieee80211_ptr;
531	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
532	int i;
533#ifdef CONFIG_WIRELESS_EXT
534	union iwreq_data wrqu;
535#endif
536
537	ASSERT_WDEV_LOCK(wdev);
538
539	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION))
540		return;
541
542	if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED))
543		return;
544
545	if (wdev->current_bss) {
546		cfg80211_unhold_bss(wdev->current_bss);
547		cfg80211_put_bss(&wdev->current_bss->pub);
548	}
549
550	wdev->current_bss = NULL;
551	wdev->sme_state = CFG80211_SME_IDLE;
552
553	if (wdev->conn) {
554		kfree(wdev->conn->ie);
555		wdev->conn->ie = NULL;
556		kfree(wdev->conn);
557		wdev->conn = NULL;
558	}
559
560	nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);
561
562	/*
563	 * Delete all the keys ... pairwise keys can't really
564	 * exist any more anyway, but default keys might.
565	 */
566	if (rdev->ops->del_key)
567		for (i = 0; i < 6; i++)
568			rdev->ops->del_key(wdev->wiphy, dev, i, NULL);
569
570#ifdef CONFIG_WIRELESS_EXT
571	memset(&wrqu, 0, sizeof(wrqu));
572	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
573	wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
574#endif
575}
576
577void cfg80211_disconnected(struct net_device *dev, u16 reason,
578			   u8 *ie, size_t ie_len, gfp_t gfp)
579{
580	struct wireless_dev *wdev = dev->ieee80211_ptr;
581	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
582	struct cfg80211_event *ev;
583	unsigned long flags;
584
585	ev = kzalloc(sizeof(*ev) + ie_len, gfp);
586	if (!ev)
587		return;
588
589	ev->type = EVENT_DISCONNECTED;
590	ev->dc.ie = ((u8 *)ev) + sizeof(*ev);
591	ev->dc.ie_len = ie_len;
592	memcpy((void *)ev->dc.ie, ie, ie_len);
593	ev->dc.reason = reason;
594
595	spin_lock_irqsave(&wdev->event_lock, flags);
596	list_add_tail(&ev->list, &wdev->event_list);
597	spin_unlock_irqrestore(&wdev->event_lock, flags);
598	schedule_work(&rdev->event_work);
599}
600EXPORT_SYMBOL(cfg80211_disconnected);
601
602int __cfg80211_connect(struct cfg80211_registered_device *rdev,
603		       struct net_device *dev,
604		       struct cfg80211_connect_params *connect,
605		       struct cfg80211_cached_keys *connkeys)
606{
607	struct wireless_dev *wdev = dev->ieee80211_ptr;
608	int err;
609
610	ASSERT_WDEV_LOCK(wdev);
611
612	if (wdev->sme_state != CFG80211_SME_IDLE)
613		return -EALREADY;
614
615	if (WARN_ON(wdev->connect_keys)) {
616		kfree(wdev->connect_keys);
617		wdev->connect_keys = NULL;
618	}
619
620	if (connkeys && connkeys->def >= 0) {
621		int idx;
622
623		idx = connkeys->def;
624		/* If given a WEP key we may need it for shared key auth */
625		if (connkeys->params[idx].cipher == WLAN_CIPHER_SUITE_WEP40 ||
626		    connkeys->params[idx].cipher == WLAN_CIPHER_SUITE_WEP104) {
627			connect->key_idx = idx;
628			connect->key = connkeys->params[idx].key;
629			connect->key_len = connkeys->params[idx].key_len;
630		}
631	}
632
633	if (!rdev->ops->connect) {
634		if (!rdev->ops->auth || !rdev->ops->assoc)
635			return -EOPNOTSUPP;
636
637		if (WARN_ON(wdev->conn))
638			return -EINPROGRESS;
639
640		wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
641		if (!wdev->conn)
642			return -ENOMEM;
643
644		/*
645		 * Copy all parameters, and treat explicitly IEs, BSSID, SSID.
646		 */
647		memcpy(&wdev->conn->params, connect, sizeof(*connect));
648		if (connect->bssid) {
649			wdev->conn->params.bssid = wdev->conn->bssid;
650			memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
651		}
652
653		if (connect->ie) {
654			wdev->conn->ie = kmemdup(connect->ie, connect->ie_len,
655						GFP_KERNEL);
656			wdev->conn->params.ie = wdev->conn->ie;
657			if (!wdev->conn->ie) {
658				kfree(wdev->conn);
659				wdev->conn = NULL;
660				return -ENOMEM;
661			}
662		}
663
664		if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
665			wdev->conn->auto_auth = true;
666			/* start with open system ... should mostly work */
667			wdev->conn->params.auth_type =
668				NL80211_AUTHTYPE_OPEN_SYSTEM;
669		} else {
670			wdev->conn->auto_auth = false;
671		}
672
673		memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
674		wdev->ssid_len = connect->ssid_len;
675		wdev->conn->params.ssid = wdev->ssid;
676		wdev->conn->params.ssid_len = connect->ssid_len;
677
678		/* don't care about result -- but fill bssid & channel */
679		if (!wdev->conn->params.bssid || !wdev->conn->params.channel)
680			cfg80211_get_conn_bss(wdev);
681
682		wdev->sme_state = CFG80211_SME_CONNECTING;
683		wdev->connect_keys = connkeys;
684
685		/* we're good if we have both BSSID and channel */
686		if (wdev->conn->params.bssid && wdev->conn->params.channel) {
687			wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
688			err = cfg80211_conn_do_work(wdev);
689		} else {
690			/* otherwise we'll need to scan for the AP first */
691			err = cfg80211_conn_scan(wdev);
692			/*
693			 * If we can't scan right now, then we need to scan again
694			 * after the current scan finished, since the parameters
695			 * changed (unless we find a good AP anyway).
696			 */
697			if (err == -EBUSY) {
698				err = 0;
699				wdev->conn->state = CFG80211_CONN_SCAN_AGAIN;
700			}
701		}
702		if (err) {
703			kfree(wdev->conn);
704			wdev->conn = NULL;
705			wdev->sme_state = CFG80211_SME_IDLE;
706			wdev->connect_keys = NULL;
707		}
708
709		return err;
710	} else {
711		wdev->sme_state = CFG80211_SME_CONNECTING;
712		wdev->connect_keys = connkeys;
713		err = rdev->ops->connect(&rdev->wiphy, dev, connect);
714		if (err) {
715			wdev->connect_keys = NULL;
716			wdev->sme_state = CFG80211_SME_IDLE;
717			return err;
718		}
719
720		memcpy(wdev->ssid, connect->ssid, connect->ssid_len);
721		wdev->ssid_len = connect->ssid_len;
722
723		return 0;
724	}
725}
726
727int cfg80211_connect(struct cfg80211_registered_device *rdev,
728		     struct net_device *dev,
729		     struct cfg80211_connect_params *connect,
730		     struct cfg80211_cached_keys *connkeys)
731{
732	int err;
733
734	wdev_lock(dev->ieee80211_ptr);
735	err = __cfg80211_connect(rdev, dev, connect, connkeys);
736	wdev_unlock(dev->ieee80211_ptr);
737
738	return err;
739}
740
741int __cfg80211_disconnect(struct cfg80211_registered_device *rdev,
742			  struct net_device *dev, u16 reason, bool wextev)
743{
744	struct wireless_dev *wdev = dev->ieee80211_ptr;
745	int err;
746
747	ASSERT_WDEV_LOCK(wdev);
748
749	if (wdev->sme_state == CFG80211_SME_IDLE)
750		return -EINVAL;
751
752	kfree(wdev->connect_keys);
753	wdev->connect_keys = NULL;
754
755	if (!rdev->ops->disconnect) {
756		if (!rdev->ops->deauth)
757			return -EOPNOTSUPP;
758
759		/* was it connected by userspace SME? */
760		if (!wdev->conn) {
761			cfg80211_mlme_down(rdev, dev);
762			return 0;
763		}
764
765		if (wdev->sme_state == CFG80211_SME_CONNECTING &&
766		    (wdev->conn->state == CFG80211_CONN_SCANNING ||
767		     wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) {
768			wdev->sme_state = CFG80211_SME_IDLE;
769			kfree(wdev->conn);
770			wdev->conn = NULL;
771			return 0;
772		}
773
774		/* wdev->conn->params.bssid must be set if > SCANNING */
775		err = __cfg80211_mlme_deauth(rdev, dev,
776					     wdev->conn->params.bssid,
777					     NULL, 0, reason);
778		if (err)
779			return err;
780	} else {
781		err = rdev->ops->disconnect(&rdev->wiphy, dev, reason);
782		if (err)
783			return err;
784	}
785
786	if (wdev->sme_state == CFG80211_SME_CONNECTED)
787		__cfg80211_disconnected(dev, NULL, 0, 0, false);
788	else if (wdev->sme_state == CFG80211_SME_CONNECTING)
789		__cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0,
790					  WLAN_STATUS_UNSPECIFIED_FAILURE,
791					  wextev);
792
793	return 0;
794}
795
796int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
797			struct net_device *dev,
798			u16 reason, bool wextev)
799{
800	int err;
801
802	wdev_lock(dev->ieee80211_ptr);
803	err = __cfg80211_disconnect(rdev, dev, reason, wextev);
804	wdev_unlock(dev->ieee80211_ptr);
805
806	return err;
807}
808
809void cfg80211_sme_disassoc(struct net_device *dev, int idx)
810{
811	struct wireless_dev *wdev = dev->ieee80211_ptr;
812	struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
813	u8 bssid[ETH_ALEN];
814
815	ASSERT_WDEV_LOCK(wdev);
816
817	if (!wdev->conn)
818		return;
819
820	if (wdev->conn->state == CFG80211_CONN_IDLE)
821		return;
822
823	/*
824	 * Ok, so the association was made by this SME -- we don't
825	 * want it any more so deauthenticate too.
826	 */
827
828	if (!wdev->auth_bsses[idx])
829		return;
830
831	memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN);
832	if (__cfg80211_mlme_deauth(rdev, dev, bssid,
833				   NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) {
834		/* whatever -- assume gone anyway */
835		cfg80211_unhold_bss(wdev->auth_bsses[idx]);
836		cfg80211_put_bss(&wdev->auth_bsses[idx]->pub);
837		wdev->auth_bsses[idx] = NULL;
838	}
839}
840