1b23aa676SSamuel Ortiz /* 2b23aa676SSamuel Ortiz * SME code for cfg80211's connect emulation. 3b23aa676SSamuel Ortiz * 4b23aa676SSamuel Ortiz * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> 5b23aa676SSamuel Ortiz * Copyright (C) 2009 Intel Corporation. All rights reserved. 6b23aa676SSamuel Ortiz */ 7b23aa676SSamuel Ortiz 8b23aa676SSamuel Ortiz #include <linux/etherdevice.h> 9b23aa676SSamuel Ortiz #include <linux/if_arp.h> 105a0e3ad6STejun Heo #include <linux/slab.h> 11b23aa676SSamuel Ortiz #include <linux/workqueue.h> 12a9a11622SJohannes Berg #include <linux/wireless.h> 13bc3b2d7fSPaul Gortmaker #include <linux/export.h> 14a9a11622SJohannes Berg #include <net/iw_handler.h> 15b23aa676SSamuel Ortiz #include <net/cfg80211.h> 16b23aa676SSamuel Ortiz #include <net/rtnetlink.h> 17b23aa676SSamuel Ortiz #include "nl80211.h" 188b19e6caSLuis R. Rodriguez #include "reg.h" 19e35e4d28SHila Gonen #include "rdev-ops.h" 20b23aa676SSamuel Ortiz 216829c878SJohannes Berg struct cfg80211_conn { 226829c878SJohannes Berg struct cfg80211_connect_params params; 236829c878SJohannes Berg /* these are sub-states of the _CONNECTING sme_state */ 246829c878SJohannes Berg enum { 256829c878SJohannes Berg CFG80211_CONN_IDLE, 266829c878SJohannes Berg CFG80211_CONN_SCANNING, 276829c878SJohannes Berg CFG80211_CONN_SCAN_AGAIN, 286829c878SJohannes Berg CFG80211_CONN_AUTHENTICATE_NEXT, 296829c878SJohannes Berg CFG80211_CONN_AUTHENTICATING, 306829c878SJohannes Berg CFG80211_CONN_ASSOCIATE_NEXT, 316829c878SJohannes Berg CFG80211_CONN_ASSOCIATING, 327d930bc3SJohannes Berg CFG80211_CONN_DEAUTH_ASSOC_FAIL, 336829c878SJohannes Berg } state; 34f401a6f7SJohannes Berg u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; 356829c878SJohannes Berg u8 *ie; 366829c878SJohannes Berg size_t ie_len; 37f401a6f7SJohannes Berg bool auto_auth, prev_bssid_valid; 386829c878SJohannes Berg }; 396829c878SJohannes Berg 4020925feeSJohn W. Linville static bool cfg80211_is_all_idle(void) 4109d989d1SLuis R. Rodriguez { 4209d989d1SLuis R. Rodriguez struct cfg80211_registered_device *rdev; 4309d989d1SLuis R. Rodriguez struct wireless_dev *wdev; 4409d989d1SLuis R. Rodriguez bool is_all_idle = true; 4509d989d1SLuis R. Rodriguez 4609d989d1SLuis R. Rodriguez /* 4709d989d1SLuis R. Rodriguez * All devices must be idle as otherwise if you are actively 4809d989d1SLuis R. Rodriguez * scanning some new beacon hints could be learned and would 4909d989d1SLuis R. Rodriguez * count as new regulatory hints. 5009d989d1SLuis R. Rodriguez */ 5109d989d1SLuis R. Rodriguez list_for_each_entry(rdev, &cfg80211_rdev_list, list) { 5289a54e48SJohannes Berg list_for_each_entry(wdev, &rdev->wdev_list, list) { 5309d989d1SLuis R. Rodriguez wdev_lock(wdev); 5409d989d1SLuis R. Rodriguez if (wdev->sme_state != CFG80211_SME_IDLE) 5509d989d1SLuis R. Rodriguez is_all_idle = false; 5609d989d1SLuis R. Rodriguez wdev_unlock(wdev); 5709d989d1SLuis R. Rodriguez } 5809d989d1SLuis R. Rodriguez } 5909d989d1SLuis R. Rodriguez 6009d989d1SLuis R. Rodriguez return is_all_idle; 6109d989d1SLuis R. Rodriguez } 6209d989d1SLuis R. Rodriguez 6309d989d1SLuis R. Rodriguez static void disconnect_work(struct work_struct *work) 6409d989d1SLuis R. Rodriguez { 655fe231e8SJohannes Berg rtnl_lock(); 665fe231e8SJohannes Berg if (cfg80211_is_all_idle()) 6709d989d1SLuis R. Rodriguez regulatory_hint_disconnect(); 685fe231e8SJohannes Berg rtnl_unlock(); 6909d989d1SLuis R. Rodriguez } 7009d989d1SLuis R. Rodriguez 7109d989d1SLuis R. Rodriguez static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); 726829c878SJohannes Berg 736829c878SJohannes Berg static int cfg80211_conn_scan(struct wireless_dev *wdev) 746829c878SJohannes Berg { 7579c97e97SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 766829c878SJohannes Berg struct cfg80211_scan_request *request; 776829c878SJohannes Berg int n_channels, err; 786829c878SJohannes Berg 796829c878SJohannes Berg ASSERT_RTNL(); 8079c97e97SJohannes Berg ASSERT_RDEV_LOCK(rdev); 81667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 826829c878SJohannes Berg 8379c97e97SJohannes Berg if (rdev->scan_req) 846829c878SJohannes Berg return -EBUSY; 856829c878SJohannes Berg 866829c878SJohannes Berg if (wdev->conn->params.channel) { 876829c878SJohannes Berg n_channels = 1; 886829c878SJohannes Berg } else { 896829c878SJohannes Berg enum ieee80211_band band; 906829c878SJohannes Berg n_channels = 0; 916829c878SJohannes Berg 926829c878SJohannes Berg for (band = 0; band < IEEE80211_NUM_BANDS; band++) { 936829c878SJohannes Berg if (!wdev->wiphy->bands[band]) 946829c878SJohannes Berg continue; 956829c878SJohannes Berg n_channels += wdev->wiphy->bands[band]->n_channels; 966829c878SJohannes Berg } 976829c878SJohannes Berg } 986829c878SJohannes Berg request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) + 996829c878SJohannes Berg sizeof(request->channels[0]) * n_channels, 1006829c878SJohannes Berg GFP_KERNEL); 1016829c878SJohannes Berg if (!request) 1026829c878SJohannes Berg return -ENOMEM; 1036829c878SJohannes Berg 1046829c878SJohannes Berg if (wdev->conn->params.channel) 1056829c878SJohannes Berg request->channels[0] = wdev->conn->params.channel; 1066829c878SJohannes Berg else { 1076829c878SJohannes Berg int i = 0, j; 1086829c878SJohannes Berg enum ieee80211_band band; 109e3081501SRajkumar Manoharan struct ieee80211_supported_band *bands; 110e3081501SRajkumar Manoharan struct ieee80211_channel *channel; 1116829c878SJohannes Berg 1126829c878SJohannes Berg for (band = 0; band < IEEE80211_NUM_BANDS; band++) { 113e3081501SRajkumar Manoharan bands = wdev->wiphy->bands[band]; 114e3081501SRajkumar Manoharan if (!bands) 1156829c878SJohannes Berg continue; 116e3081501SRajkumar Manoharan for (j = 0; j < bands->n_channels; j++) { 117e3081501SRajkumar Manoharan channel = &bands->channels[j]; 118e3081501SRajkumar Manoharan if (channel->flags & IEEE80211_CHAN_DISABLED) 119e3081501SRajkumar Manoharan continue; 120e3081501SRajkumar Manoharan request->channels[i++] = channel; 1216829c878SJohannes Berg } 122e3081501SRajkumar Manoharan request->rates[band] = (1 << bands->n_bitrates) - 1; 123e3081501SRajkumar Manoharan } 124e3081501SRajkumar Manoharan n_channels = i; 1256829c878SJohannes Berg } 1266829c878SJohannes Berg request->n_channels = n_channels; 1275ba63533SJohannes Berg request->ssids = (void *)&request->channels[n_channels]; 1286829c878SJohannes Berg request->n_ssids = 1; 1296829c878SJohannes Berg 1306829c878SJohannes Berg memcpy(request->ssids[0].ssid, wdev->conn->params.ssid, 1316829c878SJohannes Berg wdev->conn->params.ssid_len); 1326829c878SJohannes Berg request->ssids[0].ssid_len = wdev->conn->params.ssid_len; 1336829c878SJohannes Berg 134fd014284SJohannes Berg request->wdev = wdev; 13579c97e97SJohannes Berg request->wiphy = &rdev->wiphy; 13615d6030bSSam Leffler request->scan_start = jiffies; 1376829c878SJohannes Berg 13879c97e97SJohannes Berg rdev->scan_req = request; 1396829c878SJohannes Berg 140e35e4d28SHila Gonen err = rdev_scan(rdev, request); 1416829c878SJohannes Berg if (!err) { 1426829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_SCANNING; 143fd014284SJohannes Berg nl80211_send_scan_start(rdev, wdev); 144463d0183SJohannes Berg dev_hold(wdev->netdev); 1456829c878SJohannes Berg } else { 14679c97e97SJohannes Berg rdev->scan_req = NULL; 1476829c878SJohannes Berg kfree(request); 1486829c878SJohannes Berg } 1496829c878SJohannes Berg return err; 1506829c878SJohannes Berg } 1516829c878SJohannes Berg 1526829c878SJohannes Berg static int cfg80211_conn_do_work(struct wireless_dev *wdev) 1536829c878SJohannes Berg { 15479c97e97SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 15519957bb3SJohannes Berg struct cfg80211_connect_params *params; 156f62fab73SJohannes Berg struct cfg80211_assoc_request req = {}; 15719957bb3SJohannes Berg int err; 1586829c878SJohannes Berg 159667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 160667503ddSJohannes Berg 1616829c878SJohannes Berg if (!wdev->conn) 1626829c878SJohannes Berg return 0; 1636829c878SJohannes Berg 16419957bb3SJohannes Berg params = &wdev->conn->params; 16519957bb3SJohannes Berg 1666829c878SJohannes Berg switch (wdev->conn->state) { 1676829c878SJohannes Berg case CFG80211_CONN_SCAN_AGAIN: 1686829c878SJohannes Berg return cfg80211_conn_scan(wdev); 1696829c878SJohannes Berg case CFG80211_CONN_AUTHENTICATE_NEXT: 17079c97e97SJohannes Berg BUG_ON(!rdev->ops->auth); 17119957bb3SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATING; 172*91bf9b26SJohannes Berg return cfg80211_mlme_auth(rdev, wdev->netdev, 17319957bb3SJohannes Berg params->channel, params->auth_type, 17419957bb3SJohannes Berg params->bssid, 17519957bb3SJohannes Berg params->ssid, params->ssid_len, 176fffd0934SJohannes Berg NULL, 0, 177fffd0934SJohannes Berg params->key, params->key_len, 178e39e5b5eSJouni Malinen params->key_idx, NULL, 0); 1796829c878SJohannes Berg case CFG80211_CONN_ASSOCIATE_NEXT: 18079c97e97SJohannes Berg BUG_ON(!rdev->ops->assoc); 18119957bb3SJohannes Berg wdev->conn->state = CFG80211_CONN_ASSOCIATING; 182f401a6f7SJohannes Berg if (wdev->conn->prev_bssid_valid) 183f62fab73SJohannes Berg req.prev_bssid = wdev->conn->prev_bssid; 184f62fab73SJohannes Berg req.ie = params->ie; 185f62fab73SJohannes Berg req.ie_len = params->ie_len; 186f62fab73SJohannes Berg req.use_mfp = params->mfp != NL80211_MFP_NO; 187f62fab73SJohannes Berg req.crypto = params->crypto; 188f62fab73SJohannes Berg req.flags = params->flags; 189f62fab73SJohannes Berg req.ht_capa = params->ht_capa; 190f62fab73SJohannes Berg req.ht_capa_mask = params->ht_capa_mask; 191f62fab73SJohannes Berg req.vht_capa = params->vht_capa; 192f62fab73SJohannes Berg req.vht_capa_mask = params->vht_capa_mask; 193f62fab73SJohannes Berg 194*91bf9b26SJohannes Berg err = cfg80211_mlme_assoc(rdev, wdev->netdev, params->channel, 195f62fab73SJohannes Berg params->bssid, params->ssid, 196f62fab73SJohannes Berg params->ssid_len, &req); 19719957bb3SJohannes Berg if (err) 198*91bf9b26SJohannes Berg cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, 199667503ddSJohannes Berg NULL, 0, 200d5cdfacbSJouni Malinen WLAN_REASON_DEAUTH_LEAVING, 201d5cdfacbSJouni Malinen false); 20219957bb3SJohannes Berg return err; 2037d930bc3SJohannes Berg case CFG80211_CONN_DEAUTH_ASSOC_FAIL: 204*91bf9b26SJohannes Berg cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, 2057d930bc3SJohannes Berg NULL, 0, 206d5cdfacbSJouni Malinen WLAN_REASON_DEAUTH_LEAVING, false); 2077d930bc3SJohannes Berg /* return an error so that we call __cfg80211_connect_result() */ 2087d930bc3SJohannes Berg return -EINVAL; 2096829c878SJohannes Berg default: 2106829c878SJohannes Berg return 0; 2116829c878SJohannes Berg } 2126829c878SJohannes Berg } 2136829c878SJohannes Berg 2146829c878SJohannes Berg void cfg80211_conn_work(struct work_struct *work) 2156829c878SJohannes Berg { 21679c97e97SJohannes Berg struct cfg80211_registered_device *rdev = 2176829c878SJohannes Berg container_of(work, struct cfg80211_registered_device, conn_work); 2186829c878SJohannes Berg struct wireless_dev *wdev; 2197400f42eSJohannes Berg u8 bssid_buf[ETH_ALEN], *bssid = NULL; 2206829c878SJohannes Berg 2216829c878SJohannes Berg rtnl_lock(); 2226829c878SJohannes Berg 22389a54e48SJohannes Berg list_for_each_entry(wdev, &rdev->wdev_list, list) { 224c8157976SJohannes Berg if (!wdev->netdev) 225c8157976SJohannes Berg continue; 226c8157976SJohannes Berg 227667503ddSJohannes Berg wdev_lock(wdev); 228667503ddSJohannes Berg if (!netif_running(wdev->netdev)) { 229667503ddSJohannes Berg wdev_unlock(wdev); 2306829c878SJohannes Berg continue; 231667503ddSJohannes Berg } 232217c1577SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTING || !wdev->conn) { 233667503ddSJohannes Berg wdev_unlock(wdev); 2346829c878SJohannes Berg continue; 235667503ddSJohannes Berg } 2367400f42eSJohannes Berg if (wdev->conn->params.bssid) { 2377400f42eSJohannes Berg memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN); 2387400f42eSJohannes Berg bssid = bssid_buf; 2397400f42eSJohannes Berg } 2406829c878SJohannes Berg if (cfg80211_conn_do_work(wdev)) 241667503ddSJohannes Berg __cfg80211_connect_result( 2427d930bc3SJohannes Berg wdev->netdev, bssid, 2436829c878SJohannes Berg NULL, 0, NULL, 0, 2446829c878SJohannes Berg WLAN_STATUS_UNSPECIFIED_FAILURE, 245df7fc0f9SJohannes Berg false, NULL); 246667503ddSJohannes Berg wdev_unlock(wdev); 2476829c878SJohannes Berg } 2486829c878SJohannes Berg 2496829c878SJohannes Berg rtnl_unlock(); 2506829c878SJohannes Berg } 2516829c878SJohannes Berg 252bbac31f4SJohannes Berg static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev) 2536829c878SJohannes Berg { 25479c97e97SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 2556829c878SJohannes Berg struct cfg80211_bss *bss; 2566829c878SJohannes Berg u16 capa = WLAN_CAPABILITY_ESS; 2576829c878SJohannes Berg 258667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 259667503ddSJohannes Berg 2606829c878SJohannes Berg if (wdev->conn->params.privacy) 2616829c878SJohannes Berg capa |= WLAN_CAPABILITY_PRIVACY; 2626829c878SJohannes Berg 263ed9d0102SJouni Malinen bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel, 264ed9d0102SJouni Malinen wdev->conn->params.bssid, 2656829c878SJohannes Berg wdev->conn->params.ssid, 2666829c878SJohannes Berg wdev->conn->params.ssid_len, 2676829c878SJohannes Berg WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, 2686829c878SJohannes Berg capa); 2696829c878SJohannes Berg if (!bss) 270bbac31f4SJohannes Berg return NULL; 2716829c878SJohannes Berg 2726829c878SJohannes Berg memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN); 2736829c878SJohannes Berg wdev->conn->params.bssid = wdev->conn->bssid; 2746829c878SJohannes Berg wdev->conn->params.channel = bss->channel; 2756829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; 27679c97e97SJohannes Berg schedule_work(&rdev->conn_work); 2776829c878SJohannes Berg 278bbac31f4SJohannes Berg return bss; 2796829c878SJohannes Berg } 2806829c878SJohannes Berg 281667503ddSJohannes Berg static void __cfg80211_sme_scan_done(struct net_device *dev) 2826829c878SJohannes Berg { 2836829c878SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 28479c97e97SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 285bbac31f4SJohannes Berg struct cfg80211_bss *bss; 2866829c878SJohannes Berg 287667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 288667503ddSJohannes Berg 2896829c878SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTING) 2906829c878SJohannes Berg return; 2916829c878SJohannes Berg 292d4b1a687SZhu Yi if (!wdev->conn) 2936829c878SJohannes Berg return; 2946829c878SJohannes Berg 2956829c878SJohannes Berg if (wdev->conn->state != CFG80211_CONN_SCANNING && 2966829c878SJohannes Berg wdev->conn->state != CFG80211_CONN_SCAN_AGAIN) 2976829c878SJohannes Berg return; 2986829c878SJohannes Berg 299bbac31f4SJohannes Berg bss = cfg80211_get_conn_bss(wdev); 300bbac31f4SJohannes Berg if (bss) { 3015b112d3dSJohannes Berg cfg80211_put_bss(&rdev->wiphy, bss); 302bbac31f4SJohannes Berg } else { 3036829c878SJohannes Berg /* not found */ 3046829c878SJohannes Berg if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) 30579c97e97SJohannes Berg schedule_work(&rdev->conn_work); 3066829c878SJohannes Berg else 307667503ddSJohannes Berg __cfg80211_connect_result( 308667503ddSJohannes Berg wdev->netdev, 309667503ddSJohannes Berg wdev->conn->params.bssid, 3106829c878SJohannes Berg NULL, 0, NULL, 0, 3116829c878SJohannes Berg WLAN_STATUS_UNSPECIFIED_FAILURE, 312df7fc0f9SJohannes Berg false, NULL); 3136829c878SJohannes Berg } 3146829c878SJohannes Berg } 3156829c878SJohannes Berg 316667503ddSJohannes Berg void cfg80211_sme_scan_done(struct net_device *dev) 317667503ddSJohannes Berg { 318667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 319667503ddSJohannes Berg 320667503ddSJohannes Berg wdev_lock(wdev); 321667503ddSJohannes Berg __cfg80211_sme_scan_done(dev); 322667503ddSJohannes Berg wdev_unlock(wdev); 323667503ddSJohannes Berg } 324667503ddSJohannes Berg 325667503ddSJohannes Berg void cfg80211_sme_rx_auth(struct net_device *dev, 326667503ddSJohannes Berg const u8 *buf, size_t len) 3276829c878SJohannes Berg { 3286829c878SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 3296829c878SJohannes Berg struct wiphy *wiphy = wdev->wiphy; 3306829c878SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); 3316829c878SJohannes Berg struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; 3326829c878SJohannes Berg u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); 3336829c878SJohannes Berg 334667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 335667503ddSJohannes Berg 3366829c878SJohannes Berg /* should only RX auth frames when connecting */ 3376829c878SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTING) 3386829c878SJohannes Berg return; 3396829c878SJohannes Berg 3406829c878SJohannes Berg if (WARN_ON(!wdev->conn)) 3416829c878SJohannes Berg return; 3426829c878SJohannes Berg 3436829c878SJohannes Berg if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && 3446829c878SJohannes Berg wdev->conn->auto_auth && 3456829c878SJohannes Berg wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) { 3466829c878SJohannes Berg /* select automatically between only open, shared, leap */ 3476829c878SJohannes Berg switch (wdev->conn->params.auth_type) { 3486829c878SJohannes Berg case NL80211_AUTHTYPE_OPEN_SYSTEM: 349fffd0934SJohannes Berg if (wdev->connect_keys) 3506829c878SJohannes Berg wdev->conn->params.auth_type = 3516829c878SJohannes Berg NL80211_AUTHTYPE_SHARED_KEY; 352fffd0934SJohannes Berg else 353fffd0934SJohannes Berg wdev->conn->params.auth_type = 354fffd0934SJohannes Berg NL80211_AUTHTYPE_NETWORK_EAP; 3556829c878SJohannes Berg break; 3566829c878SJohannes Berg case NL80211_AUTHTYPE_SHARED_KEY: 3576829c878SJohannes Berg wdev->conn->params.auth_type = 3586829c878SJohannes Berg NL80211_AUTHTYPE_NETWORK_EAP; 3596829c878SJohannes Berg break; 3606829c878SJohannes Berg default: 3616829c878SJohannes Berg /* huh? */ 3626829c878SJohannes Berg wdev->conn->params.auth_type = 3636829c878SJohannes Berg NL80211_AUTHTYPE_OPEN_SYSTEM; 3646829c878SJohannes Berg break; 3656829c878SJohannes Berg } 3666829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; 3676829c878SJohannes Berg schedule_work(&rdev->conn_work); 36819957bb3SJohannes Berg } else if (status_code != WLAN_STATUS_SUCCESS) { 3694bde0f7dSJohannes Berg __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, 370df7fc0f9SJohannes Berg status_code, false, NULL); 37119957bb3SJohannes Berg } else if (wdev->sme_state == CFG80211_SME_CONNECTING && 3726829c878SJohannes Berg wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { 3736829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; 3746829c878SJohannes Berg schedule_work(&rdev->conn_work); 3756829c878SJohannes Berg } 3766829c878SJohannes Berg } 377b23aa676SSamuel Ortiz 378f401a6f7SJohannes Berg bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev) 379f401a6f7SJohannes Berg { 380f401a6f7SJohannes Berg struct wiphy *wiphy = wdev->wiphy; 381f401a6f7SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); 382f401a6f7SJohannes Berg 383f401a6f7SJohannes Berg if (WARN_ON(!wdev->conn)) 384f401a6f7SJohannes Berg return false; 385f401a6f7SJohannes Berg 386f401a6f7SJohannes Berg if (!wdev->conn->prev_bssid_valid) 387f401a6f7SJohannes Berg return false; 388f401a6f7SJohannes Berg 389f401a6f7SJohannes Berg /* 390f401a6f7SJohannes Berg * Some stupid APs don't accept reassoc, so we 391f401a6f7SJohannes Berg * need to fall back to trying regular assoc. 392f401a6f7SJohannes Berg */ 393f401a6f7SJohannes Berg wdev->conn->prev_bssid_valid = false; 394f401a6f7SJohannes Berg wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; 395f401a6f7SJohannes Berg schedule_work(&rdev->conn_work); 396f401a6f7SJohannes Berg 397f401a6f7SJohannes Berg return true; 398f401a6f7SJohannes Berg } 399f401a6f7SJohannes Berg 4007d930bc3SJohannes Berg void cfg80211_sme_failed_assoc(struct wireless_dev *wdev) 4017d930bc3SJohannes Berg { 4027d930bc3SJohannes Berg struct wiphy *wiphy = wdev->wiphy; 4037d930bc3SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); 4047d930bc3SJohannes Berg 4057d930bc3SJohannes Berg wdev->conn->state = CFG80211_CONN_DEAUTH_ASSOC_FAIL; 4067d930bc3SJohannes Berg schedule_work(&rdev->conn_work); 4077d930bc3SJohannes Berg } 4087d930bc3SJohannes Berg 409667503ddSJohannes Berg void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, 410b23aa676SSamuel Ortiz const u8 *req_ie, size_t req_ie_len, 411b23aa676SSamuel Ortiz const u8 *resp_ie, size_t resp_ie_len, 412df7fc0f9SJohannes Berg u16 status, bool wextev, 413df7fc0f9SJohannes Berg struct cfg80211_bss *bss) 414b23aa676SSamuel Ortiz { 415b23aa676SSamuel Ortiz struct wireless_dev *wdev = dev->ieee80211_ptr; 4169caf0364SJohannes Berg const u8 *country_ie; 4173d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT 418b23aa676SSamuel Ortiz union iwreq_data wrqu; 419b23aa676SSamuel Ortiz #endif 420b23aa676SSamuel Ortiz 421667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 422667503ddSJohannes Berg 423074ac8dfSJohannes Berg if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && 424074ac8dfSJohannes Berg wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) 425b23aa676SSamuel Ortiz return; 426b23aa676SSamuel Ortiz 427f7969969SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTING) 428ea416a79SJohannes Berg return; 429ea416a79SJohannes Berg 430e45cd82aSJohannes Berg nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, 431e45cd82aSJohannes Berg bssid, req_ie, req_ie_len, 432e45cd82aSJohannes Berg resp_ie, resp_ie_len, 433667503ddSJohannes Berg status, GFP_KERNEL); 434e45cd82aSJohannes Berg 4353d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT 436e45cd82aSJohannes Berg if (wextev) { 437e45cd82aSJohannes Berg if (req_ie && status == WLAN_STATUS_SUCCESS) { 438e45cd82aSJohannes Berg memset(&wrqu, 0, sizeof(wrqu)); 439e45cd82aSJohannes Berg wrqu.data.length = req_ie_len; 4403409ff77SZhu Yi wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie); 441e45cd82aSJohannes Berg } 442e45cd82aSJohannes Berg 443e45cd82aSJohannes Berg if (resp_ie && status == WLAN_STATUS_SUCCESS) { 444e45cd82aSJohannes Berg memset(&wrqu, 0, sizeof(wrqu)); 445e45cd82aSJohannes Berg wrqu.data.length = resp_ie_len; 446e45cd82aSJohannes Berg wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); 447e45cd82aSJohannes Berg } 448e45cd82aSJohannes Berg 449e45cd82aSJohannes Berg memset(&wrqu, 0, sizeof(wrqu)); 450e45cd82aSJohannes Berg wrqu.ap_addr.sa_family = ARPHRD_ETHER; 451f401a6f7SJohannes Berg if (bssid && status == WLAN_STATUS_SUCCESS) { 452e45cd82aSJohannes Berg memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); 453f401a6f7SJohannes Berg memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); 454f401a6f7SJohannes Berg wdev->wext.prev_bssid_valid = true; 455f401a6f7SJohannes Berg } 456e45cd82aSJohannes Berg wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); 457e45cd82aSJohannes Berg } 458e45cd82aSJohannes Berg #endif 459e45cd82aSJohannes Berg 460df7fc0f9SJohannes Berg if (wdev->current_bss) { 461df7fc0f9SJohannes Berg cfg80211_unhold_bss(wdev->current_bss); 4625b112d3dSJohannes Berg cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); 463df7fc0f9SJohannes Berg wdev->current_bss = NULL; 464df7fc0f9SJohannes Berg } 465df7fc0f9SJohannes Berg 46619957bb3SJohannes Berg if (wdev->conn) 46719957bb3SJohannes Berg wdev->conn->state = CFG80211_CONN_IDLE; 46819957bb3SJohannes Berg 469fffd0934SJohannes Berg if (status != WLAN_STATUS_SUCCESS) { 470fffd0934SJohannes Berg wdev->sme_state = CFG80211_SME_IDLE; 471415ad1efSDavid Kilroy if (wdev->conn) 472415ad1efSDavid Kilroy kfree(wdev->conn->ie); 473fffd0934SJohannes Berg kfree(wdev->conn); 474fffd0934SJohannes Berg wdev->conn = NULL; 475fffd0934SJohannes Berg kfree(wdev->connect_keys); 476fffd0934SJohannes Berg wdev->connect_keys = NULL; 4778dadadb7SJohannes Berg wdev->ssid_len = 0; 4785b112d3dSJohannes Berg cfg80211_put_bss(wdev->wiphy, bss); 479fffd0934SJohannes Berg return; 480fffd0934SJohannes Berg } 481fffd0934SJohannes Berg 482df7fc0f9SJohannes Berg if (!bss) 483ed9d0102SJouni Malinen bss = cfg80211_get_bss(wdev->wiphy, 484ed9d0102SJouni Malinen wdev->conn ? wdev->conn->params.channel : 485ed9d0102SJouni Malinen NULL, 486ed9d0102SJouni Malinen bssid, 487b23aa676SSamuel Ortiz wdev->ssid, wdev->ssid_len, 488b23aa676SSamuel Ortiz WLAN_CAPABILITY_ESS, 489b23aa676SSamuel Ortiz WLAN_CAPABILITY_ESS); 490b23aa676SSamuel Ortiz 491b23aa676SSamuel Ortiz if (WARN_ON(!bss)) 492b23aa676SSamuel Ortiz return; 493b23aa676SSamuel Ortiz 49419957bb3SJohannes Berg cfg80211_hold_bss(bss_from_pub(bss)); 49519957bb3SJohannes Berg wdev->current_bss = bss_from_pub(bss); 496b23aa676SSamuel Ortiz 497b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_CONNECTED; 498fffd0934SJohannes Berg cfg80211_upload_connect_keys(wdev); 4998b19e6caSLuis R. Rodriguez 5009caf0364SJohannes Berg rcu_read_lock(); 5019caf0364SJohannes Berg country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); 5029caf0364SJohannes Berg if (!country_ie) { 5039caf0364SJohannes Berg rcu_read_unlock(); 5049caf0364SJohannes Berg return; 5059caf0364SJohannes Berg } 5069caf0364SJohannes Berg 5079caf0364SJohannes Berg country_ie = kmemdup(country_ie, 2 + country_ie[1], GFP_ATOMIC); 5089caf0364SJohannes Berg rcu_read_unlock(); 5098b19e6caSLuis R. Rodriguez 5108b19e6caSLuis R. Rodriguez if (!country_ie) 5118b19e6caSLuis R. Rodriguez return; 5128b19e6caSLuis R. Rodriguez 5138b19e6caSLuis R. Rodriguez /* 5148b19e6caSLuis R. Rodriguez * ieee80211_bss_get_ie() ensures we can access: 5158b19e6caSLuis R. Rodriguez * - country_ie + 2, the start of the country ie data, and 5168b19e6caSLuis R. Rodriguez * - and country_ie[1] which is the IE length 5178b19e6caSLuis R. Rodriguez */ 5181a919318SJohannes Berg regulatory_hint_11d(wdev->wiphy, bss->channel->band, 5191a919318SJohannes Berg country_ie + 2, country_ie[1]); 5209caf0364SJohannes Berg kfree(country_ie); 521b23aa676SSamuel Ortiz } 522f2129354SJohannes Berg 523f2129354SJohannes Berg void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, 524f2129354SJohannes Berg const u8 *req_ie, size_t req_ie_len, 525f2129354SJohannes Berg const u8 *resp_ie, size_t resp_ie_len, 526f2129354SJohannes Berg u16 status, gfp_t gfp) 527f2129354SJohannes Berg { 528667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 529667503ddSJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 530667503ddSJohannes Berg struct cfg80211_event *ev; 531667503ddSJohannes Berg unsigned long flags; 532667503ddSJohannes Berg 533f7969969SJohannes Berg CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING); 534f7969969SJohannes Berg 535667503ddSJohannes Berg ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); 536667503ddSJohannes Berg if (!ev) 537667503ddSJohannes Berg return; 538667503ddSJohannes Berg 539667503ddSJohannes Berg ev->type = EVENT_CONNECT_RESULT; 54016a832e7SZhu Yi if (bssid) 541667503ddSJohannes Berg memcpy(ev->cr.bssid, bssid, ETH_ALEN); 5427834704bSNishant Sarmukadam if (req_ie_len) { 543667503ddSJohannes Berg ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev); 544667503ddSJohannes Berg ev->cr.req_ie_len = req_ie_len; 545667503ddSJohannes Berg memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len); 5467834704bSNishant Sarmukadam } 5477834704bSNishant Sarmukadam if (resp_ie_len) { 548667503ddSJohannes Berg ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; 549667503ddSJohannes Berg ev->cr.resp_ie_len = resp_ie_len; 550667503ddSJohannes Berg memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len); 5517834704bSNishant Sarmukadam } 552667503ddSJohannes Berg ev->cr.status = status; 553667503ddSJohannes Berg 554667503ddSJohannes Berg spin_lock_irqsave(&wdev->event_lock, flags); 555667503ddSJohannes Berg list_add_tail(&ev->list, &wdev->event_list); 556667503ddSJohannes Berg spin_unlock_irqrestore(&wdev->event_lock, flags); 557e60d7443SAlban Browaeys queue_work(cfg80211_wq, &rdev->event_work); 558f2129354SJohannes Berg } 559b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_connect_result); 560b23aa676SSamuel Ortiz 561ed9d0102SJouni Malinen void __cfg80211_roamed(struct wireless_dev *wdev, 562adbde344SVasanthakumar Thiagarajan struct cfg80211_bss *bss, 563b23aa676SSamuel Ortiz const u8 *req_ie, size_t req_ie_len, 564667503ddSJohannes Berg const u8 *resp_ie, size_t resp_ie_len) 565b23aa676SSamuel Ortiz { 5663d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT 567b23aa676SSamuel Ortiz union iwreq_data wrqu; 568b23aa676SSamuel Ortiz #endif 569667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 570667503ddSJohannes Berg 571074ac8dfSJohannes Berg if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && 572074ac8dfSJohannes Berg wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) 573adbde344SVasanthakumar Thiagarajan goto out; 574b23aa676SSamuel Ortiz 575f7969969SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTED) 576adbde344SVasanthakumar Thiagarajan goto out; 577b23aa676SSamuel Ortiz 578b23aa676SSamuel Ortiz /* internal error -- how did we get to CONNECTED w/o BSS? */ 579b23aa676SSamuel Ortiz if (WARN_ON(!wdev->current_bss)) { 580adbde344SVasanthakumar Thiagarajan goto out; 581b23aa676SSamuel Ortiz } 582b23aa676SSamuel Ortiz 583b23aa676SSamuel Ortiz cfg80211_unhold_bss(wdev->current_bss); 5845b112d3dSJohannes Berg cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); 585b23aa676SSamuel Ortiz wdev->current_bss = NULL; 586b23aa676SSamuel Ortiz 58719957bb3SJohannes Berg cfg80211_hold_bss(bss_from_pub(bss)); 58819957bb3SJohannes Berg wdev->current_bss = bss_from_pub(bss); 589b23aa676SSamuel Ortiz 590adbde344SVasanthakumar Thiagarajan nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bss->bssid, 591667503ddSJohannes Berg req_ie, req_ie_len, resp_ie, resp_ie_len, 592667503ddSJohannes Berg GFP_KERNEL); 593b23aa676SSamuel Ortiz 5943d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT 595b23aa676SSamuel Ortiz if (req_ie) { 596b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 597b23aa676SSamuel Ortiz wrqu.data.length = req_ie_len; 5983409ff77SZhu Yi wireless_send_event(wdev->netdev, IWEVASSOCREQIE, 599667503ddSJohannes Berg &wrqu, req_ie); 600b23aa676SSamuel Ortiz } 601b23aa676SSamuel Ortiz 602b23aa676SSamuel Ortiz if (resp_ie) { 603b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 604b23aa676SSamuel Ortiz wrqu.data.length = resp_ie_len; 605667503ddSJohannes Berg wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, 606667503ddSJohannes Berg &wrqu, resp_ie); 607b23aa676SSamuel Ortiz } 608b23aa676SSamuel Ortiz 609b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 610b23aa676SSamuel Ortiz wrqu.ap_addr.sa_family = ARPHRD_ETHER; 611adbde344SVasanthakumar Thiagarajan memcpy(wrqu.ap_addr.sa_data, bss->bssid, ETH_ALEN); 612adbde344SVasanthakumar Thiagarajan memcpy(wdev->wext.prev_bssid, bss->bssid, ETH_ALEN); 613f401a6f7SJohannes Berg wdev->wext.prev_bssid_valid = true; 614667503ddSJohannes Berg wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); 615b23aa676SSamuel Ortiz #endif 616adbde344SVasanthakumar Thiagarajan 617adbde344SVasanthakumar Thiagarajan return; 618adbde344SVasanthakumar Thiagarajan out: 6195b112d3dSJohannes Berg cfg80211_put_bss(wdev->wiphy, bss); 620b23aa676SSamuel Ortiz } 621667503ddSJohannes Berg 622ed9d0102SJouni Malinen void cfg80211_roamed(struct net_device *dev, 623ed9d0102SJouni Malinen struct ieee80211_channel *channel, 624ed9d0102SJouni Malinen const u8 *bssid, 625667503ddSJohannes Berg const u8 *req_ie, size_t req_ie_len, 626667503ddSJohannes Berg const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) 627667503ddSJohannes Berg { 628667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 629adbde344SVasanthakumar Thiagarajan struct cfg80211_bss *bss; 630adbde344SVasanthakumar Thiagarajan 631adbde344SVasanthakumar Thiagarajan CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); 632adbde344SVasanthakumar Thiagarajan 633adbde344SVasanthakumar Thiagarajan bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid, 634adbde344SVasanthakumar Thiagarajan wdev->ssid_len, WLAN_CAPABILITY_ESS, 635adbde344SVasanthakumar Thiagarajan WLAN_CAPABILITY_ESS); 636adbde344SVasanthakumar Thiagarajan if (WARN_ON(!bss)) 637adbde344SVasanthakumar Thiagarajan return; 638adbde344SVasanthakumar Thiagarajan 639adbde344SVasanthakumar Thiagarajan cfg80211_roamed_bss(dev, bss, req_ie, req_ie_len, resp_ie, 640adbde344SVasanthakumar Thiagarajan resp_ie_len, gfp); 641adbde344SVasanthakumar Thiagarajan } 642adbde344SVasanthakumar Thiagarajan EXPORT_SYMBOL(cfg80211_roamed); 643adbde344SVasanthakumar Thiagarajan 644adbde344SVasanthakumar Thiagarajan void cfg80211_roamed_bss(struct net_device *dev, 645adbde344SVasanthakumar Thiagarajan struct cfg80211_bss *bss, const u8 *req_ie, 646adbde344SVasanthakumar Thiagarajan size_t req_ie_len, const u8 *resp_ie, 647adbde344SVasanthakumar Thiagarajan size_t resp_ie_len, gfp_t gfp) 648adbde344SVasanthakumar Thiagarajan { 649adbde344SVasanthakumar Thiagarajan struct wireless_dev *wdev = dev->ieee80211_ptr; 650667503ddSJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 651667503ddSJohannes Berg struct cfg80211_event *ev; 652667503ddSJohannes Berg unsigned long flags; 653667503ddSJohannes Berg 654f7969969SJohannes Berg CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); 655f7969969SJohannes Berg 656adbde344SVasanthakumar Thiagarajan if (WARN_ON(!bss)) 657667503ddSJohannes Berg return; 658667503ddSJohannes Berg 659adbde344SVasanthakumar Thiagarajan ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); 660adbde344SVasanthakumar Thiagarajan if (!ev) { 6615b112d3dSJohannes Berg cfg80211_put_bss(wdev->wiphy, bss); 662adbde344SVasanthakumar Thiagarajan return; 663adbde344SVasanthakumar Thiagarajan } 664adbde344SVasanthakumar Thiagarajan 665667503ddSJohannes Berg ev->type = EVENT_ROAMED; 666667503ddSJohannes Berg ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev); 667667503ddSJohannes Berg ev->rm.req_ie_len = req_ie_len; 668667503ddSJohannes Berg memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len); 669667503ddSJohannes Berg ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; 670667503ddSJohannes Berg ev->rm.resp_ie_len = resp_ie_len; 671667503ddSJohannes Berg memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len); 672adbde344SVasanthakumar Thiagarajan ev->rm.bss = bss; 673667503ddSJohannes Berg 674667503ddSJohannes Berg spin_lock_irqsave(&wdev->event_lock, flags); 675667503ddSJohannes Berg list_add_tail(&ev->list, &wdev->event_list); 676667503ddSJohannes Berg spin_unlock_irqrestore(&wdev->event_lock, flags); 677e60d7443SAlban Browaeys queue_work(cfg80211_wq, &rdev->event_work); 678667503ddSJohannes Berg } 679adbde344SVasanthakumar Thiagarajan EXPORT_SYMBOL(cfg80211_roamed_bss); 680b23aa676SSamuel Ortiz 681667503ddSJohannes Berg void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, 6826829c878SJohannes Berg size_t ie_len, u16 reason, bool from_ap) 683b23aa676SSamuel Ortiz { 684b23aa676SSamuel Ortiz struct wireless_dev *wdev = dev->ieee80211_ptr; 685fffd0934SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 686fffd0934SJohannes Berg int i; 6873d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT 688b23aa676SSamuel Ortiz union iwreq_data wrqu; 689b23aa676SSamuel Ortiz #endif 690b23aa676SSamuel Ortiz 691667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 692667503ddSJohannes Berg 693074ac8dfSJohannes Berg if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && 694074ac8dfSJohannes Berg wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) 695b23aa676SSamuel Ortiz return; 696b23aa676SSamuel Ortiz 697f7969969SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTED) 698b23aa676SSamuel Ortiz return; 699b23aa676SSamuel Ortiz 700b23aa676SSamuel Ortiz if (wdev->current_bss) { 701b23aa676SSamuel Ortiz cfg80211_unhold_bss(wdev->current_bss); 7025b112d3dSJohannes Berg cfg80211_put_bss(wdev->wiphy, &wdev->current_bss->pub); 703b23aa676SSamuel Ortiz } 704b23aa676SSamuel Ortiz 705b23aa676SSamuel Ortiz wdev->current_bss = NULL; 706b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_IDLE; 7078dadadb7SJohannes Berg wdev->ssid_len = 0; 708b23aa676SSamuel Ortiz 7096829c878SJohannes Berg if (wdev->conn) { 7106829c878SJohannes Berg kfree(wdev->conn->ie); 7116829c878SJohannes Berg wdev->conn->ie = NULL; 71219957bb3SJohannes Berg kfree(wdev->conn); 71319957bb3SJohannes Berg wdev->conn = NULL; 7146829c878SJohannes Berg } 7156829c878SJohannes Berg 716fffd0934SJohannes Berg nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); 717fffd0934SJohannes Berg 718fffd0934SJohannes Berg /* 719fffd0934SJohannes Berg * Delete all the keys ... pairwise keys can't really 720fffd0934SJohannes Berg * exist any more anyway, but default keys might. 721fffd0934SJohannes Berg */ 722fffd0934SJohannes Berg if (rdev->ops->del_key) 723fffd0934SJohannes Berg for (i = 0; i < 6; i++) 724e35e4d28SHila Gonen rdev_del_key(rdev, dev, i, false, NULL); 725b23aa676SSamuel Ortiz 7263d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT 727b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 728b23aa676SSamuel Ortiz wrqu.ap_addr.sa_family = ARPHRD_ETHER; 729b23aa676SSamuel Ortiz wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); 7305f612033SAbhijeet Kolekar wdev->wext.connect.ssid_len = 0; 731b23aa676SSamuel Ortiz #endif 73209d989d1SLuis R. Rodriguez 73309d989d1SLuis R. Rodriguez schedule_work(&cfg80211_disconnect_work); 734b23aa676SSamuel Ortiz } 735b23aa676SSamuel Ortiz 736b23aa676SSamuel Ortiz void cfg80211_disconnected(struct net_device *dev, u16 reason, 737b23aa676SSamuel Ortiz u8 *ie, size_t ie_len, gfp_t gfp) 738b23aa676SSamuel Ortiz { 739667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 740667503ddSJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 741667503ddSJohannes Berg struct cfg80211_event *ev; 742667503ddSJohannes Berg unsigned long flags; 743667503ddSJohannes Berg 744f7969969SJohannes Berg CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); 745f7969969SJohannes Berg 746667503ddSJohannes Berg ev = kzalloc(sizeof(*ev) + ie_len, gfp); 747667503ddSJohannes Berg if (!ev) 748667503ddSJohannes Berg return; 749667503ddSJohannes Berg 750667503ddSJohannes Berg ev->type = EVENT_DISCONNECTED; 751667503ddSJohannes Berg ev->dc.ie = ((u8 *)ev) + sizeof(*ev); 752667503ddSJohannes Berg ev->dc.ie_len = ie_len; 753667503ddSJohannes Berg memcpy((void *)ev->dc.ie, ie, ie_len); 754667503ddSJohannes Berg ev->dc.reason = reason; 755667503ddSJohannes Berg 756667503ddSJohannes Berg spin_lock_irqsave(&wdev->event_lock, flags); 757667503ddSJohannes Berg list_add_tail(&ev->list, &wdev->event_list); 758667503ddSJohannes Berg spin_unlock_irqrestore(&wdev->event_lock, flags); 759e60d7443SAlban Browaeys queue_work(cfg80211_wq, &rdev->event_work); 760b23aa676SSamuel Ortiz } 761b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_disconnected); 762b23aa676SSamuel Ortiz 763667503ddSJohannes Berg int __cfg80211_connect(struct cfg80211_registered_device *rdev, 764b23aa676SSamuel Ortiz struct net_device *dev, 765fffd0934SJohannes Berg struct cfg80211_connect_params *connect, 766f401a6f7SJohannes Berg struct cfg80211_cached_keys *connkeys, 767f401a6f7SJohannes Berg const u8 *prev_bssid) 768b23aa676SSamuel Ortiz { 769b23aa676SSamuel Ortiz struct wireless_dev *wdev = dev->ieee80211_ptr; 770bbac31f4SJohannes Berg struct cfg80211_bss *bss = NULL; 771667503ddSJohannes Berg int err; 772667503ddSJohannes Berg 773667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 774b23aa676SSamuel Ortiz 775b23aa676SSamuel Ortiz if (wdev->sme_state != CFG80211_SME_IDLE) 776b23aa676SSamuel Ortiz return -EALREADY; 777b23aa676SSamuel Ortiz 778fffd0934SJohannes Berg if (WARN_ON(wdev->connect_keys)) { 779fffd0934SJohannes Berg kfree(wdev->connect_keys); 780fffd0934SJohannes Berg wdev->connect_keys = NULL; 781fffd0934SJohannes Berg } 782fffd0934SJohannes Berg 7837e7c8926SBen Greear cfg80211_oper_and_ht_capa(&connect->ht_capa_mask, 7847e7c8926SBen Greear rdev->wiphy.ht_capa_mod_mask); 7857e7c8926SBen Greear 786fffd0934SJohannes Berg if (connkeys && connkeys->def >= 0) { 787fffd0934SJohannes Berg int idx; 788bcba8eaeSSamuel Ortiz u32 cipher; 789fffd0934SJohannes Berg 790fffd0934SJohannes Berg idx = connkeys->def; 791bcba8eaeSSamuel Ortiz cipher = connkeys->params[idx].cipher; 792fffd0934SJohannes Berg /* If given a WEP key we may need it for shared key auth */ 793bcba8eaeSSamuel Ortiz if (cipher == WLAN_CIPHER_SUITE_WEP40 || 794bcba8eaeSSamuel Ortiz cipher == WLAN_CIPHER_SUITE_WEP104) { 795fffd0934SJohannes Berg connect->key_idx = idx; 796fffd0934SJohannes Berg connect->key = connkeys->params[idx].key; 797fffd0934SJohannes Berg connect->key_len = connkeys->params[idx].key_len; 798bcba8eaeSSamuel Ortiz 799bcba8eaeSSamuel Ortiz /* 800bcba8eaeSSamuel Ortiz * If ciphers are not set (e.g. when going through 801bcba8eaeSSamuel Ortiz * iwconfig), we have to set them appropriately here. 802bcba8eaeSSamuel Ortiz */ 803bcba8eaeSSamuel Ortiz if (connect->crypto.cipher_group == 0) 804bcba8eaeSSamuel Ortiz connect->crypto.cipher_group = cipher; 805bcba8eaeSSamuel Ortiz 806bcba8eaeSSamuel Ortiz if (connect->crypto.n_ciphers_pairwise == 0) { 807bcba8eaeSSamuel Ortiz connect->crypto.n_ciphers_pairwise = 1; 808bcba8eaeSSamuel Ortiz connect->crypto.ciphers_pairwise[0] = cipher; 809bcba8eaeSSamuel Ortiz } 810fffd0934SJohannes Berg } 811fffd0934SJohannes Berg } 812fffd0934SJohannes Berg 813b23aa676SSamuel Ortiz if (!rdev->ops->connect) { 8146829c878SJohannes Berg if (!rdev->ops->auth || !rdev->ops->assoc) 815b23aa676SSamuel Ortiz return -EOPNOTSUPP; 8166829c878SJohannes Berg 81719957bb3SJohannes Berg if (WARN_ON(wdev->conn)) 81819957bb3SJohannes Berg return -EINPROGRESS; 81919957bb3SJohannes Berg 8206829c878SJohannes Berg wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); 8216829c878SJohannes Berg if (!wdev->conn) 8226829c878SJohannes Berg return -ENOMEM; 8236829c878SJohannes Berg 8246829c878SJohannes Berg /* 8256829c878SJohannes Berg * Copy all parameters, and treat explicitly IEs, BSSID, SSID. 8266829c878SJohannes Berg */ 8276829c878SJohannes Berg memcpy(&wdev->conn->params, connect, sizeof(*connect)); 8286829c878SJohannes Berg if (connect->bssid) { 8296829c878SJohannes Berg wdev->conn->params.bssid = wdev->conn->bssid; 8306829c878SJohannes Berg memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); 8316829c878SJohannes Berg } 8326829c878SJohannes Berg 8336829c878SJohannes Berg if (connect->ie) { 8346829c878SJohannes Berg wdev->conn->ie = kmemdup(connect->ie, connect->ie_len, 8356829c878SJohannes Berg GFP_KERNEL); 8366829c878SJohannes Berg wdev->conn->params.ie = wdev->conn->ie; 83719957bb3SJohannes Berg if (!wdev->conn->ie) { 83819957bb3SJohannes Berg kfree(wdev->conn); 83919957bb3SJohannes Berg wdev->conn = NULL; 8406829c878SJohannes Berg return -ENOMEM; 8416829c878SJohannes Berg } 84219957bb3SJohannes Berg } 8436829c878SJohannes Berg 8446829c878SJohannes Berg if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { 8456829c878SJohannes Berg wdev->conn->auto_auth = true; 8466829c878SJohannes Berg /* start with open system ... should mostly work */ 8476829c878SJohannes Berg wdev->conn->params.auth_type = 8486829c878SJohannes Berg NL80211_AUTHTYPE_OPEN_SYSTEM; 8496829c878SJohannes Berg } else { 8506829c878SJohannes Berg wdev->conn->auto_auth = false; 8516829c878SJohannes Berg } 8526829c878SJohannes Berg 8536829c878SJohannes Berg memcpy(wdev->ssid, connect->ssid, connect->ssid_len); 8546829c878SJohannes Berg wdev->ssid_len = connect->ssid_len; 8556829c878SJohannes Berg wdev->conn->params.ssid = wdev->ssid; 8566829c878SJohannes Berg wdev->conn->params.ssid_len = connect->ssid_len; 8576829c878SJohannes Berg 8588bb89485SJohannes Berg /* see if we have the bss already */ 859bbac31f4SJohannes Berg bss = cfg80211_get_conn_bss(wdev); 8606829c878SJohannes Berg 8616829c878SJohannes Berg wdev->sme_state = CFG80211_SME_CONNECTING; 862fffd0934SJohannes Berg wdev->connect_keys = connkeys; 8636829c878SJohannes Berg 864f401a6f7SJohannes Berg if (prev_bssid) { 865f401a6f7SJohannes Berg memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN); 866f401a6f7SJohannes Berg wdev->conn->prev_bssid_valid = true; 867f401a6f7SJohannes Berg } 868f401a6f7SJohannes Berg 869bbac31f4SJohannes Berg /* we're good if we have a matching bss struct */ 870bbac31f4SJohannes Berg if (bss) { 8716829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; 8726829c878SJohannes Berg err = cfg80211_conn_do_work(wdev); 8735b112d3dSJohannes Berg cfg80211_put_bss(wdev->wiphy, bss); 8746829c878SJohannes Berg } else { 8756829c878SJohannes Berg /* otherwise we'll need to scan for the AP first */ 8766829c878SJohannes Berg err = cfg80211_conn_scan(wdev); 8776829c878SJohannes Berg /* 8786829c878SJohannes Berg * If we can't scan right now, then we need to scan again 8796829c878SJohannes Berg * after the current scan finished, since the parameters 8806829c878SJohannes Berg * changed (unless we find a good AP anyway). 8816829c878SJohannes Berg */ 8826829c878SJohannes Berg if (err == -EBUSY) { 8836829c878SJohannes Berg err = 0; 8846829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; 8856829c878SJohannes Berg } 8866829c878SJohannes Berg } 88719957bb3SJohannes Berg if (err) { 888415ad1efSDavid Kilroy kfree(wdev->conn->ie); 88919957bb3SJohannes Berg kfree(wdev->conn); 89019957bb3SJohannes Berg wdev->conn = NULL; 8916829c878SJohannes Berg wdev->sme_state = CFG80211_SME_IDLE; 892fffd0934SJohannes Berg wdev->connect_keys = NULL; 8938dadadb7SJohannes Berg wdev->ssid_len = 0; 89419957bb3SJohannes Berg } 8956829c878SJohannes Berg 8966829c878SJohannes Berg return err; 897b23aa676SSamuel Ortiz } else { 898b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_CONNECTING; 899fffd0934SJohannes Berg wdev->connect_keys = connkeys; 900e35e4d28SHila Gonen err = rdev_connect(rdev, dev, connect); 901b23aa676SSamuel Ortiz if (err) { 902fffd0934SJohannes Berg wdev->connect_keys = NULL; 903b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_IDLE; 904b23aa676SSamuel Ortiz return err; 905b23aa676SSamuel Ortiz } 906b23aa676SSamuel Ortiz 907b23aa676SSamuel Ortiz memcpy(wdev->ssid, connect->ssid, connect->ssid_len); 908b23aa676SSamuel Ortiz wdev->ssid_len = connect->ssid_len; 909b23aa676SSamuel Ortiz 910b23aa676SSamuel Ortiz return 0; 911b23aa676SSamuel Ortiz } 9126829c878SJohannes Berg } 913b23aa676SSamuel Ortiz 914667503ddSJohannes Berg int cfg80211_connect(struct cfg80211_registered_device *rdev, 915667503ddSJohannes Berg struct net_device *dev, 916fffd0934SJohannes Berg struct cfg80211_connect_params *connect, 917fffd0934SJohannes Berg struct cfg80211_cached_keys *connkeys) 918667503ddSJohannes Berg { 919667503ddSJohannes Berg int err; 920667503ddSJohannes Berg 921667503ddSJohannes Berg wdev_lock(dev->ieee80211_ptr); 922f401a6f7SJohannes Berg err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL); 923667503ddSJohannes Berg wdev_unlock(dev->ieee80211_ptr); 924667503ddSJohannes Berg 925667503ddSJohannes Berg return err; 926667503ddSJohannes Berg } 927667503ddSJohannes Berg 928667503ddSJohannes Berg int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, 929f2129354SJohannes Berg struct net_device *dev, u16 reason, bool wextev) 930b23aa676SSamuel Ortiz { 9316829c878SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 932b23aa676SSamuel Ortiz int err; 933b23aa676SSamuel Ortiz 934667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 935667503ddSJohannes Berg 9366829c878SJohannes Berg if (wdev->sme_state == CFG80211_SME_IDLE) 9376829c878SJohannes Berg return -EINVAL; 9386829c878SJohannes Berg 939fffd0934SJohannes Berg kfree(wdev->connect_keys); 940fffd0934SJohannes Berg wdev->connect_keys = NULL; 941fffd0934SJohannes Berg 942b23aa676SSamuel Ortiz if (!rdev->ops->disconnect) { 94319957bb3SJohannes Berg if (!rdev->ops->deauth) 94419957bb3SJohannes Berg return -EOPNOTSUPP; 9456829c878SJohannes Berg 94619957bb3SJohannes Berg /* was it connected by userspace SME? */ 94719957bb3SJohannes Berg if (!wdev->conn) { 94819957bb3SJohannes Berg cfg80211_mlme_down(rdev, dev); 9492430816bSJohannes Berg goto disconnect; 95019957bb3SJohannes Berg } 9516829c878SJohannes Berg 9526829c878SJohannes Berg if (wdev->sme_state == CFG80211_SME_CONNECTING && 9536829c878SJohannes Berg (wdev->conn->state == CFG80211_CONN_SCANNING || 9546829c878SJohannes Berg wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) { 9556829c878SJohannes Berg wdev->sme_state = CFG80211_SME_IDLE; 956415ad1efSDavid Kilroy kfree(wdev->conn->ie); 95719957bb3SJohannes Berg kfree(wdev->conn); 95819957bb3SJohannes Berg wdev->conn = NULL; 9598dadadb7SJohannes Berg wdev->ssid_len = 0; 9606829c878SJohannes Berg return 0; 9616829c878SJohannes Berg } 9626829c878SJohannes Berg 9636829c878SJohannes Berg /* wdev->conn->params.bssid must be set if > SCANNING */ 964*91bf9b26SJohannes Berg err = cfg80211_mlme_deauth(rdev, dev, 965667503ddSJohannes Berg wdev->conn->params.bssid, 966d5cdfacbSJouni Malinen NULL, 0, reason, false); 9676829c878SJohannes Berg if (err) 9686829c878SJohannes Berg return err; 969b23aa676SSamuel Ortiz } else { 970e35e4d28SHila Gonen err = rdev_disconnect(rdev, dev, reason); 971b23aa676SSamuel Ortiz if (err) 972b23aa676SSamuel Ortiz return err; 973b23aa676SSamuel Ortiz } 974b23aa676SSamuel Ortiz 9752430816bSJohannes Berg disconnect: 9766829c878SJohannes Berg if (wdev->sme_state == CFG80211_SME_CONNECTED) 977667503ddSJohannes Berg __cfg80211_disconnected(dev, NULL, 0, 0, false); 9786829c878SJohannes Berg else if (wdev->sme_state == CFG80211_SME_CONNECTING) 979f2129354SJohannes Berg __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, 9806829c878SJohannes Berg WLAN_STATUS_UNSPECIFIED_FAILURE, 981df7fc0f9SJohannes Berg wextev, NULL); 982b23aa676SSamuel Ortiz 983b23aa676SSamuel Ortiz return 0; 984b23aa676SSamuel Ortiz } 98519957bb3SJohannes Berg 986667503ddSJohannes Berg int cfg80211_disconnect(struct cfg80211_registered_device *rdev, 987667503ddSJohannes Berg struct net_device *dev, 988667503ddSJohannes Berg u16 reason, bool wextev) 989667503ddSJohannes Berg { 990667503ddSJohannes Berg int err; 991667503ddSJohannes Berg 992667503ddSJohannes Berg wdev_lock(dev->ieee80211_ptr); 993667503ddSJohannes Berg err = __cfg80211_disconnect(rdev, dev, reason, wextev); 994667503ddSJohannes Berg wdev_unlock(dev->ieee80211_ptr); 995667503ddSJohannes Berg 996667503ddSJohannes Berg return err; 997667503ddSJohannes Berg } 998667503ddSJohannes Berg 99995de817bSJohannes Berg void cfg80211_sme_disassoc(struct net_device *dev, 100095de817bSJohannes Berg struct cfg80211_internal_bss *bss) 100119957bb3SJohannes Berg { 100219957bb3SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 100319957bb3SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 100419957bb3SJohannes Berg u8 bssid[ETH_ALEN]; 100519957bb3SJohannes Berg 1006667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 1007667503ddSJohannes Berg 100819957bb3SJohannes Berg if (!wdev->conn) 100919957bb3SJohannes Berg return; 101019957bb3SJohannes Berg 101119957bb3SJohannes Berg if (wdev->conn->state == CFG80211_CONN_IDLE) 101219957bb3SJohannes Berg return; 101319957bb3SJohannes Berg 101419957bb3SJohannes Berg /* 101519957bb3SJohannes Berg * Ok, so the association was made by this SME -- we don't 101619957bb3SJohannes Berg * want it any more so deauthenticate too. 101719957bb3SJohannes Berg */ 101819957bb3SJohannes Berg 101995de817bSJohannes Berg memcpy(bssid, bss->pub.bssid, ETH_ALEN); 102019957bb3SJohannes Berg 1021*91bf9b26SJohannes Berg cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0, 102295de817bSJohannes Berg WLAN_REASON_DEAUTH_LEAVING, false); 102319957bb3SJohannes Berg } 1024