diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-07-02 03:13:27 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-07-10 15:01:51 -0400 |
commit | 6829c878ecd24ff0ae41b4668c7e9d0f11b66942 (patch) | |
tree | acf78b685d60694040953b4f61d768b95b79e45d | |
parent | b23aa676ab9d54469cda9f7151f51a2851c6f36e (diff) |
cfg80211: emulate connect with auth/assoc
This adds code to cfg80211 so that drivers (mac80211 right
now) that don't implement connect but rather auth/assoc can
still be used with the nl80211 connect command. This will
also be necessary for the wext compat code.
Signed-off-by: Samuel Ortiz <samuel.ortiz@intel.com>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | include/net/cfg80211.h | 6 | ||||
-rw-r--r-- | net/wireless/core.c | 7 | ||||
-rw-r--r-- | net/wireless/core.h | 8 | ||||
-rw-r--r-- | net/wireless/mlme.c | 79 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 4 | ||||
-rw-r--r-- | net/wireless/scan.c | 7 | ||||
-rw-r--r-- | net/wireless/sme.c | 395 |
7 files changed, 474 insertions, 32 deletions
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 68e11321ed74..24fab439d415 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h | |||
@@ -1209,6 +1209,9 @@ extern void wiphy_unregister(struct wiphy *wiphy); | |||
1209 | */ | 1209 | */ |
1210 | extern void wiphy_free(struct wiphy *wiphy); | 1210 | extern void wiphy_free(struct wiphy *wiphy); |
1211 | 1211 | ||
1212 | /* internal struct */ | ||
1213 | struct cfg80211_conn; | ||
1214 | |||
1212 | /** | 1215 | /** |
1213 | * struct wireless_dev - wireless per-netdev state | 1216 | * struct wireless_dev - wireless per-netdev state |
1214 | * | 1217 | * |
@@ -1242,9 +1245,10 @@ struct wireless_dev { | |||
1242 | u8 ssid_len; | 1245 | u8 ssid_len; |
1243 | enum { | 1246 | enum { |
1244 | CFG80211_SME_IDLE, | 1247 | CFG80211_SME_IDLE, |
1245 | CFG80211_SME_CONNECTING, /* ->connect called */ | 1248 | CFG80211_SME_CONNECTING, |
1246 | CFG80211_SME_CONNECTED, | 1249 | CFG80211_SME_CONNECTED, |
1247 | } sme_state; | 1250 | } sme_state; |
1251 | struct cfg80211_conn *conn; | ||
1248 | 1252 | ||
1249 | #ifdef CONFIG_WIRELESS_EXT | 1253 | #ifdef CONFIG_WIRELESS_EXT |
1250 | /* wext data */ | 1254 | /* wext data */ |
diff --git a/net/wireless/core.c b/net/wireless/core.c index 314e00f70e3b..a0a679704612 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -321,6 +321,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) | |||
321 | } | 321 | } |
322 | 322 | ||
323 | INIT_WORK(&drv->rfkill_sync, cfg80211_rfkill_sync_work); | 323 | INIT_WORK(&drv->rfkill_sync, cfg80211_rfkill_sync_work); |
324 | INIT_WORK(&drv->conn_work, cfg80211_conn_work); | ||
324 | 325 | ||
325 | /* | 326 | /* |
326 | * Initialize wiphy parameters to IEEE 802.11 MIB default values. | 327 | * Initialize wiphy parameters to IEEE 802.11 MIB default values. |
@@ -481,6 +482,8 @@ void wiphy_unregister(struct wiphy *wiphy) | |||
481 | /* unlock again before freeing */ | 482 | /* unlock again before freeing */ |
482 | mutex_unlock(&drv->mtx); | 483 | mutex_unlock(&drv->mtx); |
483 | 484 | ||
485 | cancel_work_sync(&drv->conn_work); | ||
486 | |||
484 | cfg80211_debugfs_drv_del(drv); | 487 | cfg80211_debugfs_drv_del(drv); |
485 | 488 | ||
486 | /* If this device got a regulatory hint tell core its | 489 | /* If this device got a regulatory hint tell core its |
@@ -569,6 +572,10 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
569 | break; | 572 | break; |
570 | } | 573 | } |
571 | break; | 574 | break; |
575 | case NETDEV_DOWN: | ||
576 | kfree(wdev->conn); | ||
577 | wdev->conn = NULL; | ||
578 | break; | ||
572 | case NETDEV_UP: | 579 | case NETDEV_UP: |
573 | #ifdef CONFIG_WIRELESS_EXT | 580 | #ifdef CONFIG_WIRELESS_EXT |
574 | if (wdev->iftype != NL80211_IFTYPE_ADHOC) | 581 | if (wdev->iftype != NL80211_IFTYPE_ADHOC) |
diff --git a/net/wireless/core.h b/net/wireless/core.h index f93f96f85d2b..2c0f64252f3d 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -62,6 +62,8 @@ struct cfg80211_registered_device { | |||
62 | struct genl_info *testmode_info; | 62 | struct genl_info *testmode_info; |
63 | #endif | 63 | #endif |
64 | 64 | ||
65 | struct work_struct conn_work; | ||
66 | |||
65 | #ifdef CONFIG_CFG80211_DEBUGFS | 67 | #ifdef CONFIG_CFG80211_DEBUGFS |
66 | /* Debugfs entries */ | 68 | /* Debugfs entries */ |
67 | struct wiphy_debugfsdentries { | 69 | struct wiphy_debugfsdentries { |
@@ -181,8 +183,14 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
181 | int cfg80211_disconnect(struct cfg80211_registered_device *rdev, | 183 | int cfg80211_disconnect(struct cfg80211_registered_device *rdev, |
182 | struct net_device *dev, u16 reason); | 184 | struct net_device *dev, u16 reason); |
183 | 185 | ||
186 | void cfg80211_conn_work(struct work_struct *work); | ||
187 | |||
184 | /* internal helpers */ | 188 | /* internal helpers */ |
185 | int cfg80211_validate_key_settings(struct key_params *params, int key_idx, | 189 | int cfg80211_validate_key_settings(struct key_params *params, int key_idx, |
186 | const u8 *mac_addr); | 190 | const u8 *mac_addr); |
191 | void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, | ||
192 | size_t ie_len, u16 reason, bool from_ap); | ||
193 | void cfg80211_sme_scan_done(struct net_device *dev); | ||
194 | void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); | ||
187 | 195 | ||
188 | #endif /* __NET_WIRELESS_CORE_H */ | 196 | #endif /* __NET_WIRELESS_CORE_H */ |
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index c4e6d4b84a46..3427fe73d3c3 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c | |||
@@ -16,58 +16,105 @@ void cfg80211_send_rx_auth(struct net_device *dev, const u8 *buf, size_t len, gf | |||
16 | { | 16 | { |
17 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | 17 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; |
18 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 18 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
19 | |||
19 | nl80211_send_rx_auth(rdev, dev, buf, len, gfp); | 20 | nl80211_send_rx_auth(rdev, dev, buf, len, gfp); |
21 | cfg80211_sme_rx_auth(dev, buf, len); | ||
20 | } | 22 | } |
21 | EXPORT_SYMBOL(cfg80211_send_rx_auth); | 23 | EXPORT_SYMBOL(cfg80211_send_rx_auth); |
22 | 24 | ||
23 | void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) | 25 | void cfg80211_send_rx_assoc(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) |
24 | { | 26 | { |
25 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | 27 | u16 status_code; |
28 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
29 | struct wiphy *wiphy = wdev->wiphy; | ||
26 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 30 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
31 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; | ||
32 | u8 *ie = mgmt->u.assoc_resp.variable; | ||
33 | int ieoffs = offsetof(struct ieee80211_mgmt, u.assoc_resp.variable); | ||
34 | |||
35 | status_code = le16_to_cpu(mgmt->u.assoc_resp.status_code); | ||
36 | |||
27 | nl80211_send_rx_assoc(rdev, dev, buf, len, gfp); | 37 | nl80211_send_rx_assoc(rdev, dev, buf, len, gfp); |
38 | |||
39 | cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, ie, len - ieoffs, | ||
40 | status_code, gfp); | ||
28 | } | 41 | } |
29 | EXPORT_SYMBOL(cfg80211_send_rx_assoc); | 42 | EXPORT_SYMBOL(cfg80211_send_rx_assoc); |
30 | 43 | ||
31 | void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) | 44 | void cfg80211_send_deauth(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) |
32 | { | 45 | { |
33 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | 46 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
47 | struct wiphy *wiphy = wdev->wiphy; | ||
34 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 48 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
49 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; | ||
50 | |||
35 | nl80211_send_deauth(rdev, dev, buf, len, gfp); | 51 | nl80211_send_deauth(rdev, dev, buf, len, gfp); |
52 | |||
53 | if (wdev->sme_state == CFG80211_SME_CONNECTED) { | ||
54 | u16 reason_code; | ||
55 | bool from_ap; | ||
56 | |||
57 | reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); | ||
58 | |||
59 | from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; | ||
60 | __cfg80211_disconnected(dev, gfp, NULL, 0, | ||
61 | reason_code, from_ap); | ||
62 | |||
63 | wdev->sme_state = CFG80211_SME_IDLE; | ||
64 | } else if (wdev->sme_state == CFG80211_SME_CONNECTING) { | ||
65 | cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, | ||
66 | WLAN_STATUS_UNSPECIFIED_FAILURE, gfp); | ||
67 | } | ||
36 | } | 68 | } |
37 | EXPORT_SYMBOL(cfg80211_send_deauth); | 69 | EXPORT_SYMBOL(cfg80211_send_deauth); |
38 | 70 | ||
39 | void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) | 71 | void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len, gfp_t gfp) |
40 | { | 72 | { |
41 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | 73 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
74 | struct wiphy *wiphy = wdev->wiphy; | ||
42 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 75 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
76 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; | ||
77 | |||
43 | nl80211_send_disassoc(rdev, dev, buf, len, gfp); | 78 | nl80211_send_disassoc(rdev, dev, buf, len, gfp); |
44 | } | ||
45 | EXPORT_SYMBOL(cfg80211_send_disassoc); | ||
46 | 79 | ||
47 | static void cfg80211_wext_disconnected(struct net_device *dev) | 80 | if (wdev->sme_state == CFG80211_SME_CONNECTED) { |
48 | { | 81 | u16 reason_code; |
49 | #ifdef CONFIG_WIRELESS_EXT | 82 | bool from_ap; |
50 | union iwreq_data wrqu; | 83 | |
51 | memset(&wrqu, 0, sizeof(wrqu)); | 84 | reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); |
52 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); | 85 | |
53 | #endif | 86 | from_ap = memcmp(mgmt->da, dev->dev_addr, ETH_ALEN) == 0; |
87 | __cfg80211_disconnected(dev, gfp, NULL, 0, | ||
88 | reason_code, from_ap); | ||
89 | |||
90 | wdev->sme_state = CFG80211_SME_IDLE; | ||
91 | } | ||
54 | } | 92 | } |
93 | EXPORT_SYMBOL(cfg80211_send_disassoc); | ||
55 | 94 | ||
56 | void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr, gfp_t gfp) | 95 | void cfg80211_send_auth_timeout(struct net_device *dev, const u8 *addr, gfp_t gfp) |
57 | { | 96 | { |
58 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | 97 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
98 | struct wiphy *wiphy = wdev->wiphy; | ||
59 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 99 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
60 | nl80211_send_auth_timeout(rdev, dev, addr, gfp); | 100 | nl80211_send_auth_timeout(rdev, dev, addr, gfp); |
61 | cfg80211_wext_disconnected(dev); | 101 | if (wdev->sme_state == CFG80211_SME_CONNECTING) |
102 | cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, | ||
103 | WLAN_STATUS_UNSPECIFIED_FAILURE, gfp); | ||
104 | wdev->sme_state = CFG80211_SME_IDLE; | ||
62 | } | 105 | } |
63 | EXPORT_SYMBOL(cfg80211_send_auth_timeout); | 106 | EXPORT_SYMBOL(cfg80211_send_auth_timeout); |
64 | 107 | ||
65 | void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr, gfp_t gfp) | 108 | void cfg80211_send_assoc_timeout(struct net_device *dev, const u8 *addr, gfp_t gfp) |
66 | { | 109 | { |
67 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | 110 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
111 | struct wiphy *wiphy = wdev->wiphy; | ||
68 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 112 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
69 | nl80211_send_assoc_timeout(rdev, dev, addr, gfp); | 113 | nl80211_send_assoc_timeout(rdev, dev, addr, gfp); |
70 | cfg80211_wext_disconnected(dev); | 114 | if (wdev->sme_state == CFG80211_SME_CONNECTING) |
115 | cfg80211_connect_result(dev, addr, NULL, 0, NULL, 0, | ||
116 | WLAN_STATUS_UNSPECIFIED_FAILURE, gfp); | ||
117 | wdev->sme_state = CFG80211_SME_IDLE; | ||
71 | } | 118 | } |
72 | EXPORT_SYMBOL(cfg80211_send_assoc_timeout); | 119 | EXPORT_SYMBOL(cfg80211_send_assoc_timeout); |
73 | 120 | ||
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 89dd3793e03c..89aa9e781d10 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -351,12 +351,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
351 | 351 | ||
352 | #undef CMD | 352 | #undef CMD |
353 | 353 | ||
354 | if (dev->ops->connect) { | 354 | if (dev->ops->connect || dev->ops->auth) { |
355 | i++; | 355 | i++; |
356 | NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT); | 356 | NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT); |
357 | } | 357 | } |
358 | 358 | ||
359 | if (dev->ops->disconnect) { | 359 | if (dev->ops->disconnect || dev->ops->deauth) { |
360 | i++; | 360 | i++; |
361 | NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT); | 361 | NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT); |
362 | } | 362 | } |
diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 261a06386822..82b33e708488 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c | |||
@@ -30,6 +30,13 @@ void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) | |||
30 | 30 | ||
31 | WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); | 31 | WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); |
32 | 32 | ||
33 | /* | ||
34 | * This must be before sending the other events! | ||
35 | * Otherwise, wpa_supplicant gets completely confused with | ||
36 | * wext events. | ||
37 | */ | ||
38 | cfg80211_sme_scan_done(dev); | ||
39 | |||
33 | if (aborted) | 40 | if (aborted) |
34 | nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev); | 41 | nl80211_send_scan_aborted(wiphy_to_dev(request->wiphy), dev); |
35 | else | 42 | else |
diff --git a/net/wireless/sme.c b/net/wireless/sme.c index fc117031d0bb..3abb04729873 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c | |||
@@ -12,6 +12,266 @@ | |||
12 | #include <net/rtnetlink.h> | 12 | #include <net/rtnetlink.h> |
13 | #include "nl80211.h" | 13 | #include "nl80211.h" |
14 | 14 | ||
15 | struct cfg80211_conn { | ||
16 | struct cfg80211_connect_params params; | ||
17 | /* these are sub-states of the _CONNECTING sme_state */ | ||
18 | enum { | ||
19 | CFG80211_CONN_IDLE, | ||
20 | CFG80211_CONN_SCANNING, | ||
21 | CFG80211_CONN_SCAN_AGAIN, | ||
22 | CFG80211_CONN_AUTHENTICATE_NEXT, | ||
23 | CFG80211_CONN_AUTHENTICATING, | ||
24 | CFG80211_CONN_ASSOCIATE_NEXT, | ||
25 | CFG80211_CONN_ASSOCIATING, | ||
26 | } state; | ||
27 | u8 bssid[ETH_ALEN]; | ||
28 | u8 *ie; | ||
29 | size_t ie_len; | ||
30 | bool auto_auth; | ||
31 | }; | ||
32 | |||
33 | |||
34 | static int cfg80211_conn_scan(struct wireless_dev *wdev) | ||
35 | { | ||
36 | struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); | ||
37 | struct cfg80211_scan_request *request; | ||
38 | int n_channels, err; | ||
39 | |||
40 | ASSERT_RTNL(); | ||
41 | |||
42 | if (drv->scan_req) | ||
43 | return -EBUSY; | ||
44 | |||
45 | if (wdev->conn->params.channel) { | ||
46 | n_channels = 1; | ||
47 | } else { | ||
48 | enum ieee80211_band band; | ||
49 | n_channels = 0; | ||
50 | |||
51 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | ||
52 | if (!wdev->wiphy->bands[band]) | ||
53 | continue; | ||
54 | n_channels += wdev->wiphy->bands[band]->n_channels; | ||
55 | } | ||
56 | } | ||
57 | request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) + | ||
58 | sizeof(request->channels[0]) * n_channels, | ||
59 | GFP_KERNEL); | ||
60 | if (!request) | ||
61 | return -ENOMEM; | ||
62 | |||
63 | request->channels = (void *)((char *)request + sizeof(*request)); | ||
64 | if (wdev->conn->params.channel) | ||
65 | request->channels[0] = wdev->conn->params.channel; | ||
66 | else { | ||
67 | int i = 0, j; | ||
68 | enum ieee80211_band band; | ||
69 | |||
70 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | ||
71 | if (!wdev->wiphy->bands[band]) | ||
72 | continue; | ||
73 | for (j = 0; j < wdev->wiphy->bands[band]->n_channels; | ||
74 | i++, j++) | ||
75 | request->channels[i] = | ||
76 | &wdev->wiphy->bands[band]->channels[j]; | ||
77 | } | ||
78 | } | ||
79 | request->n_channels = n_channels; | ||
80 | request->ssids = (void *)(request->channels + n_channels); | ||
81 | request->n_ssids = 1; | ||
82 | |||
83 | memcpy(request->ssids[0].ssid, wdev->conn->params.ssid, | ||
84 | wdev->conn->params.ssid_len); | ||
85 | request->ssids[0].ssid_len = wdev->conn->params.ssid_len; | ||
86 | |||
87 | request->ifidx = wdev->netdev->ifindex; | ||
88 | request->wiphy = &drv->wiphy; | ||
89 | |||
90 | drv->scan_req = request; | ||
91 | |||
92 | err = drv->ops->scan(wdev->wiphy, wdev->netdev, request); | ||
93 | if (!err) { | ||
94 | wdev->conn->state = CFG80211_CONN_SCANNING; | ||
95 | nl80211_send_scan_start(drv, wdev->netdev); | ||
96 | } else { | ||
97 | drv->scan_req = NULL; | ||
98 | kfree(request); | ||
99 | } | ||
100 | return err; | ||
101 | } | ||
102 | |||
103 | static int cfg80211_conn_do_work(struct wireless_dev *wdev) | ||
104 | { | ||
105 | struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); | ||
106 | union { | ||
107 | struct cfg80211_auth_request auth_req; | ||
108 | struct cfg80211_assoc_request assoc_req; | ||
109 | } u; | ||
110 | |||
111 | memset(&u, 0, sizeof(u)); | ||
112 | |||
113 | if (!wdev->conn) | ||
114 | return 0; | ||
115 | |||
116 | switch (wdev->conn->state) { | ||
117 | case CFG80211_CONN_SCAN_AGAIN: | ||
118 | return cfg80211_conn_scan(wdev); | ||
119 | case CFG80211_CONN_AUTHENTICATE_NEXT: | ||
120 | u.auth_req.chan = wdev->conn->params.channel; | ||
121 | u.auth_req.peer_addr = wdev->conn->params.bssid; | ||
122 | u.auth_req.ssid = wdev->conn->params.ssid; | ||
123 | u.auth_req.ssid_len = wdev->conn->params.ssid_len; | ||
124 | u.auth_req.auth_type = wdev->conn->params.auth_type; | ||
125 | u.auth_req.ie = NULL; | ||
126 | u.auth_req.ie_len = 0; | ||
127 | wdev->conn->state = CFG80211_CONN_AUTHENTICATING; | ||
128 | BUG_ON(!drv->ops->auth); | ||
129 | return drv->ops->auth(wdev->wiphy, wdev->netdev, &u.auth_req); | ||
130 | case CFG80211_CONN_ASSOCIATE_NEXT: | ||
131 | u.assoc_req.chan = wdev->conn->params.channel; | ||
132 | u.assoc_req.peer_addr = wdev->conn->params.bssid; | ||
133 | u.assoc_req.ssid = wdev->conn->params.ssid; | ||
134 | u.assoc_req.ssid_len = wdev->conn->params.ssid_len; | ||
135 | u.assoc_req.ie = wdev->conn->params.ie; | ||
136 | u.assoc_req.ie_len = wdev->conn->params.ie_len; | ||
137 | u.assoc_req.use_mfp = false; | ||
138 | memcpy(&u.assoc_req.crypto, &wdev->conn->params.crypto, | ||
139 | sizeof(u.assoc_req.crypto)); | ||
140 | wdev->conn->state = CFG80211_CONN_ASSOCIATING; | ||
141 | BUG_ON(!drv->ops->assoc); | ||
142 | return drv->ops->assoc(wdev->wiphy, wdev->netdev, | ||
143 | &u.assoc_req); | ||
144 | default: | ||
145 | return 0; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | void cfg80211_conn_work(struct work_struct *work) | ||
150 | { | ||
151 | struct cfg80211_registered_device *drv = | ||
152 | container_of(work, struct cfg80211_registered_device, conn_work); | ||
153 | struct wireless_dev *wdev; | ||
154 | |||
155 | rtnl_lock(); | ||
156 | mutex_lock(&drv->devlist_mtx); | ||
157 | |||
158 | list_for_each_entry(wdev, &drv->netdev_list, list) { | ||
159 | if (!netif_running(wdev->netdev)) | ||
160 | continue; | ||
161 | if (wdev->sme_state != CFG80211_SME_CONNECTING) | ||
162 | continue; | ||
163 | if (cfg80211_conn_do_work(wdev)) | ||
164 | cfg80211_connect_result(wdev->netdev, | ||
165 | wdev->conn->params.bssid, | ||
166 | NULL, 0, NULL, 0, | ||
167 | WLAN_STATUS_UNSPECIFIED_FAILURE, | ||
168 | GFP_ATOMIC); | ||
169 | } | ||
170 | |||
171 | mutex_unlock(&drv->devlist_mtx); | ||
172 | rtnl_unlock(); | ||
173 | } | ||
174 | |||
175 | static bool cfg80211_get_conn_bss(struct wireless_dev *wdev) | ||
176 | { | ||
177 | struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); | ||
178 | struct cfg80211_bss *bss; | ||
179 | u16 capa = WLAN_CAPABILITY_ESS; | ||
180 | |||
181 | if (wdev->conn->params.privacy) | ||
182 | capa |= WLAN_CAPABILITY_PRIVACY; | ||
183 | |||
184 | bss = cfg80211_get_bss(wdev->wiphy, NULL, wdev->conn->params.bssid, | ||
185 | wdev->conn->params.ssid, | ||
186 | wdev->conn->params.ssid_len, | ||
187 | WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, | ||
188 | capa); | ||
189 | |||
190 | if (!bss) | ||
191 | return false; | ||
192 | |||
193 | memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN); | ||
194 | wdev->conn->params.bssid = wdev->conn->bssid; | ||
195 | wdev->conn->params.channel = bss->channel; | ||
196 | wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; | ||
197 | schedule_work(&drv->conn_work); | ||
198 | |||
199 | cfg80211_put_bss(bss); | ||
200 | return true; | ||
201 | } | ||
202 | |||
203 | void cfg80211_sme_scan_done(struct net_device *dev) | ||
204 | { | ||
205 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
206 | struct cfg80211_registered_device *drv = wiphy_to_dev(wdev->wiphy); | ||
207 | |||
208 | if (wdev->sme_state != CFG80211_SME_CONNECTING) | ||
209 | return; | ||
210 | |||
211 | if (WARN_ON(!wdev->conn)) | ||
212 | return; | ||
213 | |||
214 | if (wdev->conn->state != CFG80211_CONN_SCANNING && | ||
215 | wdev->conn->state != CFG80211_CONN_SCAN_AGAIN) | ||
216 | return; | ||
217 | |||
218 | if (!cfg80211_get_conn_bss(wdev)) { | ||
219 | /* not found */ | ||
220 | if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) | ||
221 | schedule_work(&drv->conn_work); | ||
222 | else | ||
223 | cfg80211_connect_result(dev, wdev->conn->params.bssid, | ||
224 | NULL, 0, NULL, 0, | ||
225 | WLAN_STATUS_UNSPECIFIED_FAILURE, | ||
226 | GFP_ATOMIC); | ||
227 | return; | ||
228 | } | ||
229 | } | ||
230 | |||
231 | void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len) | ||
232 | { | ||
233 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
234 | struct wiphy *wiphy = wdev->wiphy; | ||
235 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | ||
236 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; | ||
237 | u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); | ||
238 | |||
239 | /* should only RX auth frames when connecting */ | ||
240 | if (wdev->sme_state != CFG80211_SME_CONNECTING) | ||
241 | return; | ||
242 | |||
243 | if (WARN_ON(!wdev->conn)) | ||
244 | return; | ||
245 | |||
246 | if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && | ||
247 | wdev->conn->auto_auth && | ||
248 | wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) { | ||
249 | /* select automatically between only open, shared, leap */ | ||
250 | switch (wdev->conn->params.auth_type) { | ||
251 | case NL80211_AUTHTYPE_OPEN_SYSTEM: | ||
252 | wdev->conn->params.auth_type = | ||
253 | NL80211_AUTHTYPE_SHARED_KEY; | ||
254 | break; | ||
255 | case NL80211_AUTHTYPE_SHARED_KEY: | ||
256 | wdev->conn->params.auth_type = | ||
257 | NL80211_AUTHTYPE_NETWORK_EAP; | ||
258 | break; | ||
259 | default: | ||
260 | /* huh? */ | ||
261 | wdev->conn->params.auth_type = | ||
262 | NL80211_AUTHTYPE_OPEN_SYSTEM; | ||
263 | break; | ||
264 | } | ||
265 | wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; | ||
266 | schedule_work(&rdev->conn_work); | ||
267 | } else if (status_code != WLAN_STATUS_SUCCESS) | ||
268 | wdev->sme_state = CFG80211_SME_IDLE; | ||
269 | else if (wdev->sme_state == CFG80211_SME_CONNECTING && | ||
270 | wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { | ||
271 | wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; | ||
272 | schedule_work(&rdev->conn_work); | ||
273 | } | ||
274 | } | ||
15 | 275 | ||
16 | void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | 276 | void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, |
17 | const u8 *req_ie, size_t req_ie_len, | 277 | const u8 *req_ie, size_t req_ie_len, |
@@ -27,7 +287,7 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
27 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) | 287 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) |
28 | return; | 288 | return; |
29 | 289 | ||
30 | if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING)) | 290 | if (wdev->sme_state != CFG80211_SME_CONNECTING) |
31 | return; | 291 | return; |
32 | 292 | ||
33 | if (wdev->current_bss) { | 293 | if (wdev->current_bss) { |
@@ -53,6 +313,9 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
53 | wdev->sme_state = CFG80211_SME_IDLE; | 313 | wdev->sme_state = CFG80211_SME_IDLE; |
54 | } | 314 | } |
55 | 315 | ||
316 | if (wdev->conn) | ||
317 | wdev->conn->state = CFG80211_CONN_IDLE; | ||
318 | |||
56 | nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid, | 319 | nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid, |
57 | req_ie, req_ie_len, resp_ie, resp_ie_len, | 320 | req_ie, req_ie_len, resp_ie, resp_ie_len, |
58 | status, gfp); | 321 | status, gfp); |
@@ -72,7 +335,7 @@ void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | |||
72 | 335 | ||
73 | memset(&wrqu, 0, sizeof(wrqu)); | 336 | memset(&wrqu, 0, sizeof(wrqu)); |
74 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | 337 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; |
75 | if (bssid) | 338 | if (bssid && status == WLAN_STATUS_SUCCESS) |
76 | memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); | 339 | memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); |
77 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); | 340 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); |
78 | #endif | 341 | #endif |
@@ -138,9 +401,8 @@ void cfg80211_roamed(struct net_device *dev, const u8 *bssid, | |||
138 | } | 401 | } |
139 | EXPORT_SYMBOL(cfg80211_roamed); | 402 | EXPORT_SYMBOL(cfg80211_roamed); |
140 | 403 | ||
141 | static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, | 404 | void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, u8 *ie, |
142 | u8 *ie, size_t ie_len, u16 reason, | 405 | size_t ie_len, u16 reason, bool from_ap) |
143 | bool from_ap) | ||
144 | { | 406 | { |
145 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 407 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
146 | #ifdef CONFIG_WIRELESS_EXT | 408 | #ifdef CONFIG_WIRELESS_EXT |
@@ -161,6 +423,11 @@ static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, | |||
161 | wdev->current_bss = NULL; | 423 | wdev->current_bss = NULL; |
162 | wdev->sme_state = CFG80211_SME_IDLE; | 424 | wdev->sme_state = CFG80211_SME_IDLE; |
163 | 425 | ||
426 | if (wdev->conn) { | ||
427 | kfree(wdev->conn->ie); | ||
428 | wdev->conn->ie = NULL; | ||
429 | } | ||
430 | |||
164 | nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev, | 431 | nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev, |
165 | reason, ie, ie_len, from_ap, gfp); | 432 | reason, ie, ie_len, from_ap, gfp); |
166 | 433 | ||
@@ -174,7 +441,7 @@ static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, | |||
174 | void cfg80211_disconnected(struct net_device *dev, u16 reason, | 441 | void cfg80211_disconnected(struct net_device *dev, u16 reason, |
175 | u8 *ie, size_t ie_len, gfp_t gfp) | 442 | u8 *ie, size_t ie_len, gfp_t gfp) |
176 | { | 443 | { |
177 | __cfg80211_disconnected(dev, reason, ie, ie_len, true, gfp); | 444 | __cfg80211_disconnected(dev, gfp, ie, ie_len, reason, true); |
178 | } | 445 | } |
179 | EXPORT_SYMBOL(cfg80211_disconnected); | 446 | EXPORT_SYMBOL(cfg80211_disconnected); |
180 | 447 | ||
@@ -189,7 +456,74 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
189 | return -EALREADY; | 456 | return -EALREADY; |
190 | 457 | ||
191 | if (!rdev->ops->connect) { | 458 | if (!rdev->ops->connect) { |
192 | return -EOPNOTSUPP; | 459 | if (!rdev->ops->auth || !rdev->ops->assoc) |
460 | return -EOPNOTSUPP; | ||
461 | |||
462 | if (!wdev->conn) { | ||
463 | wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); | ||
464 | if (!wdev->conn) | ||
465 | return -ENOMEM; | ||
466 | } else | ||
467 | memset(wdev->conn, 0, sizeof(*wdev->conn)); | ||
468 | |||
469 | /* | ||
470 | * Copy all parameters, and treat explicitly IEs, BSSID, SSID. | ||
471 | */ | ||
472 | memcpy(&wdev->conn->params, connect, sizeof(*connect)); | ||
473 | if (connect->bssid) { | ||
474 | wdev->conn->params.bssid = wdev->conn->bssid; | ||
475 | memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); | ||
476 | } | ||
477 | |||
478 | if (connect->ie) { | ||
479 | wdev->conn->ie = kmemdup(connect->ie, connect->ie_len, | ||
480 | GFP_KERNEL); | ||
481 | wdev->conn->params.ie = wdev->conn->ie; | ||
482 | if (!wdev->conn->ie) | ||
483 | return -ENOMEM; | ||
484 | } | ||
485 | |||
486 | if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { | ||
487 | wdev->conn->auto_auth = true; | ||
488 | /* start with open system ... should mostly work */ | ||
489 | wdev->conn->params.auth_type = | ||
490 | NL80211_AUTHTYPE_OPEN_SYSTEM; | ||
491 | } else { | ||
492 | wdev->conn->auto_auth = false; | ||
493 | } | ||
494 | |||
495 | memcpy(wdev->ssid, connect->ssid, connect->ssid_len); | ||
496 | wdev->ssid_len = connect->ssid_len; | ||
497 | wdev->conn->params.ssid = wdev->ssid; | ||
498 | wdev->conn->params.ssid_len = connect->ssid_len; | ||
499 | |||
500 | /* don't care about result -- but fill bssid & channel */ | ||
501 | if (!wdev->conn->params.bssid || !wdev->conn->params.channel) | ||
502 | cfg80211_get_conn_bss(wdev); | ||
503 | |||
504 | wdev->sme_state = CFG80211_SME_CONNECTING; | ||
505 | |||
506 | /* we're good if we have both BSSID and channel */ | ||
507 | if (wdev->conn->params.bssid && wdev->conn->params.channel) { | ||
508 | wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; | ||
509 | err = cfg80211_conn_do_work(wdev); | ||
510 | } else { | ||
511 | /* otherwise we'll need to scan for the AP first */ | ||
512 | err = cfg80211_conn_scan(wdev); | ||
513 | /* | ||
514 | * If we can't scan right now, then we need to scan again | ||
515 | * after the current scan finished, since the parameters | ||
516 | * changed (unless we find a good AP anyway). | ||
517 | */ | ||
518 | if (err == -EBUSY) { | ||
519 | err = 0; | ||
520 | wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; | ||
521 | } | ||
522 | } | ||
523 | if (err) | ||
524 | wdev->sme_state = CFG80211_SME_IDLE; | ||
525 | |||
526 | return err; | ||
193 | } else { | 527 | } else { |
194 | wdev->sme_state = CFG80211_SME_CONNECTING; | 528 | wdev->sme_state = CFG80211_SME_CONNECTING; |
195 | err = rdev->ops->connect(&rdev->wiphy, dev, connect); | 529 | err = rdev->ops->connect(&rdev->wiphy, dev, connect); |
@@ -197,28 +531,63 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev, | |||
197 | wdev->sme_state = CFG80211_SME_IDLE; | 531 | wdev->sme_state = CFG80211_SME_IDLE; |
198 | return err; | 532 | return err; |
199 | } | 533 | } |
200 | } | ||
201 | 534 | ||
202 | memcpy(wdev->ssid, connect->ssid, connect->ssid_len); | 535 | memcpy(wdev->ssid, connect->ssid, connect->ssid_len); |
203 | wdev->ssid_len = connect->ssid_len; | 536 | wdev->ssid_len = connect->ssid_len; |
204 | 537 | ||
205 | return 0; | 538 | return 0; |
539 | } | ||
206 | } | 540 | } |
207 | 541 | ||
208 | int cfg80211_disconnect(struct cfg80211_registered_device *rdev, | 542 | int cfg80211_disconnect(struct cfg80211_registered_device *rdev, |
209 | struct net_device *dev, u16 reason) | 543 | struct net_device *dev, u16 reason) |
210 | { | 544 | { |
545 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
211 | int err; | 546 | int err; |
212 | 547 | ||
548 | if (wdev->sme_state == CFG80211_SME_IDLE) | ||
549 | return -EINVAL; | ||
550 | |||
213 | if (!rdev->ops->disconnect) { | 551 | if (!rdev->ops->disconnect) { |
214 | return -EOPNOTSUPP; | 552 | struct cfg80211_deauth_request deauth; |
553 | u8 bssid[ETH_ALEN]; | ||
554 | |||
555 | /* internal bug. */ | ||
556 | if (WARN_ON(!wdev->conn)) | ||
557 | return -EINVAL; | ||
558 | |||
559 | if (wdev->sme_state == CFG80211_SME_CONNECTING && | ||
560 | (wdev->conn->state == CFG80211_CONN_SCANNING || | ||
561 | wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) { | ||
562 | wdev->sme_state = CFG80211_SME_IDLE; | ||
563 | return 0; | ||
564 | } | ||
565 | |||
566 | if (!rdev->ops->deauth) | ||
567 | return -EOPNOTSUPP; | ||
568 | |||
569 | memset(&deauth, 0, sizeof(deauth)); | ||
570 | |||
571 | /* wdev->conn->params.bssid must be set if > SCANNING */ | ||
572 | memcpy(bssid, wdev->conn->params.bssid, ETH_ALEN); | ||
573 | deauth.peer_addr = bssid; | ||
574 | deauth.reason_code = reason; | ||
575 | |||
576 | err = rdev->ops->deauth(&rdev->wiphy, dev, &deauth); | ||
577 | if (err) | ||
578 | return err; | ||
215 | } else { | 579 | } else { |
216 | err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); | 580 | err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); |
217 | if (err) | 581 | if (err) |
218 | return err; | 582 | return err; |
219 | } | 583 | } |
220 | 584 | ||
221 | __cfg80211_disconnected(dev, 0, NULL, 0, false, GFP_KERNEL); | 585 | if (wdev->sme_state == CFG80211_SME_CONNECTED) |
586 | __cfg80211_disconnected(dev, GFP_KERNEL, NULL, 0, 0, false); | ||
587 | else if (wdev->sme_state == CFG80211_SME_CONNECTING) | ||
588 | cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, | ||
589 | WLAN_STATUS_UNSPECIFIED_FAILURE, | ||
590 | GFP_KERNEL); | ||
222 | 591 | ||
223 | return 0; | 592 | return 0; |
224 | } | 593 | } |