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> 10b23aa676SSamuel Ortiz #include <linux/workqueue.h> 11a9a11622SJohannes Berg #include <linux/wireless.h> 12a9a11622SJohannes Berg #include <net/iw_handler.h> 13b23aa676SSamuel Ortiz #include <net/cfg80211.h> 14b23aa676SSamuel Ortiz #include <net/rtnetlink.h> 15b23aa676SSamuel Ortiz #include "nl80211.h" 168b19e6caSLuis R. Rodriguez #include "reg.h" 17b23aa676SSamuel Ortiz 186829c878SJohannes Berg struct cfg80211_conn { 196829c878SJohannes Berg struct cfg80211_connect_params params; 206829c878SJohannes Berg /* these are sub-states of the _CONNECTING sme_state */ 216829c878SJohannes Berg enum { 226829c878SJohannes Berg CFG80211_CONN_IDLE, 236829c878SJohannes Berg CFG80211_CONN_SCANNING, 246829c878SJohannes Berg CFG80211_CONN_SCAN_AGAIN, 256829c878SJohannes Berg CFG80211_CONN_AUTHENTICATE_NEXT, 266829c878SJohannes Berg CFG80211_CONN_AUTHENTICATING, 276829c878SJohannes Berg CFG80211_CONN_ASSOCIATE_NEXT, 286829c878SJohannes Berg CFG80211_CONN_ASSOCIATING, 297d930bc3SJohannes Berg CFG80211_CONN_DEAUTH_ASSOC_FAIL, 306829c878SJohannes Berg } state; 31f401a6f7SJohannes Berg u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; 326829c878SJohannes Berg u8 *ie; 336829c878SJohannes Berg size_t ie_len; 34f401a6f7SJohannes Berg bool auto_auth, prev_bssid_valid; 356829c878SJohannes Berg }; 366829c878SJohannes Berg 3709d989d1SLuis R. Rodriguez bool cfg80211_is_all_idle(void) 3809d989d1SLuis R. Rodriguez { 3909d989d1SLuis R. Rodriguez struct cfg80211_registered_device *rdev; 4009d989d1SLuis R. Rodriguez struct wireless_dev *wdev; 4109d989d1SLuis R. Rodriguez bool is_all_idle = true; 4209d989d1SLuis R. Rodriguez 4309d989d1SLuis R. Rodriguez mutex_lock(&cfg80211_mutex); 4409d989d1SLuis R. Rodriguez 4509d989d1SLuis R. Rodriguez /* 4609d989d1SLuis R. Rodriguez * All devices must be idle as otherwise if you are actively 4709d989d1SLuis R. Rodriguez * scanning some new beacon hints could be learned and would 4809d989d1SLuis R. Rodriguez * count as new regulatory hints. 4909d989d1SLuis R. Rodriguez */ 5009d989d1SLuis R. Rodriguez list_for_each_entry(rdev, &cfg80211_rdev_list, list) { 5109d989d1SLuis R. Rodriguez cfg80211_lock_rdev(rdev); 5209d989d1SLuis R. Rodriguez list_for_each_entry(wdev, &rdev->netdev_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 cfg80211_unlock_rdev(rdev); 5909d989d1SLuis R. Rodriguez } 6009d989d1SLuis R. Rodriguez 6109d989d1SLuis R. Rodriguez mutex_unlock(&cfg80211_mutex); 6209d989d1SLuis R. Rodriguez 6309d989d1SLuis R. Rodriguez return is_all_idle; 6409d989d1SLuis R. Rodriguez } 6509d989d1SLuis R. Rodriguez 6609d989d1SLuis R. Rodriguez static void disconnect_work(struct work_struct *work) 6709d989d1SLuis R. Rodriguez { 6809d989d1SLuis R. Rodriguez if (!cfg80211_is_all_idle()) 6909d989d1SLuis R. Rodriguez return; 7009d989d1SLuis R. Rodriguez 7109d989d1SLuis R. Rodriguez regulatory_hint_disconnect(); 7209d989d1SLuis R. Rodriguez } 7309d989d1SLuis R. Rodriguez 7409d989d1SLuis R. Rodriguez static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work); 756829c878SJohannes Berg 766829c878SJohannes Berg static int cfg80211_conn_scan(struct wireless_dev *wdev) 776829c878SJohannes Berg { 7879c97e97SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 796829c878SJohannes Berg struct cfg80211_scan_request *request; 806829c878SJohannes Berg int n_channels, err; 816829c878SJohannes Berg 826829c878SJohannes Berg ASSERT_RTNL(); 8379c97e97SJohannes Berg ASSERT_RDEV_LOCK(rdev); 84667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 856829c878SJohannes Berg 8679c97e97SJohannes Berg if (rdev->scan_req) 876829c878SJohannes Berg return -EBUSY; 886829c878SJohannes Berg 896829c878SJohannes Berg if (wdev->conn->params.channel) { 906829c878SJohannes Berg n_channels = 1; 916829c878SJohannes Berg } else { 926829c878SJohannes Berg enum ieee80211_band band; 936829c878SJohannes Berg n_channels = 0; 946829c878SJohannes Berg 956829c878SJohannes Berg for (band = 0; band < IEEE80211_NUM_BANDS; band++) { 966829c878SJohannes Berg if (!wdev->wiphy->bands[band]) 976829c878SJohannes Berg continue; 986829c878SJohannes Berg n_channels += wdev->wiphy->bands[band]->n_channels; 996829c878SJohannes Berg } 1006829c878SJohannes Berg } 1016829c878SJohannes Berg request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) + 1026829c878SJohannes Berg sizeof(request->channels[0]) * n_channels, 1036829c878SJohannes Berg GFP_KERNEL); 1046829c878SJohannes Berg if (!request) 1056829c878SJohannes Berg return -ENOMEM; 1066829c878SJohannes Berg 1076829c878SJohannes Berg if (wdev->conn->params.channel) 1086829c878SJohannes Berg request->channels[0] = wdev->conn->params.channel; 1096829c878SJohannes Berg else { 1106829c878SJohannes Berg int i = 0, j; 1116829c878SJohannes Berg enum ieee80211_band band; 1126829c878SJohannes Berg 1136829c878SJohannes Berg for (band = 0; band < IEEE80211_NUM_BANDS; band++) { 1146829c878SJohannes Berg if (!wdev->wiphy->bands[band]) 1156829c878SJohannes Berg continue; 1166829c878SJohannes Berg for (j = 0; j < wdev->wiphy->bands[band]->n_channels; 1176829c878SJohannes Berg i++, j++) 1186829c878SJohannes Berg request->channels[i] = 1196829c878SJohannes Berg &wdev->wiphy->bands[band]->channels[j]; 1206829c878SJohannes Berg } 1216829c878SJohannes Berg } 1226829c878SJohannes Berg request->n_channels = n_channels; 1235ba63533SJohannes Berg request->ssids = (void *)&request->channels[n_channels]; 1246829c878SJohannes Berg request->n_ssids = 1; 1256829c878SJohannes Berg 1266829c878SJohannes Berg memcpy(request->ssids[0].ssid, wdev->conn->params.ssid, 1276829c878SJohannes Berg wdev->conn->params.ssid_len); 1286829c878SJohannes Berg request->ssids[0].ssid_len = wdev->conn->params.ssid_len; 1296829c878SJohannes Berg 130463d0183SJohannes Berg request->dev = wdev->netdev; 13179c97e97SJohannes Berg request->wiphy = &rdev->wiphy; 1326829c878SJohannes Berg 13379c97e97SJohannes Berg rdev->scan_req = request; 1346829c878SJohannes Berg 13579c97e97SJohannes Berg err = rdev->ops->scan(wdev->wiphy, wdev->netdev, request); 1366829c878SJohannes Berg if (!err) { 1376829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_SCANNING; 13879c97e97SJohannes Berg nl80211_send_scan_start(rdev, wdev->netdev); 139463d0183SJohannes Berg dev_hold(wdev->netdev); 1406829c878SJohannes Berg } else { 14179c97e97SJohannes Berg rdev->scan_req = NULL; 1426829c878SJohannes Berg kfree(request); 1436829c878SJohannes Berg } 1446829c878SJohannes Berg return err; 1456829c878SJohannes Berg } 1466829c878SJohannes Berg 1476829c878SJohannes Berg static int cfg80211_conn_do_work(struct wireless_dev *wdev) 1486829c878SJohannes Berg { 14979c97e97SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 15019957bb3SJohannes Berg struct cfg80211_connect_params *params; 151f401a6f7SJohannes Berg const u8 *prev_bssid = NULL; 15219957bb3SJohannes Berg int err; 1536829c878SJohannes Berg 154667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 155667503ddSJohannes Berg 1566829c878SJohannes Berg if (!wdev->conn) 1576829c878SJohannes Berg return 0; 1586829c878SJohannes Berg 15919957bb3SJohannes Berg params = &wdev->conn->params; 16019957bb3SJohannes Berg 1616829c878SJohannes Berg switch (wdev->conn->state) { 1626829c878SJohannes Berg case CFG80211_CONN_SCAN_AGAIN: 1636829c878SJohannes Berg return cfg80211_conn_scan(wdev); 1646829c878SJohannes Berg case CFG80211_CONN_AUTHENTICATE_NEXT: 16579c97e97SJohannes Berg BUG_ON(!rdev->ops->auth); 16619957bb3SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATING; 16779c97e97SJohannes Berg return __cfg80211_mlme_auth(rdev, wdev->netdev, 16819957bb3SJohannes Berg params->channel, params->auth_type, 16919957bb3SJohannes Berg params->bssid, 17019957bb3SJohannes Berg params->ssid, params->ssid_len, 171fffd0934SJohannes Berg NULL, 0, 172fffd0934SJohannes Berg params->key, params->key_len, 173d5cdfacbSJouni Malinen params->key_idx, false); 1746829c878SJohannes Berg case CFG80211_CONN_ASSOCIATE_NEXT: 17579c97e97SJohannes Berg BUG_ON(!rdev->ops->assoc); 17619957bb3SJohannes Berg wdev->conn->state = CFG80211_CONN_ASSOCIATING; 177f401a6f7SJohannes Berg if (wdev->conn->prev_bssid_valid) 178f401a6f7SJohannes Berg prev_bssid = wdev->conn->prev_bssid; 17979c97e97SJohannes Berg err = __cfg80211_mlme_assoc(rdev, wdev->netdev, 180667503ddSJohannes Berg params->channel, params->bssid, 181f401a6f7SJohannes Berg prev_bssid, 18219957bb3SJohannes Berg params->ssid, params->ssid_len, 18319957bb3SJohannes Berg params->ie, params->ie_len, 18419957bb3SJohannes Berg false, ¶ms->crypto); 18519957bb3SJohannes Berg if (err) 18679c97e97SJohannes Berg __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, 187667503ddSJohannes Berg NULL, 0, 188d5cdfacbSJouni Malinen WLAN_REASON_DEAUTH_LEAVING, 189d5cdfacbSJouni Malinen false); 19019957bb3SJohannes Berg return err; 1917d930bc3SJohannes Berg case CFG80211_CONN_DEAUTH_ASSOC_FAIL: 1927d930bc3SJohannes Berg __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, 1937d930bc3SJohannes Berg NULL, 0, 194d5cdfacbSJouni Malinen WLAN_REASON_DEAUTH_LEAVING, false); 1957d930bc3SJohannes Berg /* return an error so that we call __cfg80211_connect_result() */ 1967d930bc3SJohannes Berg return -EINVAL; 1976829c878SJohannes Berg default: 1986829c878SJohannes Berg return 0; 1996829c878SJohannes Berg } 2006829c878SJohannes Berg } 2016829c878SJohannes Berg 2026829c878SJohannes Berg void cfg80211_conn_work(struct work_struct *work) 2036829c878SJohannes Berg { 20479c97e97SJohannes Berg struct cfg80211_registered_device *rdev = 2056829c878SJohannes Berg container_of(work, struct cfg80211_registered_device, conn_work); 2066829c878SJohannes Berg struct wireless_dev *wdev; 2077400f42eSJohannes Berg u8 bssid_buf[ETH_ALEN], *bssid = NULL; 2086829c878SJohannes Berg 2096829c878SJohannes Berg rtnl_lock(); 21079c97e97SJohannes Berg cfg80211_lock_rdev(rdev); 21179c97e97SJohannes Berg mutex_lock(&rdev->devlist_mtx); 2126829c878SJohannes Berg 21379c97e97SJohannes Berg list_for_each_entry(wdev, &rdev->netdev_list, list) { 214667503ddSJohannes Berg wdev_lock(wdev); 215667503ddSJohannes Berg if (!netif_running(wdev->netdev)) { 216667503ddSJohannes Berg wdev_unlock(wdev); 2176829c878SJohannes Berg continue; 218667503ddSJohannes Berg } 219667503ddSJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTING) { 220667503ddSJohannes Berg wdev_unlock(wdev); 2216829c878SJohannes Berg continue; 222667503ddSJohannes Berg } 2237400f42eSJohannes Berg if (wdev->conn->params.bssid) { 2247400f42eSJohannes Berg memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN); 2257400f42eSJohannes Berg bssid = bssid_buf; 2267400f42eSJohannes Berg } 2276829c878SJohannes Berg if (cfg80211_conn_do_work(wdev)) 228667503ddSJohannes Berg __cfg80211_connect_result( 2297d930bc3SJohannes Berg wdev->netdev, bssid, 2306829c878SJohannes Berg NULL, 0, NULL, 0, 2316829c878SJohannes Berg WLAN_STATUS_UNSPECIFIED_FAILURE, 232df7fc0f9SJohannes Berg false, NULL); 233667503ddSJohannes Berg wdev_unlock(wdev); 2346829c878SJohannes Berg } 2356829c878SJohannes Berg 23679c97e97SJohannes Berg mutex_unlock(&rdev->devlist_mtx); 23779c97e97SJohannes Berg cfg80211_unlock_rdev(rdev); 2386829c878SJohannes Berg rtnl_unlock(); 2396829c878SJohannes Berg } 2406829c878SJohannes Berg 241bbac31f4SJohannes Berg static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev) 2426829c878SJohannes Berg { 24379c97e97SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 2446829c878SJohannes Berg struct cfg80211_bss *bss; 2456829c878SJohannes Berg u16 capa = WLAN_CAPABILITY_ESS; 2466829c878SJohannes Berg 247667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 248667503ddSJohannes Berg 2496829c878SJohannes Berg if (wdev->conn->params.privacy) 2506829c878SJohannes Berg capa |= WLAN_CAPABILITY_PRIVACY; 2516829c878SJohannes Berg 2526829c878SJohannes Berg bss = cfg80211_get_bss(wdev->wiphy, NULL, wdev->conn->params.bssid, 2536829c878SJohannes Berg wdev->conn->params.ssid, 2546829c878SJohannes Berg wdev->conn->params.ssid_len, 2556829c878SJohannes Berg WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, 2566829c878SJohannes Berg capa); 2576829c878SJohannes Berg if (!bss) 258bbac31f4SJohannes Berg return NULL; 2596829c878SJohannes Berg 2606829c878SJohannes Berg memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN); 2616829c878SJohannes Berg wdev->conn->params.bssid = wdev->conn->bssid; 2626829c878SJohannes Berg wdev->conn->params.channel = bss->channel; 2636829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; 26479c97e97SJohannes Berg schedule_work(&rdev->conn_work); 2656829c878SJohannes Berg 266bbac31f4SJohannes Berg return bss; 2676829c878SJohannes Berg } 2686829c878SJohannes Berg 269667503ddSJohannes Berg static void __cfg80211_sme_scan_done(struct net_device *dev) 2706829c878SJohannes Berg { 2716829c878SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 27279c97e97SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 273bbac31f4SJohannes Berg struct cfg80211_bss *bss; 2746829c878SJohannes Berg 275667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 276667503ddSJohannes Berg 2776829c878SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTING) 2786829c878SJohannes Berg return; 2796829c878SJohannes Berg 280d4b1a687SZhu Yi if (!wdev->conn) 2816829c878SJohannes Berg return; 2826829c878SJohannes Berg 2836829c878SJohannes Berg if (wdev->conn->state != CFG80211_CONN_SCANNING && 2846829c878SJohannes Berg wdev->conn->state != CFG80211_CONN_SCAN_AGAIN) 2856829c878SJohannes Berg return; 2866829c878SJohannes Berg 287bbac31f4SJohannes Berg bss = cfg80211_get_conn_bss(wdev); 288bbac31f4SJohannes Berg if (bss) { 289bbac31f4SJohannes Berg cfg80211_put_bss(bss); 290bbac31f4SJohannes Berg } else { 2916829c878SJohannes Berg /* not found */ 2926829c878SJohannes Berg if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) 29379c97e97SJohannes Berg schedule_work(&rdev->conn_work); 2946829c878SJohannes Berg else 295667503ddSJohannes Berg __cfg80211_connect_result( 296667503ddSJohannes Berg wdev->netdev, 297667503ddSJohannes Berg wdev->conn->params.bssid, 2986829c878SJohannes Berg NULL, 0, NULL, 0, 2996829c878SJohannes Berg WLAN_STATUS_UNSPECIFIED_FAILURE, 300df7fc0f9SJohannes Berg false, NULL); 3016829c878SJohannes Berg } 3026829c878SJohannes Berg } 3036829c878SJohannes Berg 304667503ddSJohannes Berg void cfg80211_sme_scan_done(struct net_device *dev) 305667503ddSJohannes Berg { 306667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 307667503ddSJohannes Berg 30859bbb6f7SJohannes Berg mutex_lock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx); 309667503ddSJohannes Berg wdev_lock(wdev); 310667503ddSJohannes Berg __cfg80211_sme_scan_done(dev); 311667503ddSJohannes Berg wdev_unlock(wdev); 31259bbb6f7SJohannes Berg mutex_unlock(&wiphy_to_dev(wdev->wiphy)->devlist_mtx); 313667503ddSJohannes Berg } 314667503ddSJohannes Berg 315667503ddSJohannes Berg void cfg80211_sme_rx_auth(struct net_device *dev, 316667503ddSJohannes Berg const u8 *buf, size_t len) 3176829c878SJohannes Berg { 3186829c878SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 3196829c878SJohannes Berg struct wiphy *wiphy = wdev->wiphy; 3206829c878SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); 3216829c878SJohannes Berg struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; 3226829c878SJohannes Berg u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); 3236829c878SJohannes Berg 324667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 325667503ddSJohannes Berg 3266829c878SJohannes Berg /* should only RX auth frames when connecting */ 3276829c878SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTING) 3286829c878SJohannes Berg return; 3296829c878SJohannes Berg 3306829c878SJohannes Berg if (WARN_ON(!wdev->conn)) 3316829c878SJohannes Berg return; 3326829c878SJohannes Berg 3336829c878SJohannes Berg if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && 3346829c878SJohannes Berg wdev->conn->auto_auth && 3356829c878SJohannes Berg wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) { 3366829c878SJohannes Berg /* select automatically between only open, shared, leap */ 3376829c878SJohannes Berg switch (wdev->conn->params.auth_type) { 3386829c878SJohannes Berg case NL80211_AUTHTYPE_OPEN_SYSTEM: 339fffd0934SJohannes Berg if (wdev->connect_keys) 3406829c878SJohannes Berg wdev->conn->params.auth_type = 3416829c878SJohannes Berg NL80211_AUTHTYPE_SHARED_KEY; 342fffd0934SJohannes Berg else 343fffd0934SJohannes Berg wdev->conn->params.auth_type = 344fffd0934SJohannes Berg NL80211_AUTHTYPE_NETWORK_EAP; 3456829c878SJohannes Berg break; 3466829c878SJohannes Berg case NL80211_AUTHTYPE_SHARED_KEY: 3476829c878SJohannes Berg wdev->conn->params.auth_type = 3486829c878SJohannes Berg NL80211_AUTHTYPE_NETWORK_EAP; 3496829c878SJohannes Berg break; 3506829c878SJohannes Berg default: 3516829c878SJohannes Berg /* huh? */ 3526829c878SJohannes Berg wdev->conn->params.auth_type = 3536829c878SJohannes Berg NL80211_AUTHTYPE_OPEN_SYSTEM; 3546829c878SJohannes Berg break; 3556829c878SJohannes Berg } 3566829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; 3576829c878SJohannes Berg schedule_work(&rdev->conn_work); 35819957bb3SJohannes Berg } else if (status_code != WLAN_STATUS_SUCCESS) { 3594bde0f7dSJohannes Berg __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, 360df7fc0f9SJohannes Berg status_code, false, NULL); 36119957bb3SJohannes Berg } else if (wdev->sme_state == CFG80211_SME_CONNECTING && 3626829c878SJohannes Berg wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { 3636829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; 3646829c878SJohannes Berg schedule_work(&rdev->conn_work); 3656829c878SJohannes Berg } 3666829c878SJohannes Berg } 367b23aa676SSamuel Ortiz 368f401a6f7SJohannes Berg bool cfg80211_sme_failed_reassoc(struct wireless_dev *wdev) 369f401a6f7SJohannes Berg { 370f401a6f7SJohannes Berg struct wiphy *wiphy = wdev->wiphy; 371f401a6f7SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); 372f401a6f7SJohannes Berg 373f401a6f7SJohannes Berg if (WARN_ON(!wdev->conn)) 374f401a6f7SJohannes Berg return false; 375f401a6f7SJohannes Berg 376f401a6f7SJohannes Berg if (!wdev->conn->prev_bssid_valid) 377f401a6f7SJohannes Berg return false; 378f401a6f7SJohannes Berg 379f401a6f7SJohannes Berg /* 380f401a6f7SJohannes Berg * Some stupid APs don't accept reassoc, so we 381f401a6f7SJohannes Berg * need to fall back to trying regular assoc. 382f401a6f7SJohannes Berg */ 383f401a6f7SJohannes Berg wdev->conn->prev_bssid_valid = false; 384f401a6f7SJohannes Berg wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; 385f401a6f7SJohannes Berg schedule_work(&rdev->conn_work); 386f401a6f7SJohannes Berg 387f401a6f7SJohannes Berg return true; 388f401a6f7SJohannes Berg } 389f401a6f7SJohannes Berg 3907d930bc3SJohannes Berg void cfg80211_sme_failed_assoc(struct wireless_dev *wdev) 3917d930bc3SJohannes Berg { 3927d930bc3SJohannes Berg struct wiphy *wiphy = wdev->wiphy; 3937d930bc3SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); 3947d930bc3SJohannes Berg 3957d930bc3SJohannes Berg wdev->conn->state = CFG80211_CONN_DEAUTH_ASSOC_FAIL; 3967d930bc3SJohannes Berg schedule_work(&rdev->conn_work); 3977d930bc3SJohannes Berg } 3987d930bc3SJohannes Berg 399667503ddSJohannes Berg void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, 400b23aa676SSamuel Ortiz const u8 *req_ie, size_t req_ie_len, 401b23aa676SSamuel Ortiz const u8 *resp_ie, size_t resp_ie_len, 402df7fc0f9SJohannes Berg u16 status, bool wextev, 403df7fc0f9SJohannes Berg struct cfg80211_bss *bss) 404b23aa676SSamuel Ortiz { 405b23aa676SSamuel Ortiz struct wireless_dev *wdev = dev->ieee80211_ptr; 4068b19e6caSLuis R. Rodriguez u8 *country_ie; 4073d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT 408b23aa676SSamuel Ortiz union iwreq_data wrqu; 409b23aa676SSamuel Ortiz #endif 410b23aa676SSamuel Ortiz 411667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 412667503ddSJohannes Berg 413b23aa676SSamuel Ortiz if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 414b23aa676SSamuel Ortiz return; 415b23aa676SSamuel Ortiz 416f7969969SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTING) 417ea416a79SJohannes Berg return; 418ea416a79SJohannes Berg 419e45cd82aSJohannes Berg nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, 420e45cd82aSJohannes Berg bssid, req_ie, req_ie_len, 421e45cd82aSJohannes Berg resp_ie, resp_ie_len, 422667503ddSJohannes Berg status, GFP_KERNEL); 423e45cd82aSJohannes Berg 4243d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT 425e45cd82aSJohannes Berg if (wextev) { 426e45cd82aSJohannes Berg if (req_ie && status == WLAN_STATUS_SUCCESS) { 427e45cd82aSJohannes Berg memset(&wrqu, 0, sizeof(wrqu)); 428e45cd82aSJohannes Berg wrqu.data.length = req_ie_len; 4293409ff77SZhu Yi wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie); 430e45cd82aSJohannes Berg } 431e45cd82aSJohannes Berg 432e45cd82aSJohannes Berg if (resp_ie && status == WLAN_STATUS_SUCCESS) { 433e45cd82aSJohannes Berg memset(&wrqu, 0, sizeof(wrqu)); 434e45cd82aSJohannes Berg wrqu.data.length = resp_ie_len; 435e45cd82aSJohannes Berg wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); 436e45cd82aSJohannes Berg } 437e45cd82aSJohannes Berg 438e45cd82aSJohannes Berg memset(&wrqu, 0, sizeof(wrqu)); 439e45cd82aSJohannes Berg wrqu.ap_addr.sa_family = ARPHRD_ETHER; 440f401a6f7SJohannes Berg if (bssid && status == WLAN_STATUS_SUCCESS) { 441e45cd82aSJohannes Berg memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); 442f401a6f7SJohannes Berg memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); 443f401a6f7SJohannes Berg wdev->wext.prev_bssid_valid = true; 444f401a6f7SJohannes Berg } 445e45cd82aSJohannes Berg wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); 446e45cd82aSJohannes Berg } 447e45cd82aSJohannes Berg #endif 448e45cd82aSJohannes Berg 449df7fc0f9SJohannes Berg if (wdev->current_bss) { 450df7fc0f9SJohannes Berg cfg80211_unhold_bss(wdev->current_bss); 451df7fc0f9SJohannes Berg cfg80211_put_bss(&wdev->current_bss->pub); 452df7fc0f9SJohannes Berg wdev->current_bss = NULL; 453df7fc0f9SJohannes Berg } 454df7fc0f9SJohannes Berg 45519957bb3SJohannes Berg if (wdev->conn) 45619957bb3SJohannes Berg wdev->conn->state = CFG80211_CONN_IDLE; 45719957bb3SJohannes Berg 458fffd0934SJohannes Berg if (status != WLAN_STATUS_SUCCESS) { 459fffd0934SJohannes Berg wdev->sme_state = CFG80211_SME_IDLE; 460415ad1efSDavid Kilroy if (wdev->conn) 461415ad1efSDavid Kilroy kfree(wdev->conn->ie); 462fffd0934SJohannes Berg kfree(wdev->conn); 463fffd0934SJohannes Berg wdev->conn = NULL; 464fffd0934SJohannes Berg kfree(wdev->connect_keys); 465fffd0934SJohannes Berg wdev->connect_keys = NULL; 4668dadadb7SJohannes Berg wdev->ssid_len = 0; 467fffd0934SJohannes Berg return; 468fffd0934SJohannes Berg } 469fffd0934SJohannes Berg 470df7fc0f9SJohannes Berg if (!bss) 471b23aa676SSamuel Ortiz bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, 472b23aa676SSamuel Ortiz wdev->ssid, wdev->ssid_len, 473b23aa676SSamuel Ortiz WLAN_CAPABILITY_ESS, 474b23aa676SSamuel Ortiz WLAN_CAPABILITY_ESS); 475b23aa676SSamuel Ortiz 476b23aa676SSamuel Ortiz if (WARN_ON(!bss)) 477b23aa676SSamuel Ortiz return; 478b23aa676SSamuel Ortiz 47919957bb3SJohannes Berg cfg80211_hold_bss(bss_from_pub(bss)); 48019957bb3SJohannes Berg wdev->current_bss = bss_from_pub(bss); 481b23aa676SSamuel Ortiz 482b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_CONNECTED; 483fffd0934SJohannes Berg cfg80211_upload_connect_keys(wdev); 4848b19e6caSLuis R. Rodriguez 4858b19e6caSLuis R. Rodriguez country_ie = (u8 *) ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); 4868b19e6caSLuis R. Rodriguez 4878b19e6caSLuis R. Rodriguez if (!country_ie) 4888b19e6caSLuis R. Rodriguez return; 4898b19e6caSLuis R. Rodriguez 4908b19e6caSLuis R. Rodriguez /* 4918b19e6caSLuis R. Rodriguez * ieee80211_bss_get_ie() ensures we can access: 4928b19e6caSLuis R. Rodriguez * - country_ie + 2, the start of the country ie data, and 4938b19e6caSLuis R. Rodriguez * - and country_ie[1] which is the IE length 4948b19e6caSLuis R. Rodriguez */ 4958b19e6caSLuis R. Rodriguez regulatory_hint_11d(wdev->wiphy, 49684920e3eSLuis R. Rodriguez bss->channel->band, 4978b19e6caSLuis R. Rodriguez country_ie + 2, 4988b19e6caSLuis R. Rodriguez country_ie[1]); 499b23aa676SSamuel Ortiz } 500f2129354SJohannes Berg 501f2129354SJohannes Berg void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, 502f2129354SJohannes Berg const u8 *req_ie, size_t req_ie_len, 503f2129354SJohannes Berg const u8 *resp_ie, size_t resp_ie_len, 504f2129354SJohannes Berg u16 status, gfp_t gfp) 505f2129354SJohannes Berg { 506667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 507667503ddSJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 508667503ddSJohannes Berg struct cfg80211_event *ev; 509667503ddSJohannes Berg unsigned long flags; 510667503ddSJohannes Berg 511f7969969SJohannes Berg CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING); 512f7969969SJohannes Berg 513667503ddSJohannes Berg ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); 514667503ddSJohannes Berg if (!ev) 515667503ddSJohannes Berg return; 516667503ddSJohannes Berg 517667503ddSJohannes Berg ev->type = EVENT_CONNECT_RESULT; 51816a832e7SZhu Yi if (bssid) 519667503ddSJohannes Berg memcpy(ev->cr.bssid, bssid, ETH_ALEN); 520*7834704bSNishant Sarmukadam if (req_ie_len) { 521667503ddSJohannes Berg ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev); 522667503ddSJohannes Berg ev->cr.req_ie_len = req_ie_len; 523667503ddSJohannes Berg memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len); 524*7834704bSNishant Sarmukadam } 525*7834704bSNishant Sarmukadam if (resp_ie_len) { 526667503ddSJohannes Berg ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; 527667503ddSJohannes Berg ev->cr.resp_ie_len = resp_ie_len; 528667503ddSJohannes Berg memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len); 529*7834704bSNishant Sarmukadam } 530667503ddSJohannes Berg ev->cr.status = status; 531667503ddSJohannes Berg 532667503ddSJohannes Berg spin_lock_irqsave(&wdev->event_lock, flags); 533667503ddSJohannes Berg list_add_tail(&ev->list, &wdev->event_list); 534667503ddSJohannes Berg spin_unlock_irqrestore(&wdev->event_lock, flags); 535e60d7443SAlban Browaeys queue_work(cfg80211_wq, &rdev->event_work); 536f2129354SJohannes Berg } 537b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_connect_result); 538b23aa676SSamuel Ortiz 539667503ddSJohannes Berg void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, 540b23aa676SSamuel Ortiz const u8 *req_ie, size_t req_ie_len, 541667503ddSJohannes Berg const u8 *resp_ie, size_t resp_ie_len) 542b23aa676SSamuel Ortiz { 543b23aa676SSamuel Ortiz struct cfg80211_bss *bss; 5443d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT 545b23aa676SSamuel Ortiz union iwreq_data wrqu; 546b23aa676SSamuel Ortiz #endif 547b23aa676SSamuel Ortiz 548667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 549667503ddSJohannes Berg 550b23aa676SSamuel Ortiz if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 551b23aa676SSamuel Ortiz return; 552b23aa676SSamuel Ortiz 553f7969969SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTED) 554b23aa676SSamuel Ortiz return; 555b23aa676SSamuel Ortiz 556b23aa676SSamuel Ortiz /* internal error -- how did we get to CONNECTED w/o BSS? */ 557b23aa676SSamuel Ortiz if (WARN_ON(!wdev->current_bss)) { 558b23aa676SSamuel Ortiz return; 559b23aa676SSamuel Ortiz } 560b23aa676SSamuel Ortiz 561b23aa676SSamuel Ortiz cfg80211_unhold_bss(wdev->current_bss); 56219957bb3SJohannes Berg cfg80211_put_bss(&wdev->current_bss->pub); 563b23aa676SSamuel Ortiz wdev->current_bss = NULL; 564b23aa676SSamuel Ortiz 565b23aa676SSamuel Ortiz bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, 566b23aa676SSamuel Ortiz wdev->ssid, wdev->ssid_len, 567b23aa676SSamuel Ortiz WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); 568b23aa676SSamuel Ortiz 569b23aa676SSamuel Ortiz if (WARN_ON(!bss)) 570b23aa676SSamuel Ortiz return; 571b23aa676SSamuel Ortiz 57219957bb3SJohannes Berg cfg80211_hold_bss(bss_from_pub(bss)); 57319957bb3SJohannes Berg wdev->current_bss = bss_from_pub(bss); 574b23aa676SSamuel Ortiz 575667503ddSJohannes Berg nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bssid, 576667503ddSJohannes Berg req_ie, req_ie_len, resp_ie, resp_ie_len, 577667503ddSJohannes Berg GFP_KERNEL); 578b23aa676SSamuel Ortiz 5793d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT 580b23aa676SSamuel Ortiz if (req_ie) { 581b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 582b23aa676SSamuel Ortiz wrqu.data.length = req_ie_len; 5833409ff77SZhu Yi wireless_send_event(wdev->netdev, IWEVASSOCREQIE, 584667503ddSJohannes Berg &wrqu, req_ie); 585b23aa676SSamuel Ortiz } 586b23aa676SSamuel Ortiz 587b23aa676SSamuel Ortiz if (resp_ie) { 588b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 589b23aa676SSamuel Ortiz wrqu.data.length = resp_ie_len; 590667503ddSJohannes Berg wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, 591667503ddSJohannes Berg &wrqu, resp_ie); 592b23aa676SSamuel Ortiz } 593b23aa676SSamuel Ortiz 594b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 595b23aa676SSamuel Ortiz wrqu.ap_addr.sa_family = ARPHRD_ETHER; 596b23aa676SSamuel Ortiz memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); 597f401a6f7SJohannes Berg memcpy(wdev->wext.prev_bssid, bssid, ETH_ALEN); 598f401a6f7SJohannes Berg wdev->wext.prev_bssid_valid = true; 599667503ddSJohannes Berg wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); 600b23aa676SSamuel Ortiz #endif 601b23aa676SSamuel Ortiz } 602667503ddSJohannes Berg 603667503ddSJohannes Berg void cfg80211_roamed(struct net_device *dev, const u8 *bssid, 604667503ddSJohannes Berg const u8 *req_ie, size_t req_ie_len, 605667503ddSJohannes Berg const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) 606667503ddSJohannes Berg { 607667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 608667503ddSJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 609667503ddSJohannes Berg struct cfg80211_event *ev; 610667503ddSJohannes Berg unsigned long flags; 611667503ddSJohannes Berg 612f7969969SJohannes Berg CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); 613f7969969SJohannes Berg 614667503ddSJohannes Berg ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); 615667503ddSJohannes Berg if (!ev) 616667503ddSJohannes Berg return; 617667503ddSJohannes Berg 618667503ddSJohannes Berg ev->type = EVENT_ROAMED; 619667503ddSJohannes Berg memcpy(ev->rm.bssid, bssid, ETH_ALEN); 620667503ddSJohannes Berg ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev); 621667503ddSJohannes Berg ev->rm.req_ie_len = req_ie_len; 622667503ddSJohannes Berg memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len); 623667503ddSJohannes Berg ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; 624667503ddSJohannes Berg ev->rm.resp_ie_len = resp_ie_len; 625667503ddSJohannes Berg memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len); 626667503ddSJohannes Berg 627667503ddSJohannes Berg spin_lock_irqsave(&wdev->event_lock, flags); 628667503ddSJohannes Berg list_add_tail(&ev->list, &wdev->event_list); 629667503ddSJohannes Berg spin_unlock_irqrestore(&wdev->event_lock, flags); 630e60d7443SAlban Browaeys queue_work(cfg80211_wq, &rdev->event_work); 631667503ddSJohannes Berg } 632b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_roamed); 633b23aa676SSamuel Ortiz 634667503ddSJohannes Berg void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, 6356829c878SJohannes Berg size_t ie_len, u16 reason, bool from_ap) 636b23aa676SSamuel Ortiz { 637b23aa676SSamuel Ortiz struct wireless_dev *wdev = dev->ieee80211_ptr; 638fffd0934SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 639fffd0934SJohannes Berg int i; 6403d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT 641b23aa676SSamuel Ortiz union iwreq_data wrqu; 642b23aa676SSamuel Ortiz #endif 643b23aa676SSamuel Ortiz 644667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 645667503ddSJohannes Berg 646b23aa676SSamuel Ortiz if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 647b23aa676SSamuel Ortiz return; 648b23aa676SSamuel Ortiz 649f7969969SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTED) 650b23aa676SSamuel Ortiz return; 651b23aa676SSamuel Ortiz 652b23aa676SSamuel Ortiz if (wdev->current_bss) { 653b23aa676SSamuel Ortiz cfg80211_unhold_bss(wdev->current_bss); 65419957bb3SJohannes Berg cfg80211_put_bss(&wdev->current_bss->pub); 655b23aa676SSamuel Ortiz } 656b23aa676SSamuel Ortiz 657b23aa676SSamuel Ortiz wdev->current_bss = NULL; 658b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_IDLE; 6598dadadb7SJohannes Berg wdev->ssid_len = 0; 660b23aa676SSamuel Ortiz 6616829c878SJohannes Berg if (wdev->conn) { 662b6f0b639SJohannes Berg const u8 *bssid; 663b6f0b639SJohannes Berg int ret; 664b6f0b639SJohannes Berg 6656829c878SJohannes Berg kfree(wdev->conn->ie); 6666829c878SJohannes Berg wdev->conn->ie = NULL; 66719957bb3SJohannes Berg kfree(wdev->conn); 66819957bb3SJohannes Berg wdev->conn = NULL; 669b6f0b639SJohannes Berg 670b6f0b639SJohannes Berg /* 671b6f0b639SJohannes Berg * If this disconnect was due to a disassoc, we 672b6f0b639SJohannes Berg * we might still have an auth BSS around. For 673b6f0b639SJohannes Berg * the userspace SME that's currently expected, 674b6f0b639SJohannes Berg * but for the kernel SME (nl80211 CONNECT or 675b6f0b639SJohannes Berg * wireless extensions) we want to clear up all 676b6f0b639SJohannes Berg * state. 677b6f0b639SJohannes Berg */ 678b6f0b639SJohannes Berg for (i = 0; i < MAX_AUTH_BSSES; i++) { 679b6f0b639SJohannes Berg if (!wdev->auth_bsses[i]) 680b6f0b639SJohannes Berg continue; 681b6f0b639SJohannes Berg bssid = wdev->auth_bsses[i]->pub.bssid; 682b6f0b639SJohannes Berg ret = __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0, 683d5cdfacbSJouni Malinen WLAN_REASON_DEAUTH_LEAVING, 684d5cdfacbSJouni Malinen false); 685b6f0b639SJohannes Berg WARN(ret, "deauth failed: %d\n", ret); 686b6f0b639SJohannes Berg } 6876829c878SJohannes Berg } 6886829c878SJohannes Berg 689fffd0934SJohannes Berg nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); 690fffd0934SJohannes Berg 691fffd0934SJohannes Berg /* 692fffd0934SJohannes Berg * Delete all the keys ... pairwise keys can't really 693fffd0934SJohannes Berg * exist any more anyway, but default keys might. 694fffd0934SJohannes Berg */ 695fffd0934SJohannes Berg if (rdev->ops->del_key) 696fffd0934SJohannes Berg for (i = 0; i < 6; i++) 697fffd0934SJohannes Berg rdev->ops->del_key(wdev->wiphy, dev, i, NULL); 698b23aa676SSamuel Ortiz 6993d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT 700b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 701b23aa676SSamuel Ortiz wrqu.ap_addr.sa_family = ARPHRD_ETHER; 702b23aa676SSamuel Ortiz wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); 7035f612033SAbhijeet Kolekar wdev->wext.connect.ssid_len = 0; 704b23aa676SSamuel Ortiz #endif 70509d989d1SLuis R. Rodriguez 70609d989d1SLuis R. Rodriguez schedule_work(&cfg80211_disconnect_work); 707b23aa676SSamuel Ortiz } 708b23aa676SSamuel Ortiz 709b23aa676SSamuel Ortiz void cfg80211_disconnected(struct net_device *dev, u16 reason, 710b23aa676SSamuel Ortiz u8 *ie, size_t ie_len, gfp_t gfp) 711b23aa676SSamuel Ortiz { 712667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 713667503ddSJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 714667503ddSJohannes Berg struct cfg80211_event *ev; 715667503ddSJohannes Berg unsigned long flags; 716667503ddSJohannes Berg 717f7969969SJohannes Berg CFG80211_DEV_WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED); 718f7969969SJohannes Berg 719667503ddSJohannes Berg ev = kzalloc(sizeof(*ev) + ie_len, gfp); 720667503ddSJohannes Berg if (!ev) 721667503ddSJohannes Berg return; 722667503ddSJohannes Berg 723667503ddSJohannes Berg ev->type = EVENT_DISCONNECTED; 724667503ddSJohannes Berg ev->dc.ie = ((u8 *)ev) + sizeof(*ev); 725667503ddSJohannes Berg ev->dc.ie_len = ie_len; 726667503ddSJohannes Berg memcpy((void *)ev->dc.ie, ie, ie_len); 727667503ddSJohannes Berg ev->dc.reason = reason; 728667503ddSJohannes Berg 729667503ddSJohannes Berg spin_lock_irqsave(&wdev->event_lock, flags); 730667503ddSJohannes Berg list_add_tail(&ev->list, &wdev->event_list); 731667503ddSJohannes Berg spin_unlock_irqrestore(&wdev->event_lock, flags); 732e60d7443SAlban Browaeys queue_work(cfg80211_wq, &rdev->event_work); 733b23aa676SSamuel Ortiz } 734b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_disconnected); 735b23aa676SSamuel Ortiz 736667503ddSJohannes Berg int __cfg80211_connect(struct cfg80211_registered_device *rdev, 737b23aa676SSamuel Ortiz struct net_device *dev, 738fffd0934SJohannes Berg struct cfg80211_connect_params *connect, 739f401a6f7SJohannes Berg struct cfg80211_cached_keys *connkeys, 740f401a6f7SJohannes Berg const u8 *prev_bssid) 741b23aa676SSamuel Ortiz { 742b23aa676SSamuel Ortiz struct wireless_dev *wdev = dev->ieee80211_ptr; 74359bbb6f7SJohannes Berg struct ieee80211_channel *chan; 744bbac31f4SJohannes Berg struct cfg80211_bss *bss = NULL; 745667503ddSJohannes Berg int err; 746667503ddSJohannes Berg 747667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 748b23aa676SSamuel Ortiz 749b23aa676SSamuel Ortiz if (wdev->sme_state != CFG80211_SME_IDLE) 750b23aa676SSamuel Ortiz return -EALREADY; 751b23aa676SSamuel Ortiz 75259bbb6f7SJohannes Berg chan = rdev_fixed_channel(rdev, wdev); 75359bbb6f7SJohannes Berg if (chan && chan != connect->channel) 75459bbb6f7SJohannes Berg return -EBUSY; 75559bbb6f7SJohannes Berg 756fffd0934SJohannes Berg if (WARN_ON(wdev->connect_keys)) { 757fffd0934SJohannes Berg kfree(wdev->connect_keys); 758fffd0934SJohannes Berg wdev->connect_keys = NULL; 759fffd0934SJohannes Berg } 760fffd0934SJohannes Berg 761fffd0934SJohannes Berg if (connkeys && connkeys->def >= 0) { 762fffd0934SJohannes Berg int idx; 763bcba8eaeSSamuel Ortiz u32 cipher; 764fffd0934SJohannes Berg 765fffd0934SJohannes Berg idx = connkeys->def; 766bcba8eaeSSamuel Ortiz cipher = connkeys->params[idx].cipher; 767fffd0934SJohannes Berg /* If given a WEP key we may need it for shared key auth */ 768bcba8eaeSSamuel Ortiz if (cipher == WLAN_CIPHER_SUITE_WEP40 || 769bcba8eaeSSamuel Ortiz cipher == WLAN_CIPHER_SUITE_WEP104) { 770fffd0934SJohannes Berg connect->key_idx = idx; 771fffd0934SJohannes Berg connect->key = connkeys->params[idx].key; 772fffd0934SJohannes Berg connect->key_len = connkeys->params[idx].key_len; 773bcba8eaeSSamuel Ortiz 774bcba8eaeSSamuel Ortiz /* 775bcba8eaeSSamuel Ortiz * If ciphers are not set (e.g. when going through 776bcba8eaeSSamuel Ortiz * iwconfig), we have to set them appropriately here. 777bcba8eaeSSamuel Ortiz */ 778bcba8eaeSSamuel Ortiz if (connect->crypto.cipher_group == 0) 779bcba8eaeSSamuel Ortiz connect->crypto.cipher_group = cipher; 780bcba8eaeSSamuel Ortiz 781bcba8eaeSSamuel Ortiz if (connect->crypto.n_ciphers_pairwise == 0) { 782bcba8eaeSSamuel Ortiz connect->crypto.n_ciphers_pairwise = 1; 783bcba8eaeSSamuel Ortiz connect->crypto.ciphers_pairwise[0] = cipher; 784bcba8eaeSSamuel Ortiz } 785fffd0934SJohannes Berg } 786fffd0934SJohannes Berg } 787fffd0934SJohannes Berg 788b23aa676SSamuel Ortiz if (!rdev->ops->connect) { 7896829c878SJohannes Berg if (!rdev->ops->auth || !rdev->ops->assoc) 790b23aa676SSamuel Ortiz return -EOPNOTSUPP; 7916829c878SJohannes Berg 79219957bb3SJohannes Berg if (WARN_ON(wdev->conn)) 79319957bb3SJohannes Berg return -EINPROGRESS; 79419957bb3SJohannes Berg 7956829c878SJohannes Berg wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); 7966829c878SJohannes Berg if (!wdev->conn) 7976829c878SJohannes Berg return -ENOMEM; 7986829c878SJohannes Berg 7996829c878SJohannes Berg /* 8006829c878SJohannes Berg * Copy all parameters, and treat explicitly IEs, BSSID, SSID. 8016829c878SJohannes Berg */ 8026829c878SJohannes Berg memcpy(&wdev->conn->params, connect, sizeof(*connect)); 8036829c878SJohannes Berg if (connect->bssid) { 8046829c878SJohannes Berg wdev->conn->params.bssid = wdev->conn->bssid; 8056829c878SJohannes Berg memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); 8066829c878SJohannes Berg } 8076829c878SJohannes Berg 8086829c878SJohannes Berg if (connect->ie) { 8096829c878SJohannes Berg wdev->conn->ie = kmemdup(connect->ie, connect->ie_len, 8106829c878SJohannes Berg GFP_KERNEL); 8116829c878SJohannes Berg wdev->conn->params.ie = wdev->conn->ie; 81219957bb3SJohannes Berg if (!wdev->conn->ie) { 81319957bb3SJohannes Berg kfree(wdev->conn); 81419957bb3SJohannes Berg wdev->conn = NULL; 8156829c878SJohannes Berg return -ENOMEM; 8166829c878SJohannes Berg } 81719957bb3SJohannes Berg } 8186829c878SJohannes Berg 8196829c878SJohannes Berg if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { 8206829c878SJohannes Berg wdev->conn->auto_auth = true; 8216829c878SJohannes Berg /* start with open system ... should mostly work */ 8226829c878SJohannes Berg wdev->conn->params.auth_type = 8236829c878SJohannes Berg NL80211_AUTHTYPE_OPEN_SYSTEM; 8246829c878SJohannes Berg } else { 8256829c878SJohannes Berg wdev->conn->auto_auth = false; 8266829c878SJohannes Berg } 8276829c878SJohannes Berg 8286829c878SJohannes Berg memcpy(wdev->ssid, connect->ssid, connect->ssid_len); 8296829c878SJohannes Berg wdev->ssid_len = connect->ssid_len; 8306829c878SJohannes Berg wdev->conn->params.ssid = wdev->ssid; 8316829c878SJohannes Berg wdev->conn->params.ssid_len = connect->ssid_len; 8326829c878SJohannes Berg 8338bb89485SJohannes Berg /* see if we have the bss already */ 834bbac31f4SJohannes Berg bss = cfg80211_get_conn_bss(wdev); 8356829c878SJohannes Berg 8366829c878SJohannes Berg wdev->sme_state = CFG80211_SME_CONNECTING; 837fffd0934SJohannes Berg wdev->connect_keys = connkeys; 8386829c878SJohannes Berg 839f401a6f7SJohannes Berg if (prev_bssid) { 840f401a6f7SJohannes Berg memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN); 841f401a6f7SJohannes Berg wdev->conn->prev_bssid_valid = true; 842f401a6f7SJohannes Berg } 843f401a6f7SJohannes Berg 844bbac31f4SJohannes Berg /* we're good if we have a matching bss struct */ 845bbac31f4SJohannes Berg if (bss) { 8466829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; 8476829c878SJohannes Berg err = cfg80211_conn_do_work(wdev); 848bbac31f4SJohannes Berg cfg80211_put_bss(bss); 8496829c878SJohannes Berg } else { 8506829c878SJohannes Berg /* otherwise we'll need to scan for the AP first */ 8516829c878SJohannes Berg err = cfg80211_conn_scan(wdev); 8526829c878SJohannes Berg /* 8536829c878SJohannes Berg * If we can't scan right now, then we need to scan again 8546829c878SJohannes Berg * after the current scan finished, since the parameters 8556829c878SJohannes Berg * changed (unless we find a good AP anyway). 8566829c878SJohannes Berg */ 8576829c878SJohannes Berg if (err == -EBUSY) { 8586829c878SJohannes Berg err = 0; 8596829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; 8606829c878SJohannes Berg } 8616829c878SJohannes Berg } 86219957bb3SJohannes Berg if (err) { 863415ad1efSDavid Kilroy kfree(wdev->conn->ie); 86419957bb3SJohannes Berg kfree(wdev->conn); 86519957bb3SJohannes Berg wdev->conn = NULL; 8666829c878SJohannes Berg wdev->sme_state = CFG80211_SME_IDLE; 867fffd0934SJohannes Berg wdev->connect_keys = NULL; 8688dadadb7SJohannes Berg wdev->ssid_len = 0; 86919957bb3SJohannes Berg } 8706829c878SJohannes Berg 8716829c878SJohannes Berg return err; 872b23aa676SSamuel Ortiz } else { 873b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_CONNECTING; 874fffd0934SJohannes Berg wdev->connect_keys = connkeys; 875b23aa676SSamuel Ortiz err = rdev->ops->connect(&rdev->wiphy, dev, connect); 876b23aa676SSamuel Ortiz if (err) { 877fffd0934SJohannes Berg wdev->connect_keys = NULL; 878b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_IDLE; 879b23aa676SSamuel Ortiz return err; 880b23aa676SSamuel Ortiz } 881b23aa676SSamuel Ortiz 882b23aa676SSamuel Ortiz memcpy(wdev->ssid, connect->ssid, connect->ssid_len); 883b23aa676SSamuel Ortiz wdev->ssid_len = connect->ssid_len; 884b23aa676SSamuel Ortiz 885b23aa676SSamuel Ortiz return 0; 886b23aa676SSamuel Ortiz } 8876829c878SJohannes Berg } 888b23aa676SSamuel Ortiz 889667503ddSJohannes Berg int cfg80211_connect(struct cfg80211_registered_device *rdev, 890667503ddSJohannes Berg struct net_device *dev, 891fffd0934SJohannes Berg struct cfg80211_connect_params *connect, 892fffd0934SJohannes Berg struct cfg80211_cached_keys *connkeys) 893667503ddSJohannes Berg { 894667503ddSJohannes Berg int err; 895667503ddSJohannes Berg 89659bbb6f7SJohannes Berg mutex_lock(&rdev->devlist_mtx); 897667503ddSJohannes Berg wdev_lock(dev->ieee80211_ptr); 898f401a6f7SJohannes Berg err = __cfg80211_connect(rdev, dev, connect, connkeys, NULL); 899667503ddSJohannes Berg wdev_unlock(dev->ieee80211_ptr); 90059bbb6f7SJohannes Berg mutex_unlock(&rdev->devlist_mtx); 901667503ddSJohannes Berg 902667503ddSJohannes Berg return err; 903667503ddSJohannes Berg } 904667503ddSJohannes Berg 905667503ddSJohannes Berg int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, 906f2129354SJohannes Berg struct net_device *dev, u16 reason, bool wextev) 907b23aa676SSamuel Ortiz { 9086829c878SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 909b23aa676SSamuel Ortiz int err; 910b23aa676SSamuel Ortiz 911667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 912667503ddSJohannes Berg 9136829c878SJohannes Berg if (wdev->sme_state == CFG80211_SME_IDLE) 9146829c878SJohannes Berg return -EINVAL; 9156829c878SJohannes Berg 916fffd0934SJohannes Berg kfree(wdev->connect_keys); 917fffd0934SJohannes Berg wdev->connect_keys = NULL; 918fffd0934SJohannes Berg 919b23aa676SSamuel Ortiz if (!rdev->ops->disconnect) { 92019957bb3SJohannes Berg if (!rdev->ops->deauth) 92119957bb3SJohannes Berg return -EOPNOTSUPP; 9226829c878SJohannes Berg 92319957bb3SJohannes Berg /* was it connected by userspace SME? */ 92419957bb3SJohannes Berg if (!wdev->conn) { 92519957bb3SJohannes Berg cfg80211_mlme_down(rdev, dev); 92619957bb3SJohannes Berg return 0; 92719957bb3SJohannes Berg } 9286829c878SJohannes Berg 9296829c878SJohannes Berg if (wdev->sme_state == CFG80211_SME_CONNECTING && 9306829c878SJohannes Berg (wdev->conn->state == CFG80211_CONN_SCANNING || 9316829c878SJohannes Berg wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) { 9326829c878SJohannes Berg wdev->sme_state = CFG80211_SME_IDLE; 933415ad1efSDavid Kilroy kfree(wdev->conn->ie); 93419957bb3SJohannes Berg kfree(wdev->conn); 93519957bb3SJohannes Berg wdev->conn = NULL; 9368dadadb7SJohannes Berg wdev->ssid_len = 0; 9376829c878SJohannes Berg return 0; 9386829c878SJohannes Berg } 9396829c878SJohannes Berg 9406829c878SJohannes Berg /* wdev->conn->params.bssid must be set if > SCANNING */ 941667503ddSJohannes Berg err = __cfg80211_mlme_deauth(rdev, dev, 942667503ddSJohannes Berg wdev->conn->params.bssid, 943d5cdfacbSJouni Malinen NULL, 0, reason, false); 9446829c878SJohannes Berg if (err) 9456829c878SJohannes Berg return err; 946b23aa676SSamuel Ortiz } else { 947b23aa676SSamuel Ortiz err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); 948b23aa676SSamuel Ortiz if (err) 949b23aa676SSamuel Ortiz return err; 950b23aa676SSamuel Ortiz } 951b23aa676SSamuel Ortiz 9526829c878SJohannes Berg if (wdev->sme_state == CFG80211_SME_CONNECTED) 953667503ddSJohannes Berg __cfg80211_disconnected(dev, NULL, 0, 0, false); 9546829c878SJohannes Berg else if (wdev->sme_state == CFG80211_SME_CONNECTING) 955f2129354SJohannes Berg __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, 9566829c878SJohannes Berg WLAN_STATUS_UNSPECIFIED_FAILURE, 957df7fc0f9SJohannes Berg wextev, NULL); 958b23aa676SSamuel Ortiz 959b23aa676SSamuel Ortiz return 0; 960b23aa676SSamuel Ortiz } 96119957bb3SJohannes Berg 962667503ddSJohannes Berg int cfg80211_disconnect(struct cfg80211_registered_device *rdev, 963667503ddSJohannes Berg struct net_device *dev, 964667503ddSJohannes Berg u16 reason, bool wextev) 965667503ddSJohannes Berg { 966667503ddSJohannes Berg int err; 967667503ddSJohannes Berg 968667503ddSJohannes Berg wdev_lock(dev->ieee80211_ptr); 969667503ddSJohannes Berg err = __cfg80211_disconnect(rdev, dev, reason, wextev); 970667503ddSJohannes Berg wdev_unlock(dev->ieee80211_ptr); 971667503ddSJohannes Berg 972667503ddSJohannes Berg return err; 973667503ddSJohannes Berg } 974667503ddSJohannes Berg 97519957bb3SJohannes Berg void cfg80211_sme_disassoc(struct net_device *dev, int idx) 97619957bb3SJohannes Berg { 97719957bb3SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 97819957bb3SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 97919957bb3SJohannes Berg u8 bssid[ETH_ALEN]; 98019957bb3SJohannes Berg 981667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 982667503ddSJohannes Berg 98319957bb3SJohannes Berg if (!wdev->conn) 98419957bb3SJohannes Berg return; 98519957bb3SJohannes Berg 98619957bb3SJohannes Berg if (wdev->conn->state == CFG80211_CONN_IDLE) 98719957bb3SJohannes Berg return; 98819957bb3SJohannes Berg 98919957bb3SJohannes Berg /* 99019957bb3SJohannes Berg * Ok, so the association was made by this SME -- we don't 99119957bb3SJohannes Berg * want it any more so deauthenticate too. 99219957bb3SJohannes Berg */ 99319957bb3SJohannes Berg 99419957bb3SJohannes Berg if (!wdev->auth_bsses[idx]) 99519957bb3SJohannes Berg return; 99619957bb3SJohannes Berg 99719957bb3SJohannes Berg memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN); 998ec3f1490SJohannes Berg if (__cfg80211_mlme_deauth(rdev, dev, bssid, 999d5cdfacbSJouni Malinen NULL, 0, WLAN_REASON_DEAUTH_LEAVING, 1000d5cdfacbSJouni Malinen false)) { 100119957bb3SJohannes Berg /* whatever -- assume gone anyway */ 100219957bb3SJohannes Berg cfg80211_unhold_bss(wdev->auth_bsses[idx]); 100319957bb3SJohannes Berg cfg80211_put_bss(&wdev->auth_bsses[idx]->pub); 100419957bb3SJohannes Berg wdev->auth_bsses[idx] = NULL; 100519957bb3SJohannes Berg } 100619957bb3SJohannes Berg } 1007