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" 19b23aa676SSamuel Ortiz 206829c878SJohannes Berg struct cfg80211_conn { 216829c878SJohannes Berg struct cfg80211_connect_params params; 226829c878SJohannes Berg /* these are sub-states of the _CONNECTING sme_state */ 236829c878SJohannes Berg enum { 246829c878SJohannes Berg CFG80211_CONN_IDLE, 256829c878SJohannes Berg CFG80211_CONN_SCANNING, 266829c878SJohannes Berg CFG80211_CONN_SCAN_AGAIN, 276829c878SJohannes Berg CFG80211_CONN_AUTHENTICATE_NEXT, 286829c878SJohannes Berg CFG80211_CONN_AUTHENTICATING, 296829c878SJohannes Berg CFG80211_CONN_ASSOCIATE_NEXT, 306829c878SJohannes Berg CFG80211_CONN_ASSOCIATING, 317d930bc3SJohannes Berg CFG80211_CONN_DEAUTH_ASSOC_FAIL, 326829c878SJohannes Berg } state; 33f401a6f7SJohannes Berg u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; 346829c878SJohannes Berg u8 *ie; 356829c878SJohannes Berg size_t ie_len; 36f401a6f7SJohannes Berg bool auto_auth, prev_bssid_valid; 376829c878SJohannes Berg }; 386829c878SJohannes Berg 3920925feeSJohn W. Linville static bool cfg80211_is_all_idle(void) 4009d989d1SLuis R. Rodriguez { 4109d989d1SLuis R. Rodriguez struct cfg80211_registered_device *rdev; 4209d989d1SLuis R. Rodriguez struct wireless_dev *wdev; 4309d989d1SLuis R. Rodriguez bool is_all_idle = true; 4409d989d1SLuis R. Rodriguez 4509d989d1SLuis R. Rodriguez mutex_lock(&cfg80211_mutex); 4609d989d1SLuis R. Rodriguez 4709d989d1SLuis R. Rodriguez /* 4809d989d1SLuis R. Rodriguez * All devices must be idle as otherwise if you are actively 4909d989d1SLuis R. Rodriguez * scanning some new beacon hints could be learned and would 5009d989d1SLuis R. Rodriguez * count as new regulatory hints. 5109d989d1SLuis R. Rodriguez */ 5209d989d1SLuis R. Rodriguez list_for_each_entry(rdev, &cfg80211_rdev_list, list) { 5309d989d1SLuis R. Rodriguez cfg80211_lock_rdev(rdev); 5489a54e48SJohannes Berg list_for_each_entry(wdev, &rdev->wdev_list, list) { 5509d989d1SLuis R. Rodriguez wdev_lock(wdev); 5609d989d1SLuis R. Rodriguez if (wdev->sme_state != CFG80211_SME_IDLE) 5709d989d1SLuis R. Rodriguez is_all_idle = false; 5809d989d1SLuis R. Rodriguez wdev_unlock(wdev); 5909d989d1SLuis R. Rodriguez } 6009d989d1SLuis R. Rodriguez cfg80211_unlock_rdev(rdev); 6109d989d1SLuis R. Rodriguez } 6209d989d1SLuis R. Rodriguez 6309d989d1SLuis R. Rodriguez mutex_unlock(&cfg80211_mutex); 6409d989d1SLuis R. Rodriguez 6509d989d1SLuis R. Rodriguez return is_all_idle; 6609d989d1SLuis R. Rodriguez } 6709d989d1SLuis R. Rodriguez 6809d989d1SLuis R. Rodriguez static void disconnect_work(struct work_struct *work) 6909d989d1SLuis R. Rodriguez { 7009d989d1SLuis R. Rodriguez if (!cfg80211_is_all_idle()) 7109d989d1SLuis R. Rodriguez return; 7209d989d1SLuis R. Rodriguez 7309d989d1SLuis R. Rodriguez regulatory_hint_disconnect(); 7409d989d1SLuis R. Rodriguez } 7509d989d1SLuis R. Rodriguez 7609d989d1SLuis R. Rodriguez static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); 776829c878SJohannes Berg 786829c878SJohannes Berg static int cfg80211_conn_scan(struct wireless_dev *wdev) 796829c878SJohannes Berg { 8079c97e97SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 816829c878SJohannes Berg struct cfg80211_scan_request *request; 826829c878SJohannes Berg int n_channels, err; 836829c878SJohannes Berg 846829c878SJohannes Berg ASSERT_RTNL(); 8579c97e97SJohannes Berg ASSERT_RDEV_LOCK(rdev); 86667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 876829c878SJohannes Berg 8879c97e97SJohannes Berg if (rdev->scan_req) 896829c878SJohannes Berg return -EBUSY; 906829c878SJohannes Berg 916829c878SJohannes Berg if (wdev->conn->params.channel) { 926829c878SJohannes Berg n_channels = 1; 936829c878SJohannes Berg } else { 946829c878SJohannes Berg enum ieee80211_band band; 956829c878SJohannes Berg n_channels = 0; 966829c878SJohannes Berg 976829c878SJohannes Berg for (band = 0; band < IEEE80211_NUM_BANDS; band++) { 986829c878SJohannes Berg if (!wdev->wiphy->bands[band]) 996829c878SJohannes Berg continue; 1006829c878SJohannes Berg n_channels += wdev->wiphy->bands[band]->n_channels; 1016829c878SJohannes Berg } 1026829c878SJohannes Berg } 1036829c878SJohannes Berg request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) + 1046829c878SJohannes Berg sizeof(request->channels[0]) * n_channels, 1056829c878SJohannes Berg GFP_KERNEL); 1066829c878SJohannes Berg if (!request) 1076829c878SJohannes Berg return -ENOMEM; 1086829c878SJohannes Berg 1096829c878SJohannes Berg if (wdev->conn->params.channel) 1106829c878SJohannes Berg request->channels[0] = wdev->conn->params.channel; 1116829c878SJohannes Berg else { 1126829c878SJohannes Berg int i = 0, j; 1136829c878SJohannes Berg enum ieee80211_band band; 114e3081501SRajkumar Manoharan struct ieee80211_supported_band *bands; 115e3081501SRajkumar Manoharan struct ieee80211_channel *channel; 1166829c878SJohannes Berg 1176829c878SJohannes Berg for (band = 0; band < IEEE80211_NUM_BANDS; band++) { 118e3081501SRajkumar Manoharan bands = wdev->wiphy->bands[band]; 119e3081501SRajkumar Manoharan if (!bands) 1206829c878SJohannes Berg continue; 121e3081501SRajkumar Manoharan for (j = 0; j < bands->n_channels; j++) { 122e3081501SRajkumar Manoharan channel = &bands->channels[j]; 123e3081501SRajkumar Manoharan if (channel->flags & IEEE80211_CHAN_DISABLED) 124e3081501SRajkumar Manoharan continue; 125e3081501SRajkumar Manoharan request->channels[i++] = channel; 1266829c878SJohannes Berg } 127e3081501SRajkumar Manoharan request->rates[band] = (1 << bands->n_bitrates) - 1; 128e3081501SRajkumar Manoharan } 129e3081501SRajkumar Manoharan n_channels = i; 1306829c878SJohannes Berg } 1316829c878SJohannes Berg request->n_channels = n_channels; 1325ba63533SJohannes Berg request->ssids = (void *)&request->channels[n_channels]; 1336829c878SJohannes Berg request->n_ssids = 1; 1346829c878SJohannes Berg 1356829c878SJohannes Berg memcpy(request->ssids[0].ssid, wdev->conn->params.ssid, 1366829c878SJohannes Berg wdev->conn->params.ssid_len); 1376829c878SJohannes Berg request->ssids[0].ssid_len = wdev->conn->params.ssid_len; 1386829c878SJohannes Berg 139*fd014284SJohannes Berg request->wdev = wdev; 14079c97e97SJohannes Berg request->wiphy = &rdev->wiphy; 1416829c878SJohannes Berg 14279c97e97SJohannes Berg rdev->scan_req = request; 1436829c878SJohannes Berg 144*fd014284SJohannes Berg err = rdev->ops->scan(wdev->wiphy, request); 1456829c878SJohannes Berg if (!err) { 1466829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_SCANNING; 147*fd014284SJohannes Berg nl80211_send_scan_start(rdev, wdev); 148463d0183SJohannes Berg dev_hold(wdev->netdev); 1496829c878SJohannes Berg } else { 15079c97e97SJohannes Berg rdev->scan_req = NULL; 1516829c878SJohannes Berg kfree(request); 1526829c878SJohannes Berg } 1536829c878SJohannes Berg return err; 1546829c878SJohannes Berg } 1556829c878SJohannes Berg 1566829c878SJohannes Berg static int cfg80211_conn_do_work(struct wireless_dev *wdev) 1576829c878SJohannes Berg { 15879c97e97SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 15919957bb3SJohannes Berg struct cfg80211_connect_params *params; 160f401a6f7SJohannes Berg const u8 *prev_bssid = NULL; 16119957bb3SJohannes Berg int err; 1626829c878SJohannes Berg 163667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 164667503ddSJohannes Berg 1656829c878SJohannes Berg if (!wdev->conn) 1666829c878SJohannes Berg return 0; 1676829c878SJohannes Berg 16819957bb3SJohannes Berg params = &wdev->conn->params; 16919957bb3SJohannes Berg 1706829c878SJohannes Berg switch (wdev->conn->state) { 1716829c878SJohannes Berg case CFG80211_CONN_SCAN_AGAIN: 1726829c878SJohannes Berg return cfg80211_conn_scan(wdev); 1736829c878SJohannes Berg case CFG80211_CONN_AUTHENTICATE_NEXT: 17479c97e97SJohannes Berg BUG_ON(!rdev->ops->auth); 17519957bb3SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATING; 17679c97e97SJohannes Berg return __cfg80211_mlme_auth(rdev, wdev->netdev, 17719957bb3SJohannes Berg params->channel, params->auth_type, 17819957bb3SJohannes Berg params->bssid, 17919957bb3SJohannes Berg params->ssid, params->ssid_len, 180fffd0934SJohannes Berg NULL, 0, 181fffd0934SJohannes Berg params->key, params->key_len, 18295de817bSJohannes Berg params->key_idx); 1836829c878SJohannes Berg case CFG80211_CONN_ASSOCIATE_NEXT: 18479c97e97SJohannes Berg BUG_ON(!rdev->ops->assoc); 18519957bb3SJohannes Berg wdev->conn->state = CFG80211_CONN_ASSOCIATING; 186f401a6f7SJohannes Berg if (wdev->conn->prev_bssid_valid) 187f401a6f7SJohannes Berg prev_bssid = wdev->conn->prev_bssid; 18879c97e97SJohannes Berg err = __cfg80211_mlme_assoc(rdev, wdev->netdev, 189667503ddSJohannes Berg params->channel, params->bssid, 190f401a6f7SJohannes Berg prev_bssid, 19119957bb3SJohannes Berg params->ssid, params->ssid_len, 19219957bb3SJohannes Berg params->ie, params->ie_len, 1937e7c8926SBen Greear false, ¶ms->crypto, 1947e7c8926SBen Greear params->flags, ¶ms->ht_capa, 1957e7c8926SBen Greear ¶ms->ht_capa_mask); 19619957bb3SJohannes Berg if (err) 19779c97e97SJohannes Berg __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, 198667503ddSJohannes Berg NULL, 0, 199d5cdfacbSJouni Malinen WLAN_REASON_DEAUTH_LEAVING, 200d5cdfacbSJouni Malinen false); 20119957bb3SJohannes Berg return err; 2027d930bc3SJohannes Berg case CFG80211_CONN_DEAUTH_ASSOC_FAIL: 2037d930bc3SJohannes Berg __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, 2047d930bc3SJohannes Berg NULL, 0, 205d5cdfacbSJouni Malinen WLAN_REASON_DEAUTH_LEAVING, false); 2067d930bc3SJohannes Berg /* return an error so that we call __cfg80211_connect_result() */ 2077d930bc3SJohannes Berg return -EINVAL; 2086829c878SJohannes Berg default: 2096829c878SJohannes Berg return 0; 2106829c878SJohannes Berg } 2116829c878SJohannes Berg } 2126829c878SJohannes Berg 2136829c878SJohannes Berg void cfg80211_conn_work(struct work_struct *work) 2146829c878SJohannes Berg { 21579c97e97SJohannes Berg struct cfg80211_registered_device *rdev = 2166829c878SJohannes Berg container_of(work, struct cfg80211_registered_device, conn_work); 2176829c878SJohannes Berg struct wireless_dev *wdev; 2187400f42eSJohannes Berg u8 bssid_buf[ETH_ALEN], *bssid = NULL; 2196829c878SJohannes Berg 2206829c878SJohannes Berg rtnl_lock(); 22179c97e97SJohannes Berg cfg80211_lock_rdev(rdev); 22279c97e97SJohannes Berg mutex_lock(&rdev->devlist_mtx); 2236829c878SJohannes Berg 22489a54e48SJohannes Berg list_for_each_entry(wdev, &rdev->wdev_list, list) { 225667503ddSJohannes Berg wdev_lock(wdev); 226667503ddSJohannes Berg if (!netif_running(wdev->netdev)) { 227667503ddSJohannes Berg wdev_unlock(wdev); 2286829c878SJohannes Berg continue; 229667503ddSJohannes Berg } 230667503ddSJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTING) { 231667503ddSJohannes Berg wdev_unlock(wdev); 2326829c878SJohannes Berg continue; 233667503ddSJohannes Berg } 2347400f42eSJohannes Berg if (wdev->conn->params.bssid) { 2357400f42eSJohannes Berg memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN); 2367400f42eSJohannes Berg bssid = bssid_buf; 2377400f42eSJohannes Berg } 2386829c878SJohannes Berg if (cfg80211_conn_do_work(wdev)) 239667503ddSJohannes Berg __cfg80211_connect_result( 2407d930bc3SJohannes Berg wdev->netdev, bssid, 2416829c878SJohannes Berg NULL, 0, NULL, 0, 2426829c878SJohannes Berg WLAN_STATUS_UNSPECIFIED_FAILURE, 243df7fc0f9SJohannes Berg false, NULL); 244667503ddSJohannes Berg wdev_unlock(wdev); 2456829c878SJohannes Berg } 2466829c878SJohannes Berg 24779c97e97SJohannes Berg mutex_unlock(&rdev->devlist_mtx); 24879c97e97SJohannes Berg cfg80211_unlock_rdev(rdev); 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) { 301bbac31f4SJohannes Berg cfg80211_put_bss(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 32059bbb6f7SJohannes Berg mutex_lock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx); 321667503ddSJohannes Berg wdev_lock(wdev); 322667503ddSJohannes Berg __cfg80211_sme_scan_done(dev); 323667503ddSJohannes Berg wdev_unlock(wdev); 32459bbb6f7SJohannes Berg mutex_unlock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx); 325667503ddSJohannes Berg } 326667503ddSJohannes Berg 327667503ddSJohannes Berg void cfg80211_sme_rx_auth(struct net_device *dev, 328667503ddSJohannes Berg const u8 *buf, size_t len) 3296829c878SJohannes Berg { 3306829c878SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 3316829c878SJohannes Berg struct wiphy *wiphy = wdev->wiphy; 3326829c878SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); 3336829c878SJohannes Berg struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; 3346829c878SJohannes Berg u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); 3356829c878SJohannes Berg 336667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 337667503ddSJohannes Berg 3386829c878SJohannes Berg /* should only RX auth frames when connecting */ 3396829c878SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTING) 3406829c878SJohannes Berg return; 3416829c878SJohannes Berg 3426829c878SJohannes Berg if (WARN_ON(!wdev->conn)) 3436829c878SJohannes Berg return; 3446829c878SJohannes Berg 3456829c878SJohannes Berg if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && 3466829c878SJohannes Berg wdev->conn->auto_auth && 3476829c878SJohannes Berg wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) { 3486829c878SJohannes Berg /* select automatically between only open, shared, leap */ 3496829c878SJohannes Berg switch (wdev->conn->params.auth_type) { 3506829c878SJohannes Berg case NL80211_AUTHTYPE_OPEN_SYSTEM: 351fffd0934SJohannes Berg if (wdev->connect_keys) 3526829c878SJohannes Berg wdev->conn->params.auth_type = 3536829c878SJohannes Berg NL80211_AUTHTYPE_SHARED_KEY; 354fffd0934SJohannes Berg else 355fffd0934SJohannes Berg wdev->conn->params.auth_type = 356fffd0934SJohannes Berg NL80211_AUTHTYPE_NETWORK_EAP; 3576829c878SJohannes Berg break; 3586829c878SJohannes Berg case NL80211_AUTHTYPE_SHARED_KEY: 3596829c878SJohannes Berg wdev->conn->params.auth_type = 3606829c878SJohannes Berg NL80211_AUTHTYPE_NETWORK_EAP; 3616829c878SJohannes Berg break; 3626829c878SJohannes Berg default: 3636829c878SJohannes Berg /* huh? */ 3646829c878SJohannes Berg wdev->conn->params.auth_type = 3656829c878SJohannes Berg NL80211_AUTHTYPE_OPEN_SYSTEM; 3666829c878SJohannes Berg break; 3676829c878SJohannes Berg } 3686829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; 3696829c878SJohannes Berg schedule_work(&rdev->conn_work); 37019957bb3SJohannes Berg } else if (status_code != WLAN_STATUS_SUCCESS) { 3714bde0f7dSJohannes Berg __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, 372df7fc0f9SJohannes Berg status_code, false, NULL); 37319957bb3SJohannes Berg } else if (wdev->sme_state == CFG80211_SME_CONNECTING && 3746829c878SJohannes Berg wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { 3756829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; 3766829c878SJohannes Berg schedule_work(&rdev->conn_work); 3776829c878SJohannes Berg } 3786829c878SJohannes Berg } 379b23aa676SSamuel Ortiz 380f401a6f7SJohannes Berg bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev) 381f401a6f7SJohannes Berg { 382f401a6f7SJohannes Berg struct wiphy *wiphy = wdev->wiphy; 383f401a6f7SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); 384f401a6f7SJohannes Berg 385f401a6f7SJohannes Berg if (WARN_ON(!wdev->conn)) 386f401a6f7SJohannes Berg return false; 387f401a6f7SJohannes Berg 388f401a6f7SJohannes Berg if (!wdev->conn->prev_bssid_valid) 389f401a6f7SJohannes Berg return false; 390f401a6f7SJohannes Berg 391f401a6f7SJohannes Berg /* 392f401a6f7SJohannes Berg * Some stupid APs don't accept reassoc, so we 393f401a6f7SJohannes Berg * need to fall back to trying regular assoc. 394f401a6f7SJohannes Berg */ 395f401a6f7SJohannes Berg wdev->conn->prev_bssid_valid = false; 396f401a6f7SJohannes Berg wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; 397f401a6f7SJohannes Berg schedule_work(&rdev->conn_work); 398f401a6f7SJohannes Berg 399f401a6f7SJohannes Berg return true; 400f401a6f7SJohannes Berg } 401f401a6f7SJohannes Berg 4027d930bc3SJohannes Berg void cfg80211_sme_failed_assoc(struct wireless_dev *wdev) 4037d930bc3SJohannes Berg { 4047d930bc3SJohannes Berg struct wiphy *wiphy = wdev->wiphy; 4057d930bc3SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); 4067d930bc3SJohannes Berg 4077d930bc3SJohannes Berg wdev->conn->state = CFG80211_CONN_DEAUTH_ASSOC_FAIL; 4087d930bc3SJohannes Berg schedule_work(&rdev->conn_work); 4097d930bc3SJohannes Berg } 4107d930bc3SJohannes Berg 411667503ddSJohannes Berg void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, 412b23aa676SSamuel Ortiz const u8 *req_ie, size_t req_ie_len, 413b23aa676SSamuel Ortiz const u8 *resp_ie, size_t resp_ie_len, 414df7fc0f9SJohannes Berg u16 status, bool wextev, 415df7fc0f9SJohannes Berg struct cfg80211_bss *bss) 416b23aa676SSamuel Ortiz { 417b23aa676SSamuel Ortiz struct wireless_dev *wdev = dev->ieee80211_ptr; 4188b19e6caSLuis R. Rodriguez u8 *country_ie; 4193d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT 420b23aa676SSamuel Ortiz union iwreq_data wrqu; 421b23aa676SSamuel Ortiz #endif 422b23aa676SSamuel Ortiz 423667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 424667503ddSJohannes Berg 425074ac8dfSJohannes Berg if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && 426074ac8dfSJohannes Berg wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) 427b23aa676SSamuel Ortiz return; 428b23aa676SSamuel Ortiz 429f7969969SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTING) 430ea416a79SJohannes Berg return; 431ea416a79SJohannes Berg 432e45cd82aSJohannes Berg nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, 433e45cd82aSJohannes Berg bssid, req_ie, req_ie_len, 434e45cd82aSJohannes Berg resp_ie, resp_ie_len, 435667503ddSJohannes Berg status, GFP_KERNEL); 436e45cd82aSJohannes Berg 4373d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT 438e45cd82aSJohannes Berg if (wextev) { 439e45cd82aSJohannes Berg if (req_ie && status == WLAN_STATUS_SUCCESS) { 440e45cd82aSJohannes Berg memset(&wrqu, 0, sizeof(wrqu)); 441e45cd82aSJohannes Berg wrqu.data.length = req_ie_len; 4423409ff77SZhu Yi wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie); 443e45cd82aSJohannes Berg } 444e45cd82aSJohannes Berg 445e45cd82aSJohannes Berg if (resp_ie && status == WLAN_STATUS_SUCCESS) { 446e45cd82aSJohannes Berg memset(&wrqu, 0, sizeof(wrqu)); 447e45cd82aSJohannes Berg wrqu.data.length = resp_ie_len; 448e45cd82aSJohannes Berg wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); 449e45cd82aSJohannes Berg } 450e45cd82aSJohannes Berg 451e45cd82aSJohannes Berg memset(&wrqu, 0, sizeof(wrqu)); 452e45cd82aSJohannes Berg wrqu.ap_addr.sa_family = ARPHRD_ETHER; 453f401a6f7SJohannes Berg if (bssid && status == WLAN_STATUS_SUCCESS) { 454e45cd82aSJohannes Berg memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); 455f401a6f7SJohannes Berg memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); 456f401a6f7SJohannes Berg wdev->wext.prev_bssid_valid = true; 457f401a6f7SJohannes Berg } 458e45cd82aSJohannes Berg wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); 459e45cd82aSJohannes Berg } 460e45cd82aSJohannes Berg #endif 461e45cd82aSJohannes Berg 462df7fc0f9SJohannes Berg if (wdev->current_bss) { 463df7fc0f9SJohannes Berg cfg80211_unhold_bss(wdev->current_bss); 464df7fc0f9SJohannes Berg cfg80211_put_bss(&wdev->current_bss->pub); 465df7fc0f9SJohannes Berg wdev->current_bss = NULL; 466df7fc0f9SJohannes Berg } 467df7fc0f9SJohannes Berg 46819957bb3SJohannes Berg if (wdev->conn) 46919957bb3SJohannes Berg wdev->conn->state = CFG80211_CONN_IDLE; 47019957bb3SJohannes Berg 471fffd0934SJohannes Berg if (status != WLAN_STATUS_SUCCESS) { 472fffd0934SJohannes Berg wdev->sme_state = CFG80211_SME_IDLE; 473415ad1efSDavid Kilroy if (wdev->conn) 474415ad1efSDavid Kilroy kfree(wdev->conn->ie); 475fffd0934SJohannes Berg kfree(wdev->conn); 476fffd0934SJohannes Berg wdev->conn = NULL; 477fffd0934SJohannes Berg kfree(wdev->connect_keys); 478fffd0934SJohannes Berg wdev->connect_keys = NULL; 4798dadadb7SJohannes Berg wdev->ssid_len = 0; 48095de817bSJohannes Berg cfg80211_put_bss(bss); 481fffd0934SJohannes Berg return; 482fffd0934SJohannes Berg } 483fffd0934SJohannes Berg 484df7fc0f9SJohannes Berg if (!bss) 485ed9d0102SJouni Malinen bss = cfg80211_get_bss(wdev->wiphy, 486ed9d0102SJouni Malinen wdev->conn ? wdev->conn->params.channel : 487ed9d0102SJouni Malinen NULL, 488ed9d0102SJouni Malinen bssid, 489b23aa676SSamuel Ortiz wdev->ssid, wdev->ssid_len, 490b23aa676SSamuel Ortiz WLAN_CAPABILITY_ESS, 491b23aa676SSamuel Ortiz WLAN_CAPABILITY_ESS); 492b23aa676SSamuel Ortiz 493b23aa676SSamuel Ortiz if (WARN_ON(!bss)) 494b23aa676SSamuel Ortiz return; 495b23aa676SSamuel Ortiz 49619957bb3SJohannes Berg cfg80211_hold_bss(bss_from_pub(bss)); 49719957bb3SJohannes Berg wdev->current_bss = bss_from_pub(bss); 498b23aa676SSamuel Ortiz 499b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_CONNECTED; 500fffd0934SJohannes Berg cfg80211_upload_connect_keys(wdev); 5018b19e6caSLuis R. Rodriguez 5028b19e6caSLuis R. Rodriguez country_ie = (u8 *) ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); 5038b19e6caSLuis R. Rodriguez 5048b19e6caSLuis R. Rodriguez if (!country_ie) 5058b19e6caSLuis R. Rodriguez return; 5068b19e6caSLuis R. Rodriguez 5078b19e6caSLuis R. Rodriguez /* 5088b19e6caSLuis R. Rodriguez * ieee80211_bss_get_ie() ensures we can access: 5098b19e6caSLuis R. Rodriguez * - country_ie + 2, the start of the country ie data, and 5108b19e6caSLuis R. Rodriguez * - and country_ie[1] which is the IE length 5118b19e6caSLuis R. Rodriguez */ 5128b19e6caSLuis R. Rodriguez regulatory_hint_11d(wdev->wiphy, 51384920e3eSLuis R. Rodriguez bss->channel->band, 5148b19e6caSLuis R. Rodriguez country_ie + 2, 5158b19e6caSLuis R. Rodriguez country_ie[1]); 516b23aa676SSamuel Ortiz } 517f2129354SJohannes Berg 518f2129354SJohannes Berg void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, 519f2129354SJohannes Berg const u8 *req_ie, size_t req_ie_len, 520f2129354SJohannes Berg const u8 *resp_ie, size_t resp_ie_len, 521f2129354SJohannes Berg u16 status, gfp_t gfp) 522f2129354SJohannes Berg { 523667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 524667503ddSJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 525667503ddSJohannes Berg struct cfg80211_event *ev; 526667503ddSJohannes Berg unsigned long flags; 527667503ddSJohannes Berg 528f7969969SJohannes Berg CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING); 529f7969969SJohannes Berg 530667503ddSJohannes Berg ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); 531667503ddSJohannes Berg if (!ev) 532667503ddSJohannes Berg return; 533667503ddSJohannes Berg 534667503ddSJohannes Berg ev->type = EVENT_CONNECT_RESULT; 53516a832e7SZhu Yi if (bssid) 536667503ddSJohannes Berg memcpy(ev->cr.bssid, bssid, ETH_ALEN); 5377834704bSNishant Sarmukadam if (req_ie_len) { 538667503ddSJohannes Berg ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev); 539667503ddSJohannes Berg ev->cr.req_ie_len = req_ie_len; 540667503ddSJohannes Berg memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len); 5417834704bSNishant Sarmukadam } 5427834704bSNishant Sarmukadam if (resp_ie_len) { 543667503ddSJohannes Berg ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; 544667503ddSJohannes Berg ev->cr.resp_ie_len = resp_ie_len; 545667503ddSJohannes Berg memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len); 5467834704bSNishant Sarmukadam } 547667503ddSJohannes Berg ev->cr.status = status; 548667503ddSJohannes Berg 549667503ddSJohannes Berg spin_lock_irqsave(&wdev->event_lock, flags); 550667503ddSJohannes Berg list_add_tail(&ev->list, &wdev->event_list); 551667503ddSJohannes Berg spin_unlock_irqrestore(&wdev->event_lock, flags); 552e60d7443SAlban Browaeys queue_work(cfg80211_wq, &rdev->event_work); 553f2129354SJohannes Berg } 554b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_connect_result); 555b23aa676SSamuel Ortiz 556ed9d0102SJouni Malinen void __cfg80211_roamed(struct wireless_dev *wdev, 557adbde344SVasanthakumar Thiagarajan struct cfg80211_bss *bss, 558b23aa676SSamuel Ortiz const u8 *req_ie, size_t req_ie_len, 559667503ddSJohannes Berg const u8 *resp_ie, size_t resp_ie_len) 560b23aa676SSamuel Ortiz { 5613d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT 562b23aa676SSamuel Ortiz union iwreq_data wrqu; 563b23aa676SSamuel Ortiz #endif 564667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 565667503ddSJohannes Berg 566074ac8dfSJohannes Berg if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && 567074ac8dfSJohannes Berg wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) 568adbde344SVasanthakumar Thiagarajan goto out; 569b23aa676SSamuel Ortiz 570f7969969SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTED) 571adbde344SVasanthakumar Thiagarajan goto out; 572b23aa676SSamuel Ortiz 573b23aa676SSamuel Ortiz /* internal error -- how did we get to CONNECTED w/o BSS? */ 574b23aa676SSamuel Ortiz if (WARN_ON(!wdev->current_bss)) { 575adbde344SVasanthakumar Thiagarajan goto out; 576b23aa676SSamuel Ortiz } 577b23aa676SSamuel Ortiz 578b23aa676SSamuel Ortiz cfg80211_unhold_bss(wdev->current_bss); 57919957bb3SJohannes Berg cfg80211_put_bss(&wdev->current_bss->pub); 580b23aa676SSamuel Ortiz wdev->current_bss = NULL; 581b23aa676SSamuel Ortiz 58219957bb3SJohannes Berg cfg80211_hold_bss(bss_from_pub(bss)); 58319957bb3SJohannes Berg wdev->current_bss = bss_from_pub(bss); 584b23aa676SSamuel Ortiz 585adbde344SVasanthakumar Thiagarajan nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bss->bssid, 586667503ddSJohannes Berg req_ie, req_ie_len, resp_ie, resp_ie_len, 587667503ddSJohannes Berg GFP_KERNEL); 588b23aa676SSamuel Ortiz 5893d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT 590b23aa676SSamuel Ortiz if (req_ie) { 591b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 592b23aa676SSamuel Ortiz wrqu.data.length = req_ie_len; 5933409ff77SZhu Yi wireless_send_event(wdev->netdev, IWEVASSOCREQIE, 594667503ddSJohannes Berg &wrqu, req_ie); 595b23aa676SSamuel Ortiz } 596b23aa676SSamuel Ortiz 597b23aa676SSamuel Ortiz if (resp_ie) { 598b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 599b23aa676SSamuel Ortiz wrqu.data.length = resp_ie_len; 600667503ddSJohannes Berg wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, 601667503ddSJohannes Berg &wrqu, resp_ie); 602b23aa676SSamuel Ortiz } 603b23aa676SSamuel Ortiz 604b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 605b23aa676SSamuel Ortiz wrqu.ap_addr.sa_family = ARPHRD_ETHER; 606adbde344SVasanthakumar Thiagarajan memcpy(wrqu.ap_addr.sa_data, bss->bssid, ETH_ALEN); 607adbde344SVasanthakumar Thiagarajan memcpy(wdev->wext.prev_bssid, bss->bssid, ETH_ALEN); 608f401a6f7SJohannes Berg wdev->wext.prev_bssid_valid = true; 609667503ddSJohannes Berg wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); 610b23aa676SSamuel Ortiz #endif 611adbde344SVasanthakumar Thiagarajan 612adbde344SVasanthakumar Thiagarajan return; 613adbde344SVasanthakumar Thiagarajan out: 614adbde344SVasanthakumar Thiagarajan cfg80211_put_bss(bss); 615b23aa676SSamuel Ortiz } 616667503ddSJohannes Berg 617ed9d0102SJouni Malinen void cfg80211_roamed(struct net_device *dev, 618ed9d0102SJouni Malinen struct ieee80211_channel *channel, 619ed9d0102SJouni Malinen const u8 *bssid, 620667503ddSJohannes Berg const u8 *req_ie, size_t req_ie_len, 621667503ddSJohannes Berg const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) 622667503ddSJohannes Berg { 623667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 624adbde344SVasanthakumar Thiagarajan struct cfg80211_bss *bss; 625adbde344SVasanthakumar Thiagarajan 626adbde344SVasanthakumar Thiagarajan CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); 627adbde344SVasanthakumar Thiagarajan 628adbde344SVasanthakumar Thiagarajan bss = cfg80211_get_bss(wdev->wiphy, channel, bssid, wdev->ssid, 629adbde344SVasanthakumar Thiagarajan wdev->ssid_len, WLAN_CAPABILITY_ESS, 630adbde344SVasanthakumar Thiagarajan WLAN_CAPABILITY_ESS); 631adbde344SVasanthakumar Thiagarajan if (WARN_ON(!bss)) 632adbde344SVasanthakumar Thiagarajan return; 633adbde344SVasanthakumar Thiagarajan 634adbde344SVasanthakumar Thiagarajan cfg80211_roamed_bss(dev, bss, req_ie, req_ie_len, resp_ie, 635adbde344SVasanthakumar Thiagarajan resp_ie_len, gfp); 636adbde344SVasanthakumar Thiagarajan } 637adbde344SVasanthakumar Thiagarajan EXPORT_SYMBOL(cfg80211_roamed); 638adbde344SVasanthakumar Thiagarajan 639adbde344SVasanthakumar Thiagarajan void cfg80211_roamed_bss(struct net_device *dev, 640adbde344SVasanthakumar Thiagarajan struct cfg80211_bss *bss, const u8 *req_ie, 641adbde344SVasanthakumar Thiagarajan size_t req_ie_len, const u8 *resp_ie, 642adbde344SVasanthakumar Thiagarajan size_t resp_ie_len, gfp_t gfp) 643adbde344SVasanthakumar Thiagarajan { 644adbde344SVasanthakumar Thiagarajan struct wireless_dev *wdev = dev->ieee80211_ptr; 645667503ddSJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 646667503ddSJohannes Berg struct cfg80211_event *ev; 647667503ddSJohannes Berg unsigned long flags; 648667503ddSJohannes Berg 649f7969969SJohannes Berg CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); 650f7969969SJohannes Berg 651adbde344SVasanthakumar Thiagarajan if (WARN_ON(!bss)) 652667503ddSJohannes Berg return; 653667503ddSJohannes Berg 654adbde344SVasanthakumar Thiagarajan ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); 655adbde344SVasanthakumar Thiagarajan if (!ev) { 656adbde344SVasanthakumar Thiagarajan cfg80211_put_bss(bss); 657adbde344SVasanthakumar Thiagarajan return; 658adbde344SVasanthakumar Thiagarajan } 659adbde344SVasanthakumar Thiagarajan 660667503ddSJohannes Berg ev->type = EVENT_ROAMED; 661667503ddSJohannes Berg ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev); 662667503ddSJohannes Berg ev->rm.req_ie_len = req_ie_len; 663667503ddSJohannes Berg memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len); 664667503ddSJohannes Berg ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; 665667503ddSJohannes Berg ev->rm.resp_ie_len = resp_ie_len; 666667503ddSJohannes Berg memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len); 667adbde344SVasanthakumar Thiagarajan ev->rm.bss = bss; 668667503ddSJohannes Berg 669667503ddSJohannes Berg spin_lock_irqsave(&wdev->event_lock, flags); 670667503ddSJohannes Berg list_add_tail(&ev->list, &wdev->event_list); 671667503ddSJohannes Berg spin_unlock_irqrestore(&wdev->event_lock, flags); 672e60d7443SAlban Browaeys queue_work(cfg80211_wq, &rdev->event_work); 673667503ddSJohannes Berg } 674adbde344SVasanthakumar Thiagarajan EXPORT_SYMBOL(cfg80211_roamed_bss); 675b23aa676SSamuel Ortiz 676667503ddSJohannes Berg void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, 6776829c878SJohannes Berg size_t ie_len, u16 reason, bool from_ap) 678b23aa676SSamuel Ortiz { 679b23aa676SSamuel Ortiz struct wireless_dev *wdev = dev->ieee80211_ptr; 680fffd0934SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 681fffd0934SJohannes Berg int i; 6823d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT 683b23aa676SSamuel Ortiz union iwreq_data wrqu; 684b23aa676SSamuel Ortiz #endif 685b23aa676SSamuel Ortiz 686667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 687667503ddSJohannes Berg 688074ac8dfSJohannes Berg if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && 689074ac8dfSJohannes Berg wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) 690b23aa676SSamuel Ortiz return; 691b23aa676SSamuel Ortiz 692f7969969SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTED) 693b23aa676SSamuel Ortiz return; 694b23aa676SSamuel Ortiz 695b23aa676SSamuel Ortiz if (wdev->current_bss) { 696b23aa676SSamuel Ortiz cfg80211_unhold_bss(wdev->current_bss); 69719957bb3SJohannes Berg cfg80211_put_bss(&wdev->current_bss->pub); 698b23aa676SSamuel Ortiz } 699b23aa676SSamuel Ortiz 700b23aa676SSamuel Ortiz wdev->current_bss = NULL; 701b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_IDLE; 7028dadadb7SJohannes Berg wdev->ssid_len = 0; 703b23aa676SSamuel Ortiz 7046829c878SJohannes Berg if (wdev->conn) { 7056829c878SJohannes Berg kfree(wdev->conn->ie); 7066829c878SJohannes Berg wdev->conn->ie = NULL; 70719957bb3SJohannes Berg kfree(wdev->conn); 70819957bb3SJohannes Berg wdev->conn = NULL; 7096829c878SJohannes Berg } 7106829c878SJohannes Berg 711fffd0934SJohannes Berg nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); 712fffd0934SJohannes Berg 713fffd0934SJohannes Berg /* 714fffd0934SJohannes Berg * Delete all the keys ... pairwise keys can't really 715fffd0934SJohannes Berg * exist any more anyway, but default keys might. 716fffd0934SJohannes Berg */ 717fffd0934SJohannes Berg if (rdev->ops->del_key) 718fffd0934SJohannes Berg for (i = 0; i < 6; i++) 719e31b8213SJohannes Berg rdev->ops->del_key(wdev->wiphy, dev, i, false, NULL); 720b23aa676SSamuel Ortiz 7213d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT 722b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 723b23aa676SSamuel Ortiz wrqu.ap_addr.sa_family = ARPHRD_ETHER; 724b23aa676SSamuel Ortiz wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); 7255f612033SAbhijeet Kolekar wdev->wext.connect.ssid_len = 0; 726b23aa676SSamuel Ortiz #endif 72709d989d1SLuis R. Rodriguez 72809d989d1SLuis R. Rodriguez schedule_work(&cfg80211_disconnect_work); 729b23aa676SSamuel Ortiz } 730b23aa676SSamuel Ortiz 731b23aa676SSamuel Ortiz void cfg80211_disconnected(struct net_device *dev, u16 reason, 732b23aa676SSamuel Ortiz u8 *ie, size_t ie_len, gfp_t gfp) 733b23aa676SSamuel Ortiz { 734667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 735667503ddSJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 736667503ddSJohannes Berg struct cfg80211_event *ev; 737667503ddSJohannes Berg unsigned long flags; 738667503ddSJohannes Berg 739f7969969SJohannes Berg CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); 740f7969969SJohannes Berg 741667503ddSJohannes Berg ev = kzalloc(sizeof(*ev) + ie_len, gfp); 742667503ddSJohannes Berg if (!ev) 743667503ddSJohannes Berg return; 744667503ddSJohannes Berg 745667503ddSJohannes Berg ev->type = EVENT_DISCONNECTED; 746667503ddSJohannes Berg ev->dc.ie = ((u8 *)ev) + sizeof(*ev); 747667503ddSJohannes Berg ev->dc.ie_len = ie_len; 748667503ddSJohannes Berg memcpy((void *)ev->dc.ie, ie, ie_len); 749667503ddSJohannes Berg ev->dc.reason = reason; 750667503ddSJohannes Berg 751667503ddSJohannes Berg spin_lock_irqsave(&wdev->event_lock, flags); 752667503ddSJohannes Berg list_add_tail(&ev->list, &wdev->event_list); 753667503ddSJohannes Berg spin_unlock_irqrestore(&wdev->event_lock, flags); 754e60d7443SAlban Browaeys queue_work(cfg80211_wq, &rdev->event_work); 755b23aa676SSamuel Ortiz } 756b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_disconnected); 757b23aa676SSamuel Ortiz 758667503ddSJohannes Berg int __cfg80211_connect(struct cfg80211_registered_device *rdev, 759b23aa676SSamuel Ortiz struct net_device *dev, 760fffd0934SJohannes Berg struct cfg80211_connect_params *connect, 761f401a6f7SJohannes Berg struct cfg80211_cached_keys *connkeys, 762f401a6f7SJohannes Berg const u8 *prev_bssid) 763b23aa676SSamuel Ortiz { 764b23aa676SSamuel Ortiz struct wireless_dev *wdev = dev->ieee80211_ptr; 765bbac31f4SJohannes Berg struct cfg80211_bss *bss = NULL; 766667503ddSJohannes Berg int err; 767667503ddSJohannes Berg 768667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 769b23aa676SSamuel Ortiz 770b23aa676SSamuel Ortiz if (wdev->sme_state != CFG80211_SME_IDLE) 771b23aa676SSamuel Ortiz return -EALREADY; 772b23aa676SSamuel Ortiz 773fffd0934SJohannes Berg if (WARN_ON(wdev->connect_keys)) { 774fffd0934SJohannes Berg kfree(wdev->connect_keys); 775fffd0934SJohannes Berg wdev->connect_keys = NULL; 776fffd0934SJohannes Berg } 777fffd0934SJohannes Berg 7787e7c8926SBen Greear cfg80211_oper_and_ht_capa(&connect->ht_capa_mask, 7797e7c8926SBen Greear rdev->wiphy.ht_capa_mod_mask); 7807e7c8926SBen Greear 781fffd0934SJohannes Berg if (connkeys && connkeys->def >= 0) { 782fffd0934SJohannes Berg int idx; 783bcba8eaeSSamuel Ortiz u32 cipher; 784fffd0934SJohannes Berg 785fffd0934SJohannes Berg idx = connkeys->def; 786bcba8eaeSSamuel Ortiz cipher = connkeys->params[idx].cipher; 787fffd0934SJohannes Berg /* If given a WEP key we may need it for shared key auth */ 788bcba8eaeSSamuel Ortiz if (cipher == WLAN_CIPHER_SUITE_WEP40 || 789bcba8eaeSSamuel Ortiz cipher == WLAN_CIPHER_SUITE_WEP104) { 790fffd0934SJohannes Berg connect->key_idx = idx; 791fffd0934SJohannes Berg connect->key = connkeys->params[idx].key; 792fffd0934SJohannes Berg connect->key_len = connkeys->params[idx].key_len; 793bcba8eaeSSamuel Ortiz 794bcba8eaeSSamuel Ortiz /* 795bcba8eaeSSamuel Ortiz * If ciphers are not set (e.g. when going through 796bcba8eaeSSamuel Ortiz * iwconfig), we have to set them appropriately here. 797bcba8eaeSSamuel Ortiz */ 798bcba8eaeSSamuel Ortiz if (connect->crypto.cipher_group == 0) 799bcba8eaeSSamuel Ortiz connect->crypto.cipher_group = cipher; 800bcba8eaeSSamuel Ortiz 801bcba8eaeSSamuel Ortiz if (connect->crypto.n_ciphers_pairwise == 0) { 802bcba8eaeSSamuel Ortiz connect->crypto.n_ciphers_pairwise = 1; 803bcba8eaeSSamuel Ortiz connect->crypto.ciphers_pairwise[0] = cipher; 804bcba8eaeSSamuel Ortiz } 805fffd0934SJohannes Berg } 806fffd0934SJohannes Berg } 807fffd0934SJohannes Berg 808b23aa676SSamuel Ortiz if (!rdev->ops->connect) { 8096829c878SJohannes Berg if (!rdev->ops->auth || !rdev->ops->assoc) 810b23aa676SSamuel Ortiz return -EOPNOTSUPP; 8116829c878SJohannes Berg 81219957bb3SJohannes Berg if (WARN_ON(wdev->conn)) 81319957bb3SJohannes Berg return -EINPROGRESS; 81419957bb3SJohannes Berg 8156829c878SJohannes Berg wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); 8166829c878SJohannes Berg if (!wdev->conn) 8176829c878SJohannes Berg return -ENOMEM; 8186829c878SJohannes Berg 8196829c878SJohannes Berg /* 8206829c878SJohannes Berg * Copy all parameters, and treat explicitly IEs, BSSID, SSID. 8216829c878SJohannes Berg */ 8226829c878SJohannes Berg memcpy(&wdev->conn->params, connect, sizeof(*connect)); 8236829c878SJohannes Berg if (connect->bssid) { 8246829c878SJohannes Berg wdev->conn->params.bssid = wdev->conn->bssid; 8256829c878SJohannes Berg memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); 8266829c878SJohannes Berg } 8276829c878SJohannes Berg 8286829c878SJohannes Berg if (connect->ie) { 8296829c878SJohannes Berg wdev->conn->ie = kmemdup(connect->ie, connect->ie_len, 8306829c878SJohannes Berg GFP_KERNEL); 8316829c878SJohannes Berg wdev->conn->params.ie = wdev->conn->ie; 83219957bb3SJohannes Berg if (!wdev->conn->ie) { 83319957bb3SJohannes Berg kfree(wdev->conn); 83419957bb3SJohannes Berg wdev->conn = NULL; 8356829c878SJohannes Berg return -ENOMEM; 8366829c878SJohannes Berg } 83719957bb3SJohannes Berg } 8386829c878SJohannes Berg 8396829c878SJohannes Berg if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { 8406829c878SJohannes Berg wdev->conn->auto_auth = true; 8416829c878SJohannes Berg /* start with open system ... should mostly work */ 8426829c878SJohannes Berg wdev->conn->params.auth_type = 8436829c878SJohannes Berg NL80211_AUTHTYPE_OPEN_SYSTEM; 8446829c878SJohannes Berg } else { 8456829c878SJohannes Berg wdev->conn->auto_auth = false; 8466829c878SJohannes Berg } 8476829c878SJohannes Berg 8486829c878SJohannes Berg memcpy(wdev->ssid, connect->ssid, connect->ssid_len); 8496829c878SJohannes Berg wdev->ssid_len = connect->ssid_len; 8506829c878SJohannes Berg wdev->conn->params.ssid = wdev->ssid; 8516829c878SJohannes Berg wdev->conn->params.ssid_len = connect->ssid_len; 8526829c878SJohannes Berg 8538bb89485SJohannes Berg /* see if we have the bss already */ 854bbac31f4SJohannes Berg bss = cfg80211_get_conn_bss(wdev); 8556829c878SJohannes Berg 8566829c878SJohannes Berg wdev->sme_state = CFG80211_SME_CONNECTING; 857fffd0934SJohannes Berg wdev->connect_keys = connkeys; 8586829c878SJohannes Berg 859f401a6f7SJohannes Berg if (prev_bssid) { 860f401a6f7SJohannes Berg memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN); 861f401a6f7SJohannes Berg wdev->conn->prev_bssid_valid = true; 862f401a6f7SJohannes Berg } 863f401a6f7SJohannes Berg 864bbac31f4SJohannes Berg /* we're good if we have a matching bss struct */ 865bbac31f4SJohannes Berg if (bss) { 8666829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; 8676829c878SJohannes Berg err = cfg80211_conn_do_work(wdev); 868bbac31f4SJohannes Berg cfg80211_put_bss(bss); 8696829c878SJohannes Berg } else { 8706829c878SJohannes Berg /* otherwise we'll need to scan for the AP first */ 8716829c878SJohannes Berg err = cfg80211_conn_scan(wdev); 8726829c878SJohannes Berg /* 8736829c878SJohannes Berg * If we can't scan right now, then we need to scan again 8746829c878SJohannes Berg * after the current scan finished, since the parameters 8756829c878SJohannes Berg * changed (unless we find a good AP anyway). 8766829c878SJohannes Berg */ 8776829c878SJohannes Berg if (err == -EBUSY) { 8786829c878SJohannes Berg err = 0; 8796829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; 8806829c878SJohannes Berg } 8816829c878SJohannes Berg } 88219957bb3SJohannes Berg if (err) { 883415ad1efSDavid Kilroy kfree(wdev->conn->ie); 88419957bb3SJohannes Berg kfree(wdev->conn); 88519957bb3SJohannes Berg wdev->conn = NULL; 8866829c878SJohannes Berg wdev->sme_state = CFG80211_SME_IDLE; 887fffd0934SJohannes Berg wdev->connect_keys = NULL; 8888dadadb7SJohannes Berg wdev->ssid_len = 0; 88919957bb3SJohannes Berg } 8906829c878SJohannes Berg 8916829c878SJohannes Berg return err; 892b23aa676SSamuel Ortiz } else { 893b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_CONNECTING; 894fffd0934SJohannes Berg wdev->connect_keys = connkeys; 895b23aa676SSamuel Ortiz err = rdev->ops->connect(&rdev->wiphy, dev, connect); 896b23aa676SSamuel Ortiz if (err) { 897fffd0934SJohannes Berg wdev->connect_keys = NULL; 898b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_IDLE; 899b23aa676SSamuel Ortiz return err; 900b23aa676SSamuel Ortiz } 901b23aa676SSamuel Ortiz 902b23aa676SSamuel Ortiz memcpy(wdev->ssid, connect->ssid, connect->ssid_len); 903b23aa676SSamuel Ortiz wdev->ssid_len = connect->ssid_len; 904b23aa676SSamuel Ortiz 905b23aa676SSamuel Ortiz return 0; 906b23aa676SSamuel Ortiz } 9076829c878SJohannes Berg } 908b23aa676SSamuel Ortiz 909667503ddSJohannes Berg int cfg80211_connect(struct cfg80211_registered_device *rdev, 910667503ddSJohannes Berg struct net_device *dev, 911fffd0934SJohannes Berg struct cfg80211_connect_params *connect, 912fffd0934SJohannes Berg struct cfg80211_cached_keys *connkeys) 913667503ddSJohannes Berg { 914667503ddSJohannes Berg int err; 915667503ddSJohannes Berg 91659bbb6f7SJohannes Berg mutex_lock(&rdev->devlist_mtx); 917667503ddSJohannes Berg wdev_lock(dev->ieee80211_ptr); 918f401a6f7SJohannes Berg err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL); 919667503ddSJohannes Berg wdev_unlock(dev->ieee80211_ptr); 92059bbb6f7SJohannes Berg mutex_unlock(&rdev->devlist_mtx); 921667503ddSJohannes Berg 922667503ddSJohannes Berg return err; 923667503ddSJohannes Berg } 924667503ddSJohannes Berg 925667503ddSJohannes Berg int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, 926f2129354SJohannes Berg struct net_device *dev, u16 reason, bool wextev) 927b23aa676SSamuel Ortiz { 9286829c878SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 929b23aa676SSamuel Ortiz int err; 930b23aa676SSamuel Ortiz 931667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 932667503ddSJohannes Berg 9336829c878SJohannes Berg if (wdev->sme_state == CFG80211_SME_IDLE) 9346829c878SJohannes Berg return -EINVAL; 9356829c878SJohannes Berg 936fffd0934SJohannes Berg kfree(wdev->connect_keys); 937fffd0934SJohannes Berg wdev->connect_keys = NULL; 938fffd0934SJohannes Berg 939b23aa676SSamuel Ortiz if (!rdev->ops->disconnect) { 94019957bb3SJohannes Berg if (!rdev->ops->deauth) 94119957bb3SJohannes Berg return -EOPNOTSUPP; 9426829c878SJohannes Berg 94319957bb3SJohannes Berg /* was it connected by userspace SME? */ 94419957bb3SJohannes Berg if (!wdev->conn) { 94519957bb3SJohannes Berg cfg80211_mlme_down(rdev, dev); 94619957bb3SJohannes Berg return 0; 94719957bb3SJohannes Berg } 9486829c878SJohannes Berg 9496829c878SJohannes Berg if (wdev->sme_state == CFG80211_SME_CONNECTING && 9506829c878SJohannes Berg (wdev->conn->state == CFG80211_CONN_SCANNING || 9516829c878SJohannes Berg wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) { 9526829c878SJohannes Berg wdev->sme_state = CFG80211_SME_IDLE; 953415ad1efSDavid Kilroy kfree(wdev->conn->ie); 95419957bb3SJohannes Berg kfree(wdev->conn); 95519957bb3SJohannes Berg wdev->conn = NULL; 9568dadadb7SJohannes Berg wdev->ssid_len = 0; 9576829c878SJohannes Berg return 0; 9586829c878SJohannes Berg } 9596829c878SJohannes Berg 9606829c878SJohannes Berg /* wdev->conn->params.bssid must be set if > SCANNING */ 961667503ddSJohannes Berg err = __cfg80211_mlme_deauth(rdev, dev, 962667503ddSJohannes Berg wdev->conn->params.bssid, 963d5cdfacbSJouni Malinen NULL, 0, reason, false); 9646829c878SJohannes Berg if (err) 9656829c878SJohannes Berg return err; 966b23aa676SSamuel Ortiz } else { 967b23aa676SSamuel Ortiz err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); 968b23aa676SSamuel Ortiz if (err) 969b23aa676SSamuel Ortiz return err; 970b23aa676SSamuel Ortiz } 971b23aa676SSamuel Ortiz 9726829c878SJohannes Berg if (wdev->sme_state == CFG80211_SME_CONNECTED) 973667503ddSJohannes Berg __cfg80211_disconnected(dev, NULL, 0, 0, false); 9746829c878SJohannes Berg else if (wdev->sme_state == CFG80211_SME_CONNECTING) 975f2129354SJohannes Berg __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, 9766829c878SJohannes Berg WLAN_STATUS_UNSPECIFIED_FAILURE, 977df7fc0f9SJohannes Berg wextev, NULL); 978b23aa676SSamuel Ortiz 979b23aa676SSamuel Ortiz return 0; 980b23aa676SSamuel Ortiz } 98119957bb3SJohannes Berg 982667503ddSJohannes Berg int cfg80211_disconnect(struct cfg80211_registered_device *rdev, 983667503ddSJohannes Berg struct net_device *dev, 984667503ddSJohannes Berg u16 reason, bool wextev) 985667503ddSJohannes Berg { 986667503ddSJohannes Berg int err; 987667503ddSJohannes Berg 988667503ddSJohannes Berg wdev_lock(dev->ieee80211_ptr); 989667503ddSJohannes Berg err = __cfg80211_disconnect(rdev, dev, reason, wextev); 990667503ddSJohannes Berg wdev_unlock(dev->ieee80211_ptr); 991667503ddSJohannes Berg 992667503ddSJohannes Berg return err; 993667503ddSJohannes Berg } 994667503ddSJohannes Berg 99595de817bSJohannes Berg void cfg80211_sme_disassoc(struct net_device *dev, 99695de817bSJohannes Berg struct cfg80211_internal_bss *bss) 99719957bb3SJohannes Berg { 99819957bb3SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 99919957bb3SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 100019957bb3SJohannes Berg u8 bssid[ETH_ALEN]; 100119957bb3SJohannes Berg 1002667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 1003667503ddSJohannes Berg 100419957bb3SJohannes Berg if (!wdev->conn) 100519957bb3SJohannes Berg return; 100619957bb3SJohannes Berg 100719957bb3SJohannes Berg if (wdev->conn->state == CFG80211_CONN_IDLE) 100819957bb3SJohannes Berg return; 100919957bb3SJohannes Berg 101019957bb3SJohannes Berg /* 101119957bb3SJohannes Berg * Ok, so the association was made by this SME -- we don't 101219957bb3SJohannes Berg * want it any more so deauthenticate too. 101319957bb3SJohannes Berg */ 101419957bb3SJohannes Berg 101595de817bSJohannes Berg memcpy(bssid, bss->pub.bssid, ETH_ALEN); 101619957bb3SJohannes Berg 101795de817bSJohannes Berg __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0, 101895de817bSJohannes Berg WLAN_REASON_DEAUTH_LEAVING, false); 101919957bb3SJohannes Berg } 1020