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, 296829c878SJohannes Berg } state; 306829c878SJohannes Berg u8 bssid[ETH_ALEN]; 316829c878SJohannes Berg u8 *ie; 326829c878SJohannes Berg size_t ie_len; 336829c878SJohannes Berg bool auto_auth; 346829c878SJohannes Berg }; 356829c878SJohannes Berg 366829c878SJohannes Berg 376829c878SJohannes Berg static int cfg80211_conn_scan(struct wireless_dev *wdev) 386829c878SJohannes Berg { 3979c97e97SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 406829c878SJohannes Berg struct cfg80211_scan_request *request; 416829c878SJohannes Berg int n_channels, err; 426829c878SJohannes Berg 436829c878SJohannes Berg ASSERT_RTNL(); 4479c97e97SJohannes Berg ASSERT_RDEV_LOCK(rdev); 45667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 466829c878SJohannes Berg 4779c97e97SJohannes Berg if (rdev->scan_req) 486829c878SJohannes Berg return -EBUSY; 496829c878SJohannes Berg 506829c878SJohannes Berg if (wdev->conn->params.channel) { 516829c878SJohannes Berg n_channels = 1; 526829c878SJohannes Berg } else { 536829c878SJohannes Berg enum ieee80211_band band; 546829c878SJohannes Berg n_channels = 0; 556829c878SJohannes Berg 566829c878SJohannes Berg for (band = 0; band < IEEE80211_NUM_BANDS; band++) { 576829c878SJohannes Berg if (!wdev->wiphy->bands[band]) 586829c878SJohannes Berg continue; 596829c878SJohannes Berg n_channels += wdev->wiphy->bands[band]->n_channels; 606829c878SJohannes Berg } 616829c878SJohannes Berg } 626829c878SJohannes Berg request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) + 636829c878SJohannes Berg sizeof(request->channels[0]) * n_channels, 646829c878SJohannes Berg GFP_KERNEL); 656829c878SJohannes Berg if (!request) 666829c878SJohannes Berg return -ENOMEM; 676829c878SJohannes Berg 686829c878SJohannes Berg request->channels = (void *)((char *)request + sizeof(*request)); 696829c878SJohannes Berg if (wdev->conn->params.channel) 706829c878SJohannes Berg request->channels[0] = wdev->conn->params.channel; 716829c878SJohannes Berg else { 726829c878SJohannes Berg int i = 0, j; 736829c878SJohannes Berg enum ieee80211_band band; 746829c878SJohannes Berg 756829c878SJohannes Berg for (band = 0; band < IEEE80211_NUM_BANDS; band++) { 766829c878SJohannes Berg if (!wdev->wiphy->bands[band]) 776829c878SJohannes Berg continue; 786829c878SJohannes Berg for (j = 0; j < wdev->wiphy->bands[band]->n_channels; 796829c878SJohannes Berg i++, j++) 806829c878SJohannes Berg request->channels[i] = 816829c878SJohannes Berg &wdev->wiphy->bands[band]->channels[j]; 826829c878SJohannes Berg } 836829c878SJohannes Berg } 846829c878SJohannes Berg request->n_channels = n_channels; 856829c878SJohannes Berg request->ssids = (void *)(request->channels + n_channels); 866829c878SJohannes Berg request->n_ssids = 1; 876829c878SJohannes Berg 886829c878SJohannes Berg memcpy(request->ssids[0].ssid, wdev->conn->params.ssid, 896829c878SJohannes Berg wdev->conn->params.ssid_len); 906829c878SJohannes Berg request->ssids[0].ssid_len = wdev->conn->params.ssid_len; 916829c878SJohannes Berg 92463d0183SJohannes Berg request->dev = wdev->netdev; 9379c97e97SJohannes Berg request->wiphy = &rdev->wiphy; 946829c878SJohannes Berg 9579c97e97SJohannes Berg rdev->scan_req = request; 966829c878SJohannes Berg 9779c97e97SJohannes Berg err = rdev->ops->scan(wdev->wiphy, wdev->netdev, request); 986829c878SJohannes Berg if (!err) { 996829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_SCANNING; 10079c97e97SJohannes Berg nl80211_send_scan_start(rdev, wdev->netdev); 101463d0183SJohannes Berg dev_hold(wdev->netdev); 1026829c878SJohannes Berg } else { 10379c97e97SJohannes Berg rdev->scan_req = NULL; 1046829c878SJohannes Berg kfree(request); 1056829c878SJohannes Berg } 1066829c878SJohannes Berg return err; 1076829c878SJohannes Berg } 1086829c878SJohannes Berg 1096829c878SJohannes Berg static int cfg80211_conn_do_work(struct wireless_dev *wdev) 1106829c878SJohannes Berg { 11179c97e97SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 11219957bb3SJohannes Berg struct cfg80211_connect_params *params; 11319957bb3SJohannes Berg int err; 1146829c878SJohannes Berg 115667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 116667503ddSJohannes Berg 1176829c878SJohannes Berg if (!wdev->conn) 1186829c878SJohannes Berg return 0; 1196829c878SJohannes Berg 12019957bb3SJohannes Berg params = &wdev->conn->params; 12119957bb3SJohannes Berg 1226829c878SJohannes Berg switch (wdev->conn->state) { 1236829c878SJohannes Berg case CFG80211_CONN_SCAN_AGAIN: 1246829c878SJohannes Berg return cfg80211_conn_scan(wdev); 1256829c878SJohannes Berg case CFG80211_CONN_AUTHENTICATE_NEXT: 12679c97e97SJohannes Berg BUG_ON(!rdev->ops->auth); 12719957bb3SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATING; 12879c97e97SJohannes Berg return __cfg80211_mlme_auth(rdev, wdev->netdev, 12919957bb3SJohannes Berg params->channel, params->auth_type, 13019957bb3SJohannes Berg params->bssid, 13119957bb3SJohannes Berg params->ssid, params->ssid_len, 132fffd0934SJohannes Berg NULL, 0, 133fffd0934SJohannes Berg params->key, params->key_len, 134fffd0934SJohannes Berg params->key_idx); 1356829c878SJohannes Berg case CFG80211_CONN_ASSOCIATE_NEXT: 13679c97e97SJohannes Berg BUG_ON(!rdev->ops->assoc); 13719957bb3SJohannes Berg wdev->conn->state = CFG80211_CONN_ASSOCIATING; 1383e5d7649SJohannes Berg /* 1393e5d7649SJohannes Berg * We could, later, implement roaming here and then actually 1403e5d7649SJohannes Berg * set prev_bssid to non-NULL. But then we need to be aware 1413e5d7649SJohannes Berg * that some APs don't like that -- so we'd need to retry 1423e5d7649SJohannes Berg * the association. 1433e5d7649SJohannes Berg */ 14479c97e97SJohannes Berg err = __cfg80211_mlme_assoc(rdev, wdev->netdev, 145667503ddSJohannes Berg params->channel, params->bssid, 146667503ddSJohannes Berg NULL, 14719957bb3SJohannes Berg params->ssid, params->ssid_len, 14819957bb3SJohannes Berg params->ie, params->ie_len, 14919957bb3SJohannes Berg false, ¶ms->crypto); 15019957bb3SJohannes Berg if (err) 15179c97e97SJohannes Berg __cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, 152667503ddSJohannes Berg NULL, 0, 153667503ddSJohannes Berg WLAN_REASON_DEAUTH_LEAVING); 15419957bb3SJohannes Berg return err; 1556829c878SJohannes Berg default: 1566829c878SJohannes Berg return 0; 1576829c878SJohannes Berg } 1586829c878SJohannes Berg } 1596829c878SJohannes Berg 1606829c878SJohannes Berg void cfg80211_conn_work(struct work_struct *work) 1616829c878SJohannes Berg { 16279c97e97SJohannes Berg struct cfg80211_registered_device *rdev = 1636829c878SJohannes Berg container_of(work, struct cfg80211_registered_device, conn_work); 1646829c878SJohannes Berg struct wireless_dev *wdev; 1656829c878SJohannes Berg 1666829c878SJohannes Berg rtnl_lock(); 16779c97e97SJohannes Berg cfg80211_lock_rdev(rdev); 16879c97e97SJohannes Berg mutex_lock(&rdev->devlist_mtx); 1696829c878SJohannes Berg 17079c97e97SJohannes Berg list_for_each_entry(wdev, &rdev->netdev_list, list) { 171667503ddSJohannes Berg wdev_lock(wdev); 172667503ddSJohannes Berg if (!netif_running(wdev->netdev)) { 173667503ddSJohannes Berg wdev_unlock(wdev); 1746829c878SJohannes Berg continue; 175667503ddSJohannes Berg } 176667503ddSJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTING) { 177667503ddSJohannes Berg wdev_unlock(wdev); 1786829c878SJohannes Berg continue; 179667503ddSJohannes Berg } 1806829c878SJohannes Berg if (cfg80211_conn_do_work(wdev)) 181667503ddSJohannes Berg __cfg80211_connect_result( 182667503ddSJohannes Berg wdev->netdev, 1836829c878SJohannes Berg wdev->conn->params.bssid, 1846829c878SJohannes Berg NULL, 0, NULL, 0, 1856829c878SJohannes Berg WLAN_STATUS_UNSPECIFIED_FAILURE, 186df7fc0f9SJohannes Berg false, NULL); 187667503ddSJohannes Berg wdev_unlock(wdev); 1886829c878SJohannes Berg } 1896829c878SJohannes Berg 19079c97e97SJohannes Berg mutex_unlock(&rdev->devlist_mtx); 19179c97e97SJohannes Berg cfg80211_unlock_rdev(rdev); 1926829c878SJohannes Berg rtnl_unlock(); 1936829c878SJohannes Berg } 1946829c878SJohannes Berg 1956829c878SJohannes Berg static bool cfg80211_get_conn_bss(struct wireless_dev *wdev) 1966829c878SJohannes Berg { 19779c97e97SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 1986829c878SJohannes Berg struct cfg80211_bss *bss; 1996829c878SJohannes Berg u16 capa = WLAN_CAPABILITY_ESS; 2006829c878SJohannes Berg 201667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 202667503ddSJohannes Berg 2036829c878SJohannes Berg if (wdev->conn->params.privacy) 2046829c878SJohannes Berg capa |= WLAN_CAPABILITY_PRIVACY; 2056829c878SJohannes Berg 2066829c878SJohannes Berg bss = cfg80211_get_bss(wdev->wiphy, NULL, wdev->conn->params.bssid, 2076829c878SJohannes Berg wdev->conn->params.ssid, 2086829c878SJohannes Berg wdev->conn->params.ssid_len, 2096829c878SJohannes Berg WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_PRIVACY, 2106829c878SJohannes Berg capa); 2116829c878SJohannes Berg if (!bss) 2126829c878SJohannes Berg return false; 2136829c878SJohannes Berg 2146829c878SJohannes Berg memcpy(wdev->conn->bssid, bss->bssid, ETH_ALEN); 2156829c878SJohannes Berg wdev->conn->params.bssid = wdev->conn->bssid; 2166829c878SJohannes Berg wdev->conn->params.channel = bss->channel; 2176829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; 21879c97e97SJohannes Berg schedule_work(&rdev->conn_work); 2196829c878SJohannes Berg 2206829c878SJohannes Berg cfg80211_put_bss(bss); 2216829c878SJohannes Berg return true; 2226829c878SJohannes Berg } 2236829c878SJohannes Berg 224667503ddSJohannes Berg static void __cfg80211_sme_scan_done(struct net_device *dev) 2256829c878SJohannes Berg { 2266829c878SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 22779c97e97SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 2286829c878SJohannes Berg 229667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 230667503ddSJohannes Berg 2316829c878SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTING) 2326829c878SJohannes Berg return; 2336829c878SJohannes Berg 234d4b1a687SZhu Yi if (!wdev->conn) 2356829c878SJohannes Berg return; 2366829c878SJohannes Berg 2376829c878SJohannes Berg if (wdev->conn->state != CFG80211_CONN_SCANNING && 2386829c878SJohannes Berg wdev->conn->state != CFG80211_CONN_SCAN_AGAIN) 2396829c878SJohannes Berg return; 2406829c878SJohannes Berg 2416829c878SJohannes Berg if (!cfg80211_get_conn_bss(wdev)) { 2426829c878SJohannes Berg /* not found */ 2436829c878SJohannes Berg if (wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) 24479c97e97SJohannes Berg schedule_work(&rdev->conn_work); 2456829c878SJohannes Berg else 246667503ddSJohannes Berg __cfg80211_connect_result( 247667503ddSJohannes Berg wdev->netdev, 248667503ddSJohannes Berg wdev->conn->params.bssid, 2496829c878SJohannes Berg NULL, 0, NULL, 0, 2506829c878SJohannes Berg WLAN_STATUS_UNSPECIFIED_FAILURE, 251df7fc0f9SJohannes Berg false, NULL); 2526829c878SJohannes Berg } 2536829c878SJohannes Berg } 2546829c878SJohannes Berg 255667503ddSJohannes Berg void cfg80211_sme_scan_done(struct net_device *dev) 256667503ddSJohannes Berg { 257667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 258667503ddSJohannes Berg 259667503ddSJohannes Berg wdev_lock(wdev); 260667503ddSJohannes Berg __cfg80211_sme_scan_done(dev); 261667503ddSJohannes Berg wdev_unlock(wdev); 262667503ddSJohannes Berg } 263667503ddSJohannes Berg 264667503ddSJohannes Berg void cfg80211_sme_rx_auth(struct net_device *dev, 265667503ddSJohannes Berg const u8 *buf, size_t len) 2666829c878SJohannes Berg { 2676829c878SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 2686829c878SJohannes Berg struct wiphy *wiphy = wdev->wiphy; 2696829c878SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); 2706829c878SJohannes Berg struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; 2716829c878SJohannes Berg u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); 2726829c878SJohannes Berg 273667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 274667503ddSJohannes Berg 2756829c878SJohannes Berg /* should only RX auth frames when connecting */ 2766829c878SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTING) 2776829c878SJohannes Berg return; 2786829c878SJohannes Berg 2796829c878SJohannes Berg if (WARN_ON(!wdev->conn)) 2806829c878SJohannes Berg return; 2816829c878SJohannes Berg 2826829c878SJohannes Berg if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && 2836829c878SJohannes Berg wdev->conn->auto_auth && 2846829c878SJohannes Berg wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) { 2856829c878SJohannes Berg /* select automatically between only open, shared, leap */ 2866829c878SJohannes Berg switch (wdev->conn->params.auth_type) { 2876829c878SJohannes Berg case NL80211_AUTHTYPE_OPEN_SYSTEM: 288fffd0934SJohannes Berg if (wdev->connect_keys) 2896829c878SJohannes Berg wdev->conn->params.auth_type = 2906829c878SJohannes Berg NL80211_AUTHTYPE_SHARED_KEY; 291fffd0934SJohannes Berg else 292fffd0934SJohannes Berg wdev->conn->params.auth_type = 293fffd0934SJohannes Berg NL80211_AUTHTYPE_NETWORK_EAP; 2946829c878SJohannes Berg break; 2956829c878SJohannes Berg case NL80211_AUTHTYPE_SHARED_KEY: 2966829c878SJohannes Berg wdev->conn->params.auth_type = 2976829c878SJohannes Berg NL80211_AUTHTYPE_NETWORK_EAP; 2986829c878SJohannes Berg break; 2996829c878SJohannes Berg default: 3006829c878SJohannes Berg /* huh? */ 3016829c878SJohannes Berg wdev->conn->params.auth_type = 3026829c878SJohannes Berg NL80211_AUTHTYPE_OPEN_SYSTEM; 3036829c878SJohannes Berg break; 3046829c878SJohannes Berg } 3056829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; 3066829c878SJohannes Berg schedule_work(&rdev->conn_work); 30719957bb3SJohannes Berg } else if (status_code != WLAN_STATUS_SUCCESS) { 3084bde0f7dSJohannes Berg __cfg80211_connect_result(dev, mgmt->bssid, NULL, 0, NULL, 0, 309df7fc0f9SJohannes Berg status_code, false, NULL); 31019957bb3SJohannes Berg } else if (wdev->sme_state == CFG80211_SME_CONNECTING && 3116829c878SJohannes Berg wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { 3126829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; 3136829c878SJohannes Berg schedule_work(&rdev->conn_work); 3146829c878SJohannes Berg } 3156829c878SJohannes Berg } 316b23aa676SSamuel Ortiz 317667503ddSJohannes Berg void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, 318b23aa676SSamuel Ortiz const u8 *req_ie, size_t req_ie_len, 319b23aa676SSamuel Ortiz const u8 *resp_ie, size_t resp_ie_len, 320df7fc0f9SJohannes Berg u16 status, bool wextev, 321df7fc0f9SJohannes Berg struct cfg80211_bss *bss) 322b23aa676SSamuel Ortiz { 323b23aa676SSamuel Ortiz struct wireless_dev *wdev = dev->ieee80211_ptr; 3248b19e6caSLuis R. Rodriguez u8 *country_ie; 325b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT 326b23aa676SSamuel Ortiz union iwreq_data wrqu; 327b23aa676SSamuel Ortiz #endif 328b23aa676SSamuel Ortiz 329667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 330667503ddSJohannes Berg 331b23aa676SSamuel Ortiz if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 332b23aa676SSamuel Ortiz return; 333b23aa676SSamuel Ortiz 334e45cd82aSJohannes Berg if (wdev->sme_state == CFG80211_SME_CONNECTED) 335e45cd82aSJohannes Berg nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, 336e45cd82aSJohannes Berg bssid, req_ie, req_ie_len, 337667503ddSJohannes Berg resp_ie, resp_ie_len, GFP_KERNEL); 338e45cd82aSJohannes Berg else 339e45cd82aSJohannes Berg nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, 340e45cd82aSJohannes Berg bssid, req_ie, req_ie_len, 341e45cd82aSJohannes Berg resp_ie, resp_ie_len, 342667503ddSJohannes Berg status, GFP_KERNEL); 343e45cd82aSJohannes Berg 344e45cd82aSJohannes Berg #ifdef CONFIG_WIRELESS_EXT 345e45cd82aSJohannes Berg if (wextev) { 346e45cd82aSJohannes Berg if (req_ie && status == WLAN_STATUS_SUCCESS) { 347e45cd82aSJohannes Berg memset(&wrqu, 0, sizeof(wrqu)); 348e45cd82aSJohannes Berg wrqu.data.length = req_ie_len; 3493409ff77SZhu Yi wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, req_ie); 350e45cd82aSJohannes Berg } 351e45cd82aSJohannes Berg 352e45cd82aSJohannes Berg if (resp_ie && status == WLAN_STATUS_SUCCESS) { 353e45cd82aSJohannes Berg memset(&wrqu, 0, sizeof(wrqu)); 354e45cd82aSJohannes Berg wrqu.data.length = resp_ie_len; 355e45cd82aSJohannes Berg wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); 356e45cd82aSJohannes Berg } 357e45cd82aSJohannes Berg 358e45cd82aSJohannes Berg memset(&wrqu, 0, sizeof(wrqu)); 359e45cd82aSJohannes Berg wrqu.ap_addr.sa_family = ARPHRD_ETHER; 360e45cd82aSJohannes Berg if (bssid && status == WLAN_STATUS_SUCCESS) 361e45cd82aSJohannes Berg memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); 362e45cd82aSJohannes Berg wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); 363e45cd82aSJohannes Berg } 364e45cd82aSJohannes Berg #endif 365e45cd82aSJohannes Berg 366df7fc0f9SJohannes Berg if (wdev->current_bss) { 367df7fc0f9SJohannes Berg cfg80211_unhold_bss(wdev->current_bss); 368df7fc0f9SJohannes Berg cfg80211_put_bss(&wdev->current_bss->pub); 369df7fc0f9SJohannes Berg wdev->current_bss = NULL; 370df7fc0f9SJohannes Berg } 371df7fc0f9SJohannes Berg 372e45cd82aSJohannes Berg if (status == WLAN_STATUS_SUCCESS && 373fffd0934SJohannes Berg wdev->sme_state == CFG80211_SME_IDLE) 374fffd0934SJohannes Berg goto success; 375e45cd82aSJohannes Berg 3766829c878SJohannes Berg if (wdev->sme_state != CFG80211_SME_CONNECTING) 377b23aa676SSamuel Ortiz return; 378b23aa676SSamuel Ortiz 37919957bb3SJohannes Berg if (wdev->conn) 38019957bb3SJohannes Berg wdev->conn->state = CFG80211_CONN_IDLE; 38119957bb3SJohannes Berg 382fffd0934SJohannes Berg if (status != WLAN_STATUS_SUCCESS) { 383fffd0934SJohannes Berg wdev->sme_state = CFG80211_SME_IDLE; 384fffd0934SJohannes Berg kfree(wdev->conn); 385fffd0934SJohannes Berg wdev->conn = NULL; 386fffd0934SJohannes Berg kfree(wdev->connect_keys); 387fffd0934SJohannes Berg wdev->connect_keys = NULL; 3888dadadb7SJohannes Berg wdev->ssid_len = 0; 389fffd0934SJohannes Berg return; 390fffd0934SJohannes Berg } 391fffd0934SJohannes Berg 392df7fc0f9SJohannes Berg success: 393df7fc0f9SJohannes Berg if (!bss) 394b23aa676SSamuel Ortiz bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, 395b23aa676SSamuel Ortiz wdev->ssid, wdev->ssid_len, 396b23aa676SSamuel Ortiz WLAN_CAPABILITY_ESS, 397b23aa676SSamuel Ortiz WLAN_CAPABILITY_ESS); 398b23aa676SSamuel Ortiz 399b23aa676SSamuel Ortiz if (WARN_ON(!bss)) 400b23aa676SSamuel Ortiz return; 401b23aa676SSamuel Ortiz 40219957bb3SJohannes Berg cfg80211_hold_bss(bss_from_pub(bss)); 40319957bb3SJohannes Berg wdev->current_bss = bss_from_pub(bss); 404b23aa676SSamuel Ortiz 405b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_CONNECTED; 406fffd0934SJohannes Berg cfg80211_upload_connect_keys(wdev); 4078b19e6caSLuis R. Rodriguez 4088b19e6caSLuis R. Rodriguez country_ie = (u8 *) ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); 4098b19e6caSLuis R. Rodriguez 4108b19e6caSLuis R. Rodriguez if (!country_ie) 4118b19e6caSLuis R. Rodriguez return; 4128b19e6caSLuis R. Rodriguez 4138b19e6caSLuis R. Rodriguez /* 4148b19e6caSLuis R. Rodriguez * ieee80211_bss_get_ie() ensures we can access: 4158b19e6caSLuis R. Rodriguez * - country_ie + 2, the start of the country ie data, and 4168b19e6caSLuis R. Rodriguez * - and country_ie[1] which is the IE length 4178b19e6caSLuis R. Rodriguez */ 4188b19e6caSLuis R. Rodriguez regulatory_hint_11d(wdev->wiphy, 4198b19e6caSLuis R. Rodriguez country_ie + 2, 4208b19e6caSLuis R. Rodriguez country_ie[1]); 421b23aa676SSamuel Ortiz } 422f2129354SJohannes Berg 423f2129354SJohannes Berg void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, 424f2129354SJohannes Berg const u8 *req_ie, size_t req_ie_len, 425f2129354SJohannes Berg const u8 *resp_ie, size_t resp_ie_len, 426f2129354SJohannes Berg u16 status, gfp_t gfp) 427f2129354SJohannes Berg { 428667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 429667503ddSJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 430667503ddSJohannes Berg struct cfg80211_event *ev; 431667503ddSJohannes Berg unsigned long flags; 432667503ddSJohannes Berg 433667503ddSJohannes Berg ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); 434667503ddSJohannes Berg if (!ev) 435667503ddSJohannes Berg return; 436667503ddSJohannes Berg 437667503ddSJohannes Berg ev->type = EVENT_CONNECT_RESULT; 438667503ddSJohannes Berg memcpy(ev->cr.bssid, bssid, ETH_ALEN); 439667503ddSJohannes Berg ev->cr.req_ie = ((u8 *)ev) + sizeof(*ev); 440667503ddSJohannes Berg ev->cr.req_ie_len = req_ie_len; 441667503ddSJohannes Berg memcpy((void *)ev->cr.req_ie, req_ie, req_ie_len); 442667503ddSJohannes Berg ev->cr.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; 443667503ddSJohannes Berg ev->cr.resp_ie_len = resp_ie_len; 444667503ddSJohannes Berg memcpy((void *)ev->cr.resp_ie, resp_ie, resp_ie_len); 445667503ddSJohannes Berg ev->cr.status = status; 446667503ddSJohannes Berg 447667503ddSJohannes Berg spin_lock_irqsave(&wdev->event_lock, flags); 448667503ddSJohannes Berg list_add_tail(&ev->list, &wdev->event_list); 449667503ddSJohannes Berg spin_unlock_irqrestore(&wdev->event_lock, flags); 450667503ddSJohannes Berg schedule_work(&rdev->event_work); 451f2129354SJohannes Berg } 452b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_connect_result); 453b23aa676SSamuel Ortiz 454667503ddSJohannes Berg void __cfg80211_roamed(struct wireless_dev *wdev, const u8 *bssid, 455b23aa676SSamuel Ortiz const u8 *req_ie, size_t req_ie_len, 456667503ddSJohannes Berg const u8 *resp_ie, size_t resp_ie_len) 457b23aa676SSamuel Ortiz { 458b23aa676SSamuel Ortiz struct cfg80211_bss *bss; 459b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT 460b23aa676SSamuel Ortiz union iwreq_data wrqu; 461b23aa676SSamuel Ortiz #endif 462b23aa676SSamuel Ortiz 463667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 464667503ddSJohannes Berg 465b23aa676SSamuel Ortiz if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 466b23aa676SSamuel Ortiz return; 467b23aa676SSamuel Ortiz 468b23aa676SSamuel Ortiz if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) 469b23aa676SSamuel Ortiz return; 470b23aa676SSamuel Ortiz 471b23aa676SSamuel Ortiz /* internal error -- how did we get to CONNECTED w/o BSS? */ 472b23aa676SSamuel Ortiz if (WARN_ON(!wdev->current_bss)) { 473b23aa676SSamuel Ortiz return; 474b23aa676SSamuel Ortiz } 475b23aa676SSamuel Ortiz 476b23aa676SSamuel Ortiz cfg80211_unhold_bss(wdev->current_bss); 47719957bb3SJohannes Berg cfg80211_put_bss(&wdev->current_bss->pub); 478b23aa676SSamuel Ortiz wdev->current_bss = NULL; 479b23aa676SSamuel Ortiz 480b23aa676SSamuel Ortiz bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, 481b23aa676SSamuel Ortiz wdev->ssid, wdev->ssid_len, 482b23aa676SSamuel Ortiz WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); 483b23aa676SSamuel Ortiz 484b23aa676SSamuel Ortiz if (WARN_ON(!bss)) 485b23aa676SSamuel Ortiz return; 486b23aa676SSamuel Ortiz 48719957bb3SJohannes Berg cfg80211_hold_bss(bss_from_pub(bss)); 48819957bb3SJohannes Berg wdev->current_bss = bss_from_pub(bss); 489b23aa676SSamuel Ortiz 490667503ddSJohannes Berg nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), wdev->netdev, bssid, 491667503ddSJohannes Berg req_ie, req_ie_len, resp_ie, resp_ie_len, 492667503ddSJohannes Berg GFP_KERNEL); 493b23aa676SSamuel Ortiz 494b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT 495b23aa676SSamuel Ortiz if (req_ie) { 496b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 497b23aa676SSamuel Ortiz wrqu.data.length = req_ie_len; 4983409ff77SZhu Yi wireless_send_event(wdev->netdev, IWEVASSOCREQIE, 499667503ddSJohannes Berg &wrqu, req_ie); 500b23aa676SSamuel Ortiz } 501b23aa676SSamuel Ortiz 502b23aa676SSamuel Ortiz if (resp_ie) { 503b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 504b23aa676SSamuel Ortiz wrqu.data.length = resp_ie_len; 505667503ddSJohannes Berg wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, 506667503ddSJohannes Berg &wrqu, resp_ie); 507b23aa676SSamuel Ortiz } 508b23aa676SSamuel Ortiz 509b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 510b23aa676SSamuel Ortiz wrqu.ap_addr.sa_family = ARPHRD_ETHER; 511b23aa676SSamuel Ortiz memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); 512667503ddSJohannes Berg wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); 513b23aa676SSamuel Ortiz #endif 514b23aa676SSamuel Ortiz } 515667503ddSJohannes Berg 516667503ddSJohannes Berg void cfg80211_roamed(struct net_device *dev, const u8 *bssid, 517667503ddSJohannes Berg const u8 *req_ie, size_t req_ie_len, 518667503ddSJohannes Berg const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) 519667503ddSJohannes Berg { 520667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 521667503ddSJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 522667503ddSJohannes Berg struct cfg80211_event *ev; 523667503ddSJohannes Berg unsigned long flags; 524667503ddSJohannes Berg 525667503ddSJohannes Berg ev = kzalloc(sizeof(*ev) + req_ie_len + resp_ie_len, gfp); 526667503ddSJohannes Berg if (!ev) 527667503ddSJohannes Berg return; 528667503ddSJohannes Berg 529667503ddSJohannes Berg ev->type = EVENT_ROAMED; 530667503ddSJohannes Berg memcpy(ev->rm.bssid, bssid, ETH_ALEN); 531667503ddSJohannes Berg ev->rm.req_ie = ((u8 *)ev) + sizeof(*ev); 532667503ddSJohannes Berg ev->rm.req_ie_len = req_ie_len; 533667503ddSJohannes Berg memcpy((void *)ev->rm.req_ie, req_ie, req_ie_len); 534667503ddSJohannes Berg ev->rm.resp_ie = ((u8 *)ev) + sizeof(*ev) + req_ie_len; 535667503ddSJohannes Berg ev->rm.resp_ie_len = resp_ie_len; 536667503ddSJohannes Berg memcpy((void *)ev->rm.resp_ie, resp_ie, resp_ie_len); 537667503ddSJohannes Berg 538667503ddSJohannes Berg spin_lock_irqsave(&wdev->event_lock, flags); 539667503ddSJohannes Berg list_add_tail(&ev->list, &wdev->event_list); 540667503ddSJohannes Berg spin_unlock_irqrestore(&wdev->event_lock, flags); 541667503ddSJohannes Berg schedule_work(&rdev->event_work); 542667503ddSJohannes Berg } 543b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_roamed); 544b23aa676SSamuel Ortiz 545667503ddSJohannes Berg void __cfg80211_disconnected(struct net_device *dev, const u8 *ie, 5466829c878SJohannes Berg size_t ie_len, u16 reason, bool from_ap) 547b23aa676SSamuel Ortiz { 548b23aa676SSamuel Ortiz struct wireless_dev *wdev = dev->ieee80211_ptr; 549fffd0934SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 550fffd0934SJohannes Berg int i; 551b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT 552b23aa676SSamuel Ortiz union iwreq_data wrqu; 553b23aa676SSamuel Ortiz #endif 554b23aa676SSamuel Ortiz 555667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 556667503ddSJohannes Berg 557b23aa676SSamuel Ortiz if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) 558b23aa676SSamuel Ortiz return; 559b23aa676SSamuel Ortiz 560b23aa676SSamuel Ortiz if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) 561b23aa676SSamuel Ortiz return; 562b23aa676SSamuel Ortiz 563b23aa676SSamuel Ortiz if (wdev->current_bss) { 564b23aa676SSamuel Ortiz cfg80211_unhold_bss(wdev->current_bss); 56519957bb3SJohannes Berg cfg80211_put_bss(&wdev->current_bss->pub); 566b23aa676SSamuel Ortiz } 567b23aa676SSamuel Ortiz 568b23aa676SSamuel Ortiz wdev->current_bss = NULL; 569b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_IDLE; 5708dadadb7SJohannes Berg wdev->ssid_len = 0; 571b23aa676SSamuel Ortiz 5726829c878SJohannes Berg if (wdev->conn) { 573b6f0b639SJohannes Berg const u8 *bssid; 574b6f0b639SJohannes Berg int ret; 575b6f0b639SJohannes Berg 5766829c878SJohannes Berg kfree(wdev->conn->ie); 5776829c878SJohannes Berg wdev->conn->ie = NULL; 57819957bb3SJohannes Berg kfree(wdev->conn); 57919957bb3SJohannes Berg wdev->conn = NULL; 580b6f0b639SJohannes Berg 581b6f0b639SJohannes Berg /* 582b6f0b639SJohannes Berg * If this disconnect was due to a disassoc, we 583b6f0b639SJohannes Berg * we might still have an auth BSS around. For 584b6f0b639SJohannes Berg * the userspace SME that's currently expected, 585b6f0b639SJohannes Berg * but for the kernel SME (nl80211 CONNECT or 586b6f0b639SJohannes Berg * wireless extensions) we want to clear up all 587b6f0b639SJohannes Berg * state. 588b6f0b639SJohannes Berg */ 589b6f0b639SJohannes Berg for (i = 0; i < MAX_AUTH_BSSES; i++) { 590b6f0b639SJohannes Berg if (!wdev->auth_bsses[i]) 591b6f0b639SJohannes Berg continue; 592b6f0b639SJohannes Berg bssid = wdev->auth_bsses[i]->pub.bssid; 593b6f0b639SJohannes Berg ret = __cfg80211_mlme_deauth(rdev, dev, bssid, NULL, 0, 594b6f0b639SJohannes Berg WLAN_REASON_DEAUTH_LEAVING); 595b6f0b639SJohannes Berg WARN(ret, "deauth failed: %d\n", ret); 596b6f0b639SJohannes Berg } 5976829c878SJohannes Berg } 5986829c878SJohannes Berg 599fffd0934SJohannes Berg nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); 600fffd0934SJohannes Berg 601fffd0934SJohannes Berg /* 602fffd0934SJohannes Berg * Delete all the keys ... pairwise keys can't really 603fffd0934SJohannes Berg * exist any more anyway, but default keys might. 604fffd0934SJohannes Berg */ 605fffd0934SJohannes Berg if (rdev->ops->del_key) 606fffd0934SJohannes Berg for (i = 0; i < 6; i++) 607fffd0934SJohannes Berg rdev->ops->del_key(wdev->wiphy, dev, i, NULL); 608b23aa676SSamuel Ortiz 609b23aa676SSamuel Ortiz #ifdef CONFIG_WIRELESS_EXT 610b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu)); 611b23aa676SSamuel Ortiz wrqu.ap_addr.sa_family = ARPHRD_ETHER; 612b23aa676SSamuel Ortiz wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); 613b23aa676SSamuel Ortiz #endif 614b23aa676SSamuel Ortiz } 615b23aa676SSamuel Ortiz 616b23aa676SSamuel Ortiz void cfg80211_disconnected(struct net_device *dev, u16 reason, 617b23aa676SSamuel Ortiz u8 *ie, size_t ie_len, gfp_t gfp) 618b23aa676SSamuel Ortiz { 619667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 620667503ddSJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 621667503ddSJohannes Berg struct cfg80211_event *ev; 622667503ddSJohannes Berg unsigned long flags; 623667503ddSJohannes Berg 624667503ddSJohannes Berg ev = kzalloc(sizeof(*ev) + ie_len, gfp); 625667503ddSJohannes Berg if (!ev) 626667503ddSJohannes Berg return; 627667503ddSJohannes Berg 628667503ddSJohannes Berg ev->type = EVENT_DISCONNECTED; 629667503ddSJohannes Berg ev->dc.ie = ((u8 *)ev) + sizeof(*ev); 630667503ddSJohannes Berg ev->dc.ie_len = ie_len; 631667503ddSJohannes Berg memcpy((void *)ev->dc.ie, ie, ie_len); 632667503ddSJohannes Berg ev->dc.reason = reason; 633667503ddSJohannes Berg 634667503ddSJohannes Berg spin_lock_irqsave(&wdev->event_lock, flags); 635667503ddSJohannes Berg list_add_tail(&ev->list, &wdev->event_list); 636667503ddSJohannes Berg spin_unlock_irqrestore(&wdev->event_lock, flags); 637667503ddSJohannes Berg schedule_work(&rdev->event_work); 638b23aa676SSamuel Ortiz } 639b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_disconnected); 640b23aa676SSamuel Ortiz 641667503ddSJohannes Berg int __cfg80211_connect(struct cfg80211_registered_device *rdev, 642b23aa676SSamuel Ortiz struct net_device *dev, 643fffd0934SJohannes Berg struct cfg80211_connect_params *connect, 644fffd0934SJohannes Berg struct cfg80211_cached_keys *connkeys) 645b23aa676SSamuel Ortiz { 646b23aa676SSamuel Ortiz struct wireless_dev *wdev = dev->ieee80211_ptr; 647667503ddSJohannes Berg int err; 648667503ddSJohannes Berg 649667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 650b23aa676SSamuel Ortiz 651b23aa676SSamuel Ortiz if (wdev->sme_state != CFG80211_SME_IDLE) 652b23aa676SSamuel Ortiz return -EALREADY; 653b23aa676SSamuel Ortiz 654fffd0934SJohannes Berg if (WARN_ON(wdev->connect_keys)) { 655fffd0934SJohannes Berg kfree(wdev->connect_keys); 656fffd0934SJohannes Berg wdev->connect_keys = NULL; 657fffd0934SJohannes Berg } 658fffd0934SJohannes Berg 659fffd0934SJohannes Berg if (connkeys && connkeys->def >= 0) { 660fffd0934SJohannes Berg int idx; 661*bcba8eaeSSamuel Ortiz u32 cipher; 662fffd0934SJohannes Berg 663fffd0934SJohannes Berg idx = connkeys->def; 664*bcba8eaeSSamuel Ortiz cipher = connkeys->params[idx].cipher; 665fffd0934SJohannes Berg /* If given a WEP key we may need it for shared key auth */ 666*bcba8eaeSSamuel Ortiz if (cipher == WLAN_CIPHER_SUITE_WEP40 || 667*bcba8eaeSSamuel Ortiz cipher == WLAN_CIPHER_SUITE_WEP104) { 668fffd0934SJohannes Berg connect->key_idx = idx; 669fffd0934SJohannes Berg connect->key = connkeys->params[idx].key; 670fffd0934SJohannes Berg connect->key_len = connkeys->params[idx].key_len; 671*bcba8eaeSSamuel Ortiz 672*bcba8eaeSSamuel Ortiz /* 673*bcba8eaeSSamuel Ortiz * If ciphers are not set (e.g. when going through 674*bcba8eaeSSamuel Ortiz * iwconfig), we have to set them appropriately here. 675*bcba8eaeSSamuel Ortiz */ 676*bcba8eaeSSamuel Ortiz if (connect->crypto.cipher_group == 0) 677*bcba8eaeSSamuel Ortiz connect->crypto.cipher_group = cipher; 678*bcba8eaeSSamuel Ortiz 679*bcba8eaeSSamuel Ortiz if (connect->crypto.n_ciphers_pairwise == 0) { 680*bcba8eaeSSamuel Ortiz connect->crypto.n_ciphers_pairwise = 1; 681*bcba8eaeSSamuel Ortiz connect->crypto.ciphers_pairwise[0] = cipher; 682*bcba8eaeSSamuel Ortiz } 683fffd0934SJohannes Berg } 684fffd0934SJohannes Berg } 685fffd0934SJohannes Berg 686b23aa676SSamuel Ortiz if (!rdev->ops->connect) { 6876829c878SJohannes Berg if (!rdev->ops->auth || !rdev->ops->assoc) 688b23aa676SSamuel Ortiz return -EOPNOTSUPP; 6896829c878SJohannes Berg 69019957bb3SJohannes Berg if (WARN_ON(wdev->conn)) 69119957bb3SJohannes Berg return -EINPROGRESS; 69219957bb3SJohannes Berg 6936829c878SJohannes Berg wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); 6946829c878SJohannes Berg if (!wdev->conn) 6956829c878SJohannes Berg return -ENOMEM; 6966829c878SJohannes Berg 6976829c878SJohannes Berg /* 6986829c878SJohannes Berg * Copy all parameters, and treat explicitly IEs, BSSID, SSID. 6996829c878SJohannes Berg */ 7006829c878SJohannes Berg memcpy(&wdev->conn->params, connect, sizeof(*connect)); 7016829c878SJohannes Berg if (connect->bssid) { 7026829c878SJohannes Berg wdev->conn->params.bssid = wdev->conn->bssid; 7036829c878SJohannes Berg memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); 7046829c878SJohannes Berg } 7056829c878SJohannes Berg 7066829c878SJohannes Berg if (connect->ie) { 7076829c878SJohannes Berg wdev->conn->ie = kmemdup(connect->ie, connect->ie_len, 7086829c878SJohannes Berg GFP_KERNEL); 7096829c878SJohannes Berg wdev->conn->params.ie = wdev->conn->ie; 71019957bb3SJohannes Berg if (!wdev->conn->ie) { 71119957bb3SJohannes Berg kfree(wdev->conn); 71219957bb3SJohannes Berg wdev->conn = NULL; 7136829c878SJohannes Berg return -ENOMEM; 7146829c878SJohannes Berg } 71519957bb3SJohannes Berg } 7166829c878SJohannes Berg 7176829c878SJohannes Berg if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { 7186829c878SJohannes Berg wdev->conn->auto_auth = true; 7196829c878SJohannes Berg /* start with open system ... should mostly work */ 7206829c878SJohannes Berg wdev->conn->params.auth_type = 7216829c878SJohannes Berg NL80211_AUTHTYPE_OPEN_SYSTEM; 7226829c878SJohannes Berg } else { 7236829c878SJohannes Berg wdev->conn->auto_auth = false; 7246829c878SJohannes Berg } 7256829c878SJohannes Berg 7266829c878SJohannes Berg memcpy(wdev->ssid, connect->ssid, connect->ssid_len); 7276829c878SJohannes Berg wdev->ssid_len = connect->ssid_len; 7286829c878SJohannes Berg wdev->conn->params.ssid = wdev->ssid; 7296829c878SJohannes Berg wdev->conn->params.ssid_len = connect->ssid_len; 7306829c878SJohannes Berg 7316829c878SJohannes Berg /* don't care about result -- but fill bssid & channel */ 7326829c878SJohannes Berg if (!wdev->conn->params.bssid || !wdev->conn->params.channel) 7336829c878SJohannes Berg cfg80211_get_conn_bss(wdev); 7346829c878SJohannes Berg 7356829c878SJohannes Berg wdev->sme_state = CFG80211_SME_CONNECTING; 736fffd0934SJohannes Berg wdev->connect_keys = connkeys; 7376829c878SJohannes Berg 7386829c878SJohannes Berg /* we're good if we have both BSSID and channel */ 7396829c878SJohannes Berg if (wdev->conn->params.bssid && wdev->conn->params.channel) { 7406829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; 7416829c878SJohannes Berg err = cfg80211_conn_do_work(wdev); 7426829c878SJohannes Berg } else { 7436829c878SJohannes Berg /* otherwise we'll need to scan for the AP first */ 7446829c878SJohannes Berg err = cfg80211_conn_scan(wdev); 7456829c878SJohannes Berg /* 7466829c878SJohannes Berg * If we can't scan right now, then we need to scan again 7476829c878SJohannes Berg * after the current scan finished, since the parameters 7486829c878SJohannes Berg * changed (unless we find a good AP anyway). 7496829c878SJohannes Berg */ 7506829c878SJohannes Berg if (err == -EBUSY) { 7516829c878SJohannes Berg err = 0; 7526829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; 7536829c878SJohannes Berg } 7546829c878SJohannes Berg } 75519957bb3SJohannes Berg if (err) { 75619957bb3SJohannes Berg kfree(wdev->conn); 75719957bb3SJohannes Berg wdev->conn = NULL; 7586829c878SJohannes Berg wdev->sme_state = CFG80211_SME_IDLE; 759fffd0934SJohannes Berg wdev->connect_keys = NULL; 7608dadadb7SJohannes Berg wdev->ssid_len = 0; 76119957bb3SJohannes Berg } 7626829c878SJohannes Berg 7636829c878SJohannes Berg return err; 764b23aa676SSamuel Ortiz } else { 765b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_CONNECTING; 766fffd0934SJohannes Berg wdev->connect_keys = connkeys; 767b23aa676SSamuel Ortiz err = rdev->ops->connect(&rdev->wiphy, dev, connect); 768b23aa676SSamuel Ortiz if (err) { 769fffd0934SJohannes Berg wdev->connect_keys = NULL; 770b23aa676SSamuel Ortiz wdev->sme_state = CFG80211_SME_IDLE; 771b23aa676SSamuel Ortiz return err; 772b23aa676SSamuel Ortiz } 773b23aa676SSamuel Ortiz 774b23aa676SSamuel Ortiz memcpy(wdev->ssid, connect->ssid, connect->ssid_len); 775b23aa676SSamuel Ortiz wdev->ssid_len = connect->ssid_len; 776b23aa676SSamuel Ortiz 777b23aa676SSamuel Ortiz return 0; 778b23aa676SSamuel Ortiz } 7796829c878SJohannes Berg } 780b23aa676SSamuel Ortiz 781667503ddSJohannes Berg int cfg80211_connect(struct cfg80211_registered_device *rdev, 782667503ddSJohannes Berg struct net_device *dev, 783fffd0934SJohannes Berg struct cfg80211_connect_params *connect, 784fffd0934SJohannes Berg struct cfg80211_cached_keys *connkeys) 785667503ddSJohannes Berg { 786667503ddSJohannes Berg int err; 787667503ddSJohannes Berg 788667503ddSJohannes Berg wdev_lock(dev->ieee80211_ptr); 789fffd0934SJohannes Berg err = __cfg80211_connect(rdev, dev, connect, connkeys); 790667503ddSJohannes Berg wdev_unlock(dev->ieee80211_ptr); 791667503ddSJohannes Berg 792667503ddSJohannes Berg return err; 793667503ddSJohannes Berg } 794667503ddSJohannes Berg 795667503ddSJohannes Berg int __cfg80211_disconnect(struct cfg80211_registered_device *rdev, 796f2129354SJohannes Berg struct net_device *dev, u16 reason, bool wextev) 797b23aa676SSamuel Ortiz { 7986829c878SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 799b23aa676SSamuel Ortiz int err; 800b23aa676SSamuel Ortiz 801667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 802667503ddSJohannes Berg 8036829c878SJohannes Berg if (wdev->sme_state == CFG80211_SME_IDLE) 8046829c878SJohannes Berg return -EINVAL; 8056829c878SJohannes Berg 806fffd0934SJohannes Berg kfree(wdev->connect_keys); 807fffd0934SJohannes Berg wdev->connect_keys = NULL; 808fffd0934SJohannes Berg 809b23aa676SSamuel Ortiz if (!rdev->ops->disconnect) { 81019957bb3SJohannes Berg if (!rdev->ops->deauth) 81119957bb3SJohannes Berg return -EOPNOTSUPP; 8126829c878SJohannes Berg 81319957bb3SJohannes Berg /* was it connected by userspace SME? */ 81419957bb3SJohannes Berg if (!wdev->conn) { 81519957bb3SJohannes Berg cfg80211_mlme_down(rdev, dev); 81619957bb3SJohannes Berg return 0; 81719957bb3SJohannes Berg } 8186829c878SJohannes Berg 8196829c878SJohannes Berg if (wdev->sme_state == CFG80211_SME_CONNECTING && 8206829c878SJohannes Berg (wdev->conn->state == CFG80211_CONN_SCANNING || 8216829c878SJohannes Berg wdev->conn->state == CFG80211_CONN_SCAN_AGAIN)) { 8226829c878SJohannes Berg wdev->sme_state = CFG80211_SME_IDLE; 82319957bb3SJohannes Berg kfree(wdev->conn); 82419957bb3SJohannes Berg wdev->conn = NULL; 8258dadadb7SJohannes Berg wdev->ssid_len = 0; 8266829c878SJohannes Berg return 0; 8276829c878SJohannes Berg } 8286829c878SJohannes Berg 8296829c878SJohannes Berg /* wdev->conn->params.bssid must be set if > SCANNING */ 830667503ddSJohannes Berg err = __cfg80211_mlme_deauth(rdev, dev, 831667503ddSJohannes Berg wdev->conn->params.bssid, 83219957bb3SJohannes Berg NULL, 0, reason); 8336829c878SJohannes Berg if (err) 8346829c878SJohannes Berg return err; 835b23aa676SSamuel Ortiz } else { 836b23aa676SSamuel Ortiz err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); 837b23aa676SSamuel Ortiz if (err) 838b23aa676SSamuel Ortiz return err; 839b23aa676SSamuel Ortiz } 840b23aa676SSamuel Ortiz 8416829c878SJohannes Berg if (wdev->sme_state == CFG80211_SME_CONNECTED) 842667503ddSJohannes Berg __cfg80211_disconnected(dev, NULL, 0, 0, false); 8436829c878SJohannes Berg else if (wdev->sme_state == CFG80211_SME_CONNECTING) 844f2129354SJohannes Berg __cfg80211_connect_result(dev, NULL, NULL, 0, NULL, 0, 8456829c878SJohannes Berg WLAN_STATUS_UNSPECIFIED_FAILURE, 846df7fc0f9SJohannes Berg wextev, NULL); 847b23aa676SSamuel Ortiz 848b23aa676SSamuel Ortiz return 0; 849b23aa676SSamuel Ortiz } 85019957bb3SJohannes Berg 851667503ddSJohannes Berg int cfg80211_disconnect(struct cfg80211_registered_device *rdev, 852667503ddSJohannes Berg struct net_device *dev, 853667503ddSJohannes Berg u16 reason, bool wextev) 854667503ddSJohannes Berg { 855667503ddSJohannes Berg int err; 856667503ddSJohannes Berg 857667503ddSJohannes Berg wdev_lock(dev->ieee80211_ptr); 858667503ddSJohannes Berg err = __cfg80211_disconnect(rdev, dev, reason, wextev); 859667503ddSJohannes Berg wdev_unlock(dev->ieee80211_ptr); 860667503ddSJohannes Berg 861667503ddSJohannes Berg return err; 862667503ddSJohannes Berg } 863667503ddSJohannes Berg 86419957bb3SJohannes Berg void cfg80211_sme_disassoc(struct net_device *dev, int idx) 86519957bb3SJohannes Berg { 86619957bb3SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr; 86719957bb3SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); 86819957bb3SJohannes Berg u8 bssid[ETH_ALEN]; 86919957bb3SJohannes Berg 870667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev); 871667503ddSJohannes Berg 87219957bb3SJohannes Berg if (!wdev->conn) 87319957bb3SJohannes Berg return; 87419957bb3SJohannes Berg 87519957bb3SJohannes Berg if (wdev->conn->state == CFG80211_CONN_IDLE) 87619957bb3SJohannes Berg return; 87719957bb3SJohannes Berg 87819957bb3SJohannes Berg /* 87919957bb3SJohannes Berg * Ok, so the association was made by this SME -- we don't 88019957bb3SJohannes Berg * want it any more so deauthenticate too. 88119957bb3SJohannes Berg */ 88219957bb3SJohannes Berg 88319957bb3SJohannes Berg if (!wdev->auth_bsses[idx]) 88419957bb3SJohannes Berg return; 88519957bb3SJohannes Berg 88619957bb3SJohannes Berg memcpy(bssid, wdev->auth_bsses[idx]->pub.bssid, ETH_ALEN); 887ec3f1490SJohannes Berg if (__cfg80211_mlme_deauth(rdev, dev, bssid, 88819957bb3SJohannes Berg NULL, 0, WLAN_REASON_DEAUTH_LEAVING)) { 88919957bb3SJohannes Berg /* whatever -- assume gone anyway */ 89019957bb3SJohannes Berg cfg80211_unhold_bss(wdev->auth_bsses[idx]); 89119957bb3SJohannes Berg cfg80211_put_bss(&wdev->auth_bsses[idx]->pub); 89219957bb3SJohannes Berg wdev->auth_bsses[idx] = NULL; 89319957bb3SJohannes Berg } 89419957bb3SJohannes Berg } 895