162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SME code for cfg80211 462306a36Sopenharmony_ci * both driver SME event handling and the SME implementation 562306a36Sopenharmony_ci * (for nl80211's connect() and wext) 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> 862306a36Sopenharmony_ci * Copyright (C) 2009, 2020, 2022-2023 Intel Corporation. All rights reserved. 962306a36Sopenharmony_ci * Copyright 2017 Intel Deutschland GmbH 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/etherdevice.h> 1362306a36Sopenharmony_ci#include <linux/if_arp.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/workqueue.h> 1662306a36Sopenharmony_ci#include <linux/wireless.h> 1762306a36Sopenharmony_ci#include <linux/export.h> 1862306a36Sopenharmony_ci#include <net/iw_handler.h> 1962306a36Sopenharmony_ci#include <net/cfg80211.h> 2062306a36Sopenharmony_ci#include <net/rtnetlink.h> 2162306a36Sopenharmony_ci#include "nl80211.h" 2262306a36Sopenharmony_ci#include "reg.h" 2362306a36Sopenharmony_ci#include "rdev-ops.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * Software SME in cfg80211, using auth/assoc/deauth calls to the 2762306a36Sopenharmony_ci * driver. This is for implementing nl80211's connect/disconnect 2862306a36Sopenharmony_ci * and wireless extensions (if configured.) 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct cfg80211_conn { 3262306a36Sopenharmony_ci struct cfg80211_connect_params params; 3362306a36Sopenharmony_ci /* these are sub-states of the _CONNECTING sme_state */ 3462306a36Sopenharmony_ci enum { 3562306a36Sopenharmony_ci CFG80211_CONN_SCANNING, 3662306a36Sopenharmony_ci CFG80211_CONN_SCAN_AGAIN, 3762306a36Sopenharmony_ci CFG80211_CONN_AUTHENTICATE_NEXT, 3862306a36Sopenharmony_ci CFG80211_CONN_AUTHENTICATING, 3962306a36Sopenharmony_ci CFG80211_CONN_AUTH_FAILED_TIMEOUT, 4062306a36Sopenharmony_ci CFG80211_CONN_ASSOCIATE_NEXT, 4162306a36Sopenharmony_ci CFG80211_CONN_ASSOCIATING, 4262306a36Sopenharmony_ci CFG80211_CONN_ASSOC_FAILED, 4362306a36Sopenharmony_ci CFG80211_CONN_ASSOC_FAILED_TIMEOUT, 4462306a36Sopenharmony_ci CFG80211_CONN_DEAUTH, 4562306a36Sopenharmony_ci CFG80211_CONN_ABANDON, 4662306a36Sopenharmony_ci CFG80211_CONN_CONNECTED, 4762306a36Sopenharmony_ci } state; 4862306a36Sopenharmony_ci u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN]; 4962306a36Sopenharmony_ci const u8 *ie; 5062306a36Sopenharmony_ci size_t ie_len; 5162306a36Sopenharmony_ci bool auto_auth, prev_bssid_valid; 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void cfg80211_sme_free(struct wireless_dev *wdev) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci if (!wdev->conn) 5762306a36Sopenharmony_ci return; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci kfree(wdev->conn->ie); 6062306a36Sopenharmony_ci kfree(wdev->conn); 6162306a36Sopenharmony_ci wdev->conn = NULL; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic int cfg80211_conn_scan(struct wireless_dev *wdev) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 6762306a36Sopenharmony_ci struct cfg80211_scan_request *request; 6862306a36Sopenharmony_ci int n_channels, err; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci ASSERT_WDEV_LOCK(wdev); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (rdev->scan_req || rdev->scan_msg) 7362306a36Sopenharmony_ci return -EBUSY; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (wdev->conn->params.channel) 7662306a36Sopenharmony_ci n_channels = 1; 7762306a36Sopenharmony_ci else 7862306a36Sopenharmony_ci n_channels = ieee80211_get_num_supported_channels(wdev->wiphy); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) + 8162306a36Sopenharmony_ci sizeof(request->channels[0]) * n_channels, 8262306a36Sopenharmony_ci GFP_KERNEL); 8362306a36Sopenharmony_ci if (!request) 8462306a36Sopenharmony_ci return -ENOMEM; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if (wdev->conn->params.channel) { 8762306a36Sopenharmony_ci enum nl80211_band band = wdev->conn->params.channel->band; 8862306a36Sopenharmony_ci struct ieee80211_supported_band *sband = 8962306a36Sopenharmony_ci wdev->wiphy->bands[band]; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (!sband) { 9262306a36Sopenharmony_ci kfree(request); 9362306a36Sopenharmony_ci return -EINVAL; 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_ci request->channels[0] = wdev->conn->params.channel; 9662306a36Sopenharmony_ci request->rates[band] = (1 << sband->n_bitrates) - 1; 9762306a36Sopenharmony_ci } else { 9862306a36Sopenharmony_ci int i = 0, j; 9962306a36Sopenharmony_ci enum nl80211_band band; 10062306a36Sopenharmony_ci struct ieee80211_supported_band *bands; 10162306a36Sopenharmony_ci struct ieee80211_channel *channel; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci for (band = 0; band < NUM_NL80211_BANDS; band++) { 10462306a36Sopenharmony_ci bands = wdev->wiphy->bands[band]; 10562306a36Sopenharmony_ci if (!bands) 10662306a36Sopenharmony_ci continue; 10762306a36Sopenharmony_ci for (j = 0; j < bands->n_channels; j++) { 10862306a36Sopenharmony_ci channel = &bands->channels[j]; 10962306a36Sopenharmony_ci if (channel->flags & IEEE80211_CHAN_DISABLED) 11062306a36Sopenharmony_ci continue; 11162306a36Sopenharmony_ci request->channels[i++] = channel; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci request->rates[band] = (1 << bands->n_bitrates) - 1; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci n_channels = i; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci request->n_channels = n_channels; 11862306a36Sopenharmony_ci request->ssids = (void *)&request->channels[n_channels]; 11962306a36Sopenharmony_ci request->n_ssids = 1; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci memcpy(request->ssids[0].ssid, wdev->conn->params.ssid, 12262306a36Sopenharmony_ci wdev->conn->params.ssid_len); 12362306a36Sopenharmony_ci request->ssids[0].ssid_len = wdev->conn->params.ssid_len; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci eth_broadcast_addr(request->bssid); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci request->wdev = wdev; 12862306a36Sopenharmony_ci request->wiphy = &rdev->wiphy; 12962306a36Sopenharmony_ci request->scan_start = jiffies; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci rdev->scan_req = request; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci err = rdev_scan(rdev, request); 13462306a36Sopenharmony_ci if (!err) { 13562306a36Sopenharmony_ci wdev->conn->state = CFG80211_CONN_SCANNING; 13662306a36Sopenharmony_ci nl80211_send_scan_start(rdev, wdev); 13762306a36Sopenharmony_ci dev_hold(wdev->netdev); 13862306a36Sopenharmony_ci } else { 13962306a36Sopenharmony_ci rdev->scan_req = NULL; 14062306a36Sopenharmony_ci kfree(request); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci return err; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int cfg80211_conn_do_work(struct wireless_dev *wdev, 14662306a36Sopenharmony_ci enum nl80211_timeout_reason *treason) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 14962306a36Sopenharmony_ci struct cfg80211_connect_params *params; 15062306a36Sopenharmony_ci struct cfg80211_auth_request auth_req = {}; 15162306a36Sopenharmony_ci struct cfg80211_assoc_request req = {}; 15262306a36Sopenharmony_ci int err; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci ASSERT_WDEV_LOCK(wdev); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (!wdev->conn) 15762306a36Sopenharmony_ci return 0; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci params = &wdev->conn->params; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci switch (wdev->conn->state) { 16262306a36Sopenharmony_ci case CFG80211_CONN_SCANNING: 16362306a36Sopenharmony_ci /* didn't find it during scan ... */ 16462306a36Sopenharmony_ci return -ENOENT; 16562306a36Sopenharmony_ci case CFG80211_CONN_SCAN_AGAIN: 16662306a36Sopenharmony_ci return cfg80211_conn_scan(wdev); 16762306a36Sopenharmony_ci case CFG80211_CONN_AUTHENTICATE_NEXT: 16862306a36Sopenharmony_ci if (WARN_ON(!rdev->ops->auth)) 16962306a36Sopenharmony_ci return -EOPNOTSUPP; 17062306a36Sopenharmony_ci wdev->conn->state = CFG80211_CONN_AUTHENTICATING; 17162306a36Sopenharmony_ci auth_req.key = params->key; 17262306a36Sopenharmony_ci auth_req.key_len = params->key_len; 17362306a36Sopenharmony_ci auth_req.key_idx = params->key_idx; 17462306a36Sopenharmony_ci auth_req.auth_type = params->auth_type; 17562306a36Sopenharmony_ci auth_req.bss = cfg80211_get_bss(&rdev->wiphy, params->channel, 17662306a36Sopenharmony_ci params->bssid, 17762306a36Sopenharmony_ci params->ssid, params->ssid_len, 17862306a36Sopenharmony_ci IEEE80211_BSS_TYPE_ESS, 17962306a36Sopenharmony_ci IEEE80211_PRIVACY_ANY); 18062306a36Sopenharmony_ci auth_req.link_id = -1; 18162306a36Sopenharmony_ci err = cfg80211_mlme_auth(rdev, wdev->netdev, &auth_req); 18262306a36Sopenharmony_ci cfg80211_put_bss(&rdev->wiphy, auth_req.bss); 18362306a36Sopenharmony_ci return err; 18462306a36Sopenharmony_ci case CFG80211_CONN_AUTH_FAILED_TIMEOUT: 18562306a36Sopenharmony_ci *treason = NL80211_TIMEOUT_AUTH; 18662306a36Sopenharmony_ci return -ENOTCONN; 18762306a36Sopenharmony_ci case CFG80211_CONN_ASSOCIATE_NEXT: 18862306a36Sopenharmony_ci if (WARN_ON(!rdev->ops->assoc)) 18962306a36Sopenharmony_ci return -EOPNOTSUPP; 19062306a36Sopenharmony_ci wdev->conn->state = CFG80211_CONN_ASSOCIATING; 19162306a36Sopenharmony_ci if (wdev->conn->prev_bssid_valid) 19262306a36Sopenharmony_ci req.prev_bssid = wdev->conn->prev_bssid; 19362306a36Sopenharmony_ci req.ie = params->ie; 19462306a36Sopenharmony_ci req.ie_len = params->ie_len; 19562306a36Sopenharmony_ci req.use_mfp = params->mfp != NL80211_MFP_NO; 19662306a36Sopenharmony_ci req.crypto = params->crypto; 19762306a36Sopenharmony_ci req.flags = params->flags; 19862306a36Sopenharmony_ci req.ht_capa = params->ht_capa; 19962306a36Sopenharmony_ci req.ht_capa_mask = params->ht_capa_mask; 20062306a36Sopenharmony_ci req.vht_capa = params->vht_capa; 20162306a36Sopenharmony_ci req.vht_capa_mask = params->vht_capa_mask; 20262306a36Sopenharmony_ci req.link_id = -1; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci req.bss = cfg80211_get_bss(&rdev->wiphy, params->channel, 20562306a36Sopenharmony_ci params->bssid, 20662306a36Sopenharmony_ci params->ssid, params->ssid_len, 20762306a36Sopenharmony_ci IEEE80211_BSS_TYPE_ESS, 20862306a36Sopenharmony_ci IEEE80211_PRIVACY_ANY); 20962306a36Sopenharmony_ci if (!req.bss) { 21062306a36Sopenharmony_ci err = -ENOENT; 21162306a36Sopenharmony_ci } else { 21262306a36Sopenharmony_ci err = cfg80211_mlme_assoc(rdev, wdev->netdev, &req); 21362306a36Sopenharmony_ci cfg80211_put_bss(&rdev->wiphy, req.bss); 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (err) 21762306a36Sopenharmony_ci cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, 21862306a36Sopenharmony_ci NULL, 0, 21962306a36Sopenharmony_ci WLAN_REASON_DEAUTH_LEAVING, 22062306a36Sopenharmony_ci false); 22162306a36Sopenharmony_ci return err; 22262306a36Sopenharmony_ci case CFG80211_CONN_ASSOC_FAILED_TIMEOUT: 22362306a36Sopenharmony_ci *treason = NL80211_TIMEOUT_ASSOC; 22462306a36Sopenharmony_ci fallthrough; 22562306a36Sopenharmony_ci case CFG80211_CONN_ASSOC_FAILED: 22662306a36Sopenharmony_ci cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, 22762306a36Sopenharmony_ci NULL, 0, 22862306a36Sopenharmony_ci WLAN_REASON_DEAUTH_LEAVING, false); 22962306a36Sopenharmony_ci return -ENOTCONN; 23062306a36Sopenharmony_ci case CFG80211_CONN_DEAUTH: 23162306a36Sopenharmony_ci cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid, 23262306a36Sopenharmony_ci NULL, 0, 23362306a36Sopenharmony_ci WLAN_REASON_DEAUTH_LEAVING, false); 23462306a36Sopenharmony_ci fallthrough; 23562306a36Sopenharmony_ci case CFG80211_CONN_ABANDON: 23662306a36Sopenharmony_ci /* free directly, disconnected event already sent */ 23762306a36Sopenharmony_ci cfg80211_sme_free(wdev); 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci default: 24062306a36Sopenharmony_ci return 0; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_civoid cfg80211_conn_work(struct work_struct *work) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = 24762306a36Sopenharmony_ci container_of(work, struct cfg80211_registered_device, conn_work); 24862306a36Sopenharmony_ci struct wireless_dev *wdev; 24962306a36Sopenharmony_ci u8 bssid_buf[ETH_ALEN], *bssid = NULL; 25062306a36Sopenharmony_ci enum nl80211_timeout_reason treason; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci wiphy_lock(&rdev->wiphy); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { 25562306a36Sopenharmony_ci if (!wdev->netdev) 25662306a36Sopenharmony_ci continue; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci wdev_lock(wdev); 25962306a36Sopenharmony_ci if (!netif_running(wdev->netdev)) { 26062306a36Sopenharmony_ci wdev_unlock(wdev); 26162306a36Sopenharmony_ci continue; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci if (!wdev->conn || 26462306a36Sopenharmony_ci wdev->conn->state == CFG80211_CONN_CONNECTED) { 26562306a36Sopenharmony_ci wdev_unlock(wdev); 26662306a36Sopenharmony_ci continue; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci if (wdev->conn->params.bssid) { 26962306a36Sopenharmony_ci memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN); 27062306a36Sopenharmony_ci bssid = bssid_buf; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci treason = NL80211_TIMEOUT_UNSPECIFIED; 27362306a36Sopenharmony_ci if (cfg80211_conn_do_work(wdev, &treason)) { 27462306a36Sopenharmony_ci struct cfg80211_connect_resp_params cr; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci memset(&cr, 0, sizeof(cr)); 27762306a36Sopenharmony_ci cr.status = -1; 27862306a36Sopenharmony_ci cr.links[0].bssid = bssid; 27962306a36Sopenharmony_ci cr.timeout_reason = treason; 28062306a36Sopenharmony_ci __cfg80211_connect_result(wdev->netdev, &cr, false); 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci wdev_unlock(wdev); 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci wiphy_unlock(&rdev->wiphy); 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic void cfg80211_step_auth_next(struct cfg80211_conn *conn, 28962306a36Sopenharmony_ci struct cfg80211_bss *bss) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci memcpy(conn->bssid, bss->bssid, ETH_ALEN); 29262306a36Sopenharmony_ci conn->params.bssid = conn->bssid; 29362306a36Sopenharmony_ci conn->params.channel = bss->channel; 29462306a36Sopenharmony_ci conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci/* Returned bss is reference counted and must be cleaned up appropriately. */ 29862306a36Sopenharmony_cistatic struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 30162306a36Sopenharmony_ci struct cfg80211_bss *bss; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci ASSERT_WDEV_LOCK(wdev); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel, 30662306a36Sopenharmony_ci wdev->conn->params.bssid, 30762306a36Sopenharmony_ci wdev->conn->params.ssid, 30862306a36Sopenharmony_ci wdev->conn->params.ssid_len, 30962306a36Sopenharmony_ci wdev->conn_bss_type, 31062306a36Sopenharmony_ci IEEE80211_PRIVACY(wdev->conn->params.privacy)); 31162306a36Sopenharmony_ci if (!bss) 31262306a36Sopenharmony_ci return NULL; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci cfg80211_step_auth_next(wdev->conn, bss); 31562306a36Sopenharmony_ci schedule_work(&rdev->conn_work); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return bss; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic void __cfg80211_sme_scan_done(struct net_device *dev) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 32362306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 32462306a36Sopenharmony_ci struct cfg80211_bss *bss; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci ASSERT_WDEV_LOCK(wdev); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (!wdev->conn) 32962306a36Sopenharmony_ci return; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (wdev->conn->state != CFG80211_CONN_SCANNING && 33262306a36Sopenharmony_ci wdev->conn->state != CFG80211_CONN_SCAN_AGAIN) 33362306a36Sopenharmony_ci return; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci bss = cfg80211_get_conn_bss(wdev); 33662306a36Sopenharmony_ci if (bss) 33762306a36Sopenharmony_ci cfg80211_put_bss(&rdev->wiphy, bss); 33862306a36Sopenharmony_ci else 33962306a36Sopenharmony_ci schedule_work(&rdev->conn_work); 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_civoid cfg80211_sme_scan_done(struct net_device *dev) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci wdev_lock(wdev); 34762306a36Sopenharmony_ci __cfg80211_sme_scan_done(dev); 34862306a36Sopenharmony_ci wdev_unlock(wdev); 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_civoid cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct wiphy *wiphy = wdev->wiphy; 35462306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); 35562306a36Sopenharmony_ci struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; 35662306a36Sopenharmony_ci u16 status_code = le16_to_cpu(mgmt->u.auth.status_code); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci ASSERT_WDEV_LOCK(wdev); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci if (!wdev->conn || wdev->conn->state == CFG80211_CONN_CONNECTED) 36162306a36Sopenharmony_ci return; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && 36462306a36Sopenharmony_ci wdev->conn->auto_auth && 36562306a36Sopenharmony_ci wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) { 36662306a36Sopenharmony_ci /* select automatically between only open, shared, leap */ 36762306a36Sopenharmony_ci switch (wdev->conn->params.auth_type) { 36862306a36Sopenharmony_ci case NL80211_AUTHTYPE_OPEN_SYSTEM: 36962306a36Sopenharmony_ci if (wdev->connect_keys) 37062306a36Sopenharmony_ci wdev->conn->params.auth_type = 37162306a36Sopenharmony_ci NL80211_AUTHTYPE_SHARED_KEY; 37262306a36Sopenharmony_ci else 37362306a36Sopenharmony_ci wdev->conn->params.auth_type = 37462306a36Sopenharmony_ci NL80211_AUTHTYPE_NETWORK_EAP; 37562306a36Sopenharmony_ci break; 37662306a36Sopenharmony_ci case NL80211_AUTHTYPE_SHARED_KEY: 37762306a36Sopenharmony_ci wdev->conn->params.auth_type = 37862306a36Sopenharmony_ci NL80211_AUTHTYPE_NETWORK_EAP; 37962306a36Sopenharmony_ci break; 38062306a36Sopenharmony_ci default: 38162306a36Sopenharmony_ci /* huh? */ 38262306a36Sopenharmony_ci wdev->conn->params.auth_type = 38362306a36Sopenharmony_ci NL80211_AUTHTYPE_OPEN_SYSTEM; 38462306a36Sopenharmony_ci break; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT; 38762306a36Sopenharmony_ci schedule_work(&rdev->conn_work); 38862306a36Sopenharmony_ci } else if (status_code != WLAN_STATUS_SUCCESS) { 38962306a36Sopenharmony_ci struct cfg80211_connect_resp_params cr; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci memset(&cr, 0, sizeof(cr)); 39262306a36Sopenharmony_ci cr.status = status_code; 39362306a36Sopenharmony_ci cr.links[0].bssid = mgmt->bssid; 39462306a36Sopenharmony_ci cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED; 39562306a36Sopenharmony_ci __cfg80211_connect_result(wdev->netdev, &cr, false); 39662306a36Sopenharmony_ci } else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) { 39762306a36Sopenharmony_ci wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; 39862306a36Sopenharmony_ci schedule_work(&rdev->conn_work); 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_cibool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status) 40362306a36Sopenharmony_ci{ 40462306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (!wdev->conn) 40762306a36Sopenharmony_ci return false; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (status == WLAN_STATUS_SUCCESS) { 41062306a36Sopenharmony_ci wdev->conn->state = CFG80211_CONN_CONNECTED; 41162306a36Sopenharmony_ci return false; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (wdev->conn->prev_bssid_valid) { 41562306a36Sopenharmony_ci /* 41662306a36Sopenharmony_ci * Some stupid APs don't accept reassoc, so we 41762306a36Sopenharmony_ci * need to fall back to trying regular assoc; 41862306a36Sopenharmony_ci * return true so no event is sent to userspace. 41962306a36Sopenharmony_ci */ 42062306a36Sopenharmony_ci wdev->conn->prev_bssid_valid = false; 42162306a36Sopenharmony_ci wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT; 42262306a36Sopenharmony_ci schedule_work(&rdev->conn_work); 42362306a36Sopenharmony_ci return true; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci wdev->conn->state = CFG80211_CONN_ASSOC_FAILED; 42762306a36Sopenharmony_ci schedule_work(&rdev->conn_work); 42862306a36Sopenharmony_ci return false; 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_civoid cfg80211_sme_deauth(struct wireless_dev *wdev) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci cfg80211_sme_free(wdev); 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_civoid cfg80211_sme_auth_timeout(struct wireless_dev *wdev) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (!wdev->conn) 44162306a36Sopenharmony_ci return; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci wdev->conn->state = CFG80211_CONN_AUTH_FAILED_TIMEOUT; 44462306a36Sopenharmony_ci schedule_work(&rdev->conn_work); 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_civoid cfg80211_sme_disassoc(struct wireless_dev *wdev) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (!wdev->conn) 45262306a36Sopenharmony_ci return; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci wdev->conn->state = CFG80211_CONN_DEAUTH; 45562306a36Sopenharmony_ci schedule_work(&rdev->conn_work); 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_civoid cfg80211_sme_assoc_timeout(struct wireless_dev *wdev) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (!wdev->conn) 46362306a36Sopenharmony_ci return; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci wdev->conn->state = CFG80211_CONN_ASSOC_FAILED_TIMEOUT; 46662306a36Sopenharmony_ci schedule_work(&rdev->conn_work); 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_civoid cfg80211_sme_abandon_assoc(struct wireless_dev *wdev) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (!wdev->conn) 47462306a36Sopenharmony_ci return; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci wdev->conn->state = CFG80211_CONN_ABANDON; 47762306a36Sopenharmony_ci schedule_work(&rdev->conn_work); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic void cfg80211_wdev_release_bsses(struct wireless_dev *wdev) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci unsigned int link; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci for_each_valid_link(wdev, link) { 48562306a36Sopenharmony_ci if (!wdev->links[link].client.current_bss) 48662306a36Sopenharmony_ci continue; 48762306a36Sopenharmony_ci cfg80211_unhold_bss(wdev->links[link].client.current_bss); 48862306a36Sopenharmony_ci cfg80211_put_bss(wdev->wiphy, 48962306a36Sopenharmony_ci &wdev->links[link].client.current_bss->pub); 49062306a36Sopenharmony_ci wdev->links[link].client.current_bss = NULL; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_civoid cfg80211_wdev_release_link_bsses(struct wireless_dev *wdev, u16 link_mask) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci unsigned int link; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci for_each_valid_link(wdev, link) { 49962306a36Sopenharmony_ci if (!wdev->links[link].client.current_bss || 50062306a36Sopenharmony_ci !(link_mask & BIT(link))) 50162306a36Sopenharmony_ci continue; 50262306a36Sopenharmony_ci cfg80211_unhold_bss(wdev->links[link].client.current_bss); 50362306a36Sopenharmony_ci cfg80211_put_bss(wdev->wiphy, 50462306a36Sopenharmony_ci &wdev->links[link].client.current_bss->pub); 50562306a36Sopenharmony_ci wdev->links[link].client.current_bss = NULL; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic int cfg80211_sme_get_conn_ies(struct wireless_dev *wdev, 51062306a36Sopenharmony_ci const u8 *ies, size_t ies_len, 51162306a36Sopenharmony_ci const u8 **out_ies, size_t *out_ies_len) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 51462306a36Sopenharmony_ci u8 *buf; 51562306a36Sopenharmony_ci size_t offs; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci if (!rdev->wiphy.extended_capabilities_len || 51862306a36Sopenharmony_ci (ies && cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, ies, ies_len))) { 51962306a36Sopenharmony_ci *out_ies = kmemdup(ies, ies_len, GFP_KERNEL); 52062306a36Sopenharmony_ci if (!*out_ies) 52162306a36Sopenharmony_ci return -ENOMEM; 52262306a36Sopenharmony_ci *out_ies_len = ies_len; 52362306a36Sopenharmony_ci return 0; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci buf = kmalloc(ies_len + rdev->wiphy.extended_capabilities_len + 2, 52762306a36Sopenharmony_ci GFP_KERNEL); 52862306a36Sopenharmony_ci if (!buf) 52962306a36Sopenharmony_ci return -ENOMEM; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (ies_len) { 53262306a36Sopenharmony_ci static const u8 before_extcapa[] = { 53362306a36Sopenharmony_ci /* not listing IEs expected to be created by driver */ 53462306a36Sopenharmony_ci WLAN_EID_RSN, 53562306a36Sopenharmony_ci WLAN_EID_QOS_CAPA, 53662306a36Sopenharmony_ci WLAN_EID_RRM_ENABLED_CAPABILITIES, 53762306a36Sopenharmony_ci WLAN_EID_MOBILITY_DOMAIN, 53862306a36Sopenharmony_ci WLAN_EID_SUPPORTED_REGULATORY_CLASSES, 53962306a36Sopenharmony_ci WLAN_EID_BSS_COEX_2040, 54062306a36Sopenharmony_ci }; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci offs = ieee80211_ie_split(ies, ies_len, before_extcapa, 54362306a36Sopenharmony_ci ARRAY_SIZE(before_extcapa), 0); 54462306a36Sopenharmony_ci memcpy(buf, ies, offs); 54562306a36Sopenharmony_ci /* leave a whole for extended capabilities IE */ 54662306a36Sopenharmony_ci memcpy(buf + offs + rdev->wiphy.extended_capabilities_len + 2, 54762306a36Sopenharmony_ci ies + offs, ies_len - offs); 54862306a36Sopenharmony_ci } else { 54962306a36Sopenharmony_ci offs = 0; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci /* place extended capabilities IE (with only driver capabilities) */ 55362306a36Sopenharmony_ci buf[offs] = WLAN_EID_EXT_CAPABILITY; 55462306a36Sopenharmony_ci buf[offs + 1] = rdev->wiphy.extended_capabilities_len; 55562306a36Sopenharmony_ci memcpy(buf + offs + 2, 55662306a36Sopenharmony_ci rdev->wiphy.extended_capabilities, 55762306a36Sopenharmony_ci rdev->wiphy.extended_capabilities_len); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci *out_ies = buf; 56062306a36Sopenharmony_ci *out_ies_len = ies_len + rdev->wiphy.extended_capabilities_len + 2; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci return 0; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic int cfg80211_sme_connect(struct wireless_dev *wdev, 56662306a36Sopenharmony_ci struct cfg80211_connect_params *connect, 56762306a36Sopenharmony_ci const u8 *prev_bssid) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 57062306a36Sopenharmony_ci struct cfg80211_bss *bss; 57162306a36Sopenharmony_ci int err; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (!rdev->ops->auth || !rdev->ops->assoc) 57462306a36Sopenharmony_ci return -EOPNOTSUPP; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci cfg80211_wdev_release_bsses(wdev); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (wdev->connected) { 57962306a36Sopenharmony_ci cfg80211_sme_free(wdev); 58062306a36Sopenharmony_ci wdev->connected = false; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (wdev->conn) 58462306a36Sopenharmony_ci return -EINPROGRESS; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL); 58762306a36Sopenharmony_ci if (!wdev->conn) 58862306a36Sopenharmony_ci return -ENOMEM; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci /* 59162306a36Sopenharmony_ci * Copy all parameters, and treat explicitly IEs, BSSID, SSID. 59262306a36Sopenharmony_ci */ 59362306a36Sopenharmony_ci memcpy(&wdev->conn->params, connect, sizeof(*connect)); 59462306a36Sopenharmony_ci if (connect->bssid) { 59562306a36Sopenharmony_ci wdev->conn->params.bssid = wdev->conn->bssid; 59662306a36Sopenharmony_ci memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN); 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (cfg80211_sme_get_conn_ies(wdev, connect->ie, connect->ie_len, 60062306a36Sopenharmony_ci &wdev->conn->ie, 60162306a36Sopenharmony_ci &wdev->conn->params.ie_len)) { 60262306a36Sopenharmony_ci kfree(wdev->conn); 60362306a36Sopenharmony_ci wdev->conn = NULL; 60462306a36Sopenharmony_ci return -ENOMEM; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci wdev->conn->params.ie = wdev->conn->ie; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { 60962306a36Sopenharmony_ci wdev->conn->auto_auth = true; 61062306a36Sopenharmony_ci /* start with open system ... should mostly work */ 61162306a36Sopenharmony_ci wdev->conn->params.auth_type = 61262306a36Sopenharmony_ci NL80211_AUTHTYPE_OPEN_SYSTEM; 61362306a36Sopenharmony_ci } else { 61462306a36Sopenharmony_ci wdev->conn->auto_auth = false; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci wdev->conn->params.ssid = wdev->u.client.ssid; 61862306a36Sopenharmony_ci wdev->conn->params.ssid_len = wdev->u.client.ssid_len; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* see if we have the bss already */ 62162306a36Sopenharmony_ci bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel, 62262306a36Sopenharmony_ci wdev->conn->params.bssid, 62362306a36Sopenharmony_ci wdev->conn->params.ssid, 62462306a36Sopenharmony_ci wdev->conn->params.ssid_len, 62562306a36Sopenharmony_ci wdev->conn_bss_type, 62662306a36Sopenharmony_ci IEEE80211_PRIVACY(wdev->conn->params.privacy)); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (prev_bssid) { 62962306a36Sopenharmony_ci memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN); 63062306a36Sopenharmony_ci wdev->conn->prev_bssid_valid = true; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* we're good if we have a matching bss struct */ 63462306a36Sopenharmony_ci if (bss) { 63562306a36Sopenharmony_ci enum nl80211_timeout_reason treason; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci cfg80211_step_auth_next(wdev->conn, bss); 63862306a36Sopenharmony_ci err = cfg80211_conn_do_work(wdev, &treason); 63962306a36Sopenharmony_ci cfg80211_put_bss(wdev->wiphy, bss); 64062306a36Sopenharmony_ci } else { 64162306a36Sopenharmony_ci /* otherwise we'll need to scan for the AP first */ 64262306a36Sopenharmony_ci err = cfg80211_conn_scan(wdev); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci /* 64562306a36Sopenharmony_ci * If we can't scan right now, then we need to scan again 64662306a36Sopenharmony_ci * after the current scan finished, since the parameters 64762306a36Sopenharmony_ci * changed (unless we find a good AP anyway). 64862306a36Sopenharmony_ci */ 64962306a36Sopenharmony_ci if (err == -EBUSY) { 65062306a36Sopenharmony_ci err = 0; 65162306a36Sopenharmony_ci wdev->conn->state = CFG80211_CONN_SCAN_AGAIN; 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (err) 65662306a36Sopenharmony_ci cfg80211_sme_free(wdev); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci return err; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic int cfg80211_sme_disconnect(struct wireless_dev *wdev, u16 reason) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 66462306a36Sopenharmony_ci int err; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci if (!wdev->conn) 66762306a36Sopenharmony_ci return 0; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if (!rdev->ops->deauth) 67062306a36Sopenharmony_ci return -EOPNOTSUPP; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci if (wdev->conn->state == CFG80211_CONN_SCANNING || 67362306a36Sopenharmony_ci wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) { 67462306a36Sopenharmony_ci err = 0; 67562306a36Sopenharmony_ci goto out; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci /* wdev->conn->params.bssid must be set if > SCANNING */ 67962306a36Sopenharmony_ci err = cfg80211_mlme_deauth(rdev, wdev->netdev, 68062306a36Sopenharmony_ci wdev->conn->params.bssid, 68162306a36Sopenharmony_ci NULL, 0, reason, false); 68262306a36Sopenharmony_ci out: 68362306a36Sopenharmony_ci cfg80211_sme_free(wdev); 68462306a36Sopenharmony_ci return err; 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci/* 68862306a36Sopenharmony_ci * code shared for in-device and software SME 68962306a36Sopenharmony_ci */ 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic bool cfg80211_is_all_idle(void) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci struct cfg80211_registered_device *rdev; 69462306a36Sopenharmony_ci struct wireless_dev *wdev; 69562306a36Sopenharmony_ci bool is_all_idle = true; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* 69862306a36Sopenharmony_ci * All devices must be idle as otherwise if you are actively 69962306a36Sopenharmony_ci * scanning some new beacon hints could be learned and would 70062306a36Sopenharmony_ci * count as new regulatory hints. 70162306a36Sopenharmony_ci * Also if there is any other active beaconing interface we 70262306a36Sopenharmony_ci * need not issue a disconnect hint and reset any info such 70362306a36Sopenharmony_ci * as chan dfs state, etc. 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_ci list_for_each_entry(rdev, &cfg80211_rdev_list, list) { 70662306a36Sopenharmony_ci list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { 70762306a36Sopenharmony_ci wdev_lock(wdev); 70862306a36Sopenharmony_ci if (wdev->conn || wdev->connected || 70962306a36Sopenharmony_ci cfg80211_beaconing_iface_active(wdev)) 71062306a36Sopenharmony_ci is_all_idle = false; 71162306a36Sopenharmony_ci wdev_unlock(wdev); 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci return is_all_idle; 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_cistatic void disconnect_work(struct work_struct *work) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci rtnl_lock(); 72162306a36Sopenharmony_ci if (cfg80211_is_all_idle()) 72262306a36Sopenharmony_ci regulatory_hint_disconnect(); 72362306a36Sopenharmony_ci rtnl_unlock(); 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ciDECLARE_WORK(cfg80211_disconnect_work, disconnect_work); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cistatic void 72962306a36Sopenharmony_cicfg80211_connect_result_release_bsses(struct wireless_dev *wdev, 73062306a36Sopenharmony_ci struct cfg80211_connect_resp_params *cr) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci unsigned int link; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci for_each_valid_link(cr, link) { 73562306a36Sopenharmony_ci if (!cr->links[link].bss) 73662306a36Sopenharmony_ci continue; 73762306a36Sopenharmony_ci cfg80211_unhold_bss(bss_from_pub(cr->links[link].bss)); 73862306a36Sopenharmony_ci cfg80211_put_bss(wdev->wiphy, cr->links[link].bss); 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci/* 74362306a36Sopenharmony_ci * API calls for drivers implementing connect/disconnect and 74462306a36Sopenharmony_ci * SME event handling 74562306a36Sopenharmony_ci */ 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci/* This method must consume bss one way or another */ 74862306a36Sopenharmony_civoid __cfg80211_connect_result(struct net_device *dev, 74962306a36Sopenharmony_ci struct cfg80211_connect_resp_params *cr, 75062306a36Sopenharmony_ci bool wextev) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 75362306a36Sopenharmony_ci const struct element *country_elem = NULL; 75462306a36Sopenharmony_ci const struct element *ssid; 75562306a36Sopenharmony_ci const u8 *country_data; 75662306a36Sopenharmony_ci u8 country_datalen; 75762306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 75862306a36Sopenharmony_ci union iwreq_data wrqu; 75962306a36Sopenharmony_ci#endif 76062306a36Sopenharmony_ci unsigned int link; 76162306a36Sopenharmony_ci const u8 *connected_addr; 76262306a36Sopenharmony_ci bool bss_not_found = false; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci ASSERT_WDEV_LOCK(wdev); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && 76762306a36Sopenharmony_ci wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) 76862306a36Sopenharmony_ci goto out; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci if (cr->valid_links) { 77162306a36Sopenharmony_ci if (WARN_ON(!cr->ap_mld_addr)) 77262306a36Sopenharmony_ci goto out; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci for_each_valid_link(cr, link) { 77562306a36Sopenharmony_ci if (WARN_ON(!cr->links[link].addr)) 77662306a36Sopenharmony_ci goto out; 77762306a36Sopenharmony_ci } 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci if (WARN_ON(wdev->connect_keys)) 78062306a36Sopenharmony_ci goto out; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci wdev->unprot_beacon_reported = 0; 78462306a36Sopenharmony_ci nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev, cr, 78562306a36Sopenharmony_ci GFP_KERNEL); 78662306a36Sopenharmony_ci connected_addr = cr->valid_links ? cr->ap_mld_addr : cr->links[0].bssid; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 78962306a36Sopenharmony_ci if (wextev && !cr->valid_links) { 79062306a36Sopenharmony_ci if (cr->req_ie && cr->status == WLAN_STATUS_SUCCESS) { 79162306a36Sopenharmony_ci memset(&wrqu, 0, sizeof(wrqu)); 79262306a36Sopenharmony_ci wrqu.data.length = cr->req_ie_len; 79362306a36Sopenharmony_ci wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, 79462306a36Sopenharmony_ci cr->req_ie); 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci if (cr->resp_ie && cr->status == WLAN_STATUS_SUCCESS) { 79862306a36Sopenharmony_ci memset(&wrqu, 0, sizeof(wrqu)); 79962306a36Sopenharmony_ci wrqu.data.length = cr->resp_ie_len; 80062306a36Sopenharmony_ci wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, 80162306a36Sopenharmony_ci cr->resp_ie); 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci memset(&wrqu, 0, sizeof(wrqu)); 80562306a36Sopenharmony_ci wrqu.ap_addr.sa_family = ARPHRD_ETHER; 80662306a36Sopenharmony_ci if (connected_addr && cr->status == WLAN_STATUS_SUCCESS) { 80762306a36Sopenharmony_ci memcpy(wrqu.ap_addr.sa_data, connected_addr, ETH_ALEN); 80862306a36Sopenharmony_ci memcpy(wdev->wext.prev_bssid, connected_addr, ETH_ALEN); 80962306a36Sopenharmony_ci wdev->wext.prev_bssid_valid = true; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci#endif 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci if (cr->status == WLAN_STATUS_SUCCESS) { 81662306a36Sopenharmony_ci if (!wiphy_to_rdev(wdev->wiphy)->ops->connect) { 81762306a36Sopenharmony_ci for_each_valid_link(cr, link) { 81862306a36Sopenharmony_ci if (WARN_ON_ONCE(!cr->links[link].bss)) 81962306a36Sopenharmony_ci break; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci for_each_valid_link(cr, link) { 82462306a36Sopenharmony_ci /* don't do extra lookups for failures */ 82562306a36Sopenharmony_ci if (cr->links[link].status != WLAN_STATUS_SUCCESS) 82662306a36Sopenharmony_ci continue; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (cr->links[link].bss) 82962306a36Sopenharmony_ci continue; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci cr->links[link].bss = 83262306a36Sopenharmony_ci cfg80211_get_bss(wdev->wiphy, NULL, 83362306a36Sopenharmony_ci cr->links[link].bssid, 83462306a36Sopenharmony_ci wdev->u.client.ssid, 83562306a36Sopenharmony_ci wdev->u.client.ssid_len, 83662306a36Sopenharmony_ci wdev->conn_bss_type, 83762306a36Sopenharmony_ci IEEE80211_PRIVACY_ANY); 83862306a36Sopenharmony_ci if (!cr->links[link].bss) { 83962306a36Sopenharmony_ci bss_not_found = true; 84062306a36Sopenharmony_ci break; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci cfg80211_hold_bss(bss_from_pub(cr->links[link].bss)); 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci cfg80211_wdev_release_bsses(wdev); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (cr->status != WLAN_STATUS_SUCCESS) { 84962306a36Sopenharmony_ci kfree_sensitive(wdev->connect_keys); 85062306a36Sopenharmony_ci wdev->connect_keys = NULL; 85162306a36Sopenharmony_ci wdev->u.client.ssid_len = 0; 85262306a36Sopenharmony_ci wdev->conn_owner_nlportid = 0; 85362306a36Sopenharmony_ci cfg80211_connect_result_release_bsses(wdev, cr); 85462306a36Sopenharmony_ci cfg80211_sme_free(wdev); 85562306a36Sopenharmony_ci return; 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci if (WARN_ON(bss_not_found)) { 85962306a36Sopenharmony_ci cfg80211_connect_result_release_bsses(wdev, cr); 86062306a36Sopenharmony_ci return; 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci memset(wdev->links, 0, sizeof(wdev->links)); 86462306a36Sopenharmony_ci for_each_valid_link(cr, link) { 86562306a36Sopenharmony_ci if (cr->links[link].status == WLAN_STATUS_SUCCESS) 86662306a36Sopenharmony_ci continue; 86762306a36Sopenharmony_ci cr->valid_links &= ~BIT(link); 86862306a36Sopenharmony_ci /* don't require bss pointer for failed links */ 86962306a36Sopenharmony_ci if (!cr->links[link].bss) 87062306a36Sopenharmony_ci continue; 87162306a36Sopenharmony_ci cfg80211_unhold_bss(bss_from_pub(cr->links[link].bss)); 87262306a36Sopenharmony_ci cfg80211_put_bss(wdev->wiphy, cr->links[link].bss); 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci wdev->valid_links = cr->valid_links; 87562306a36Sopenharmony_ci for_each_valid_link(cr, link) 87662306a36Sopenharmony_ci wdev->links[link].client.current_bss = 87762306a36Sopenharmony_ci bss_from_pub(cr->links[link].bss); 87862306a36Sopenharmony_ci wdev->connected = true; 87962306a36Sopenharmony_ci ether_addr_copy(wdev->u.client.connected_addr, connected_addr); 88062306a36Sopenharmony_ci if (cr->valid_links) { 88162306a36Sopenharmony_ci for_each_valid_link(cr, link) 88262306a36Sopenharmony_ci memcpy(wdev->links[link].addr, cr->links[link].addr, 88362306a36Sopenharmony_ci ETH_ALEN); 88462306a36Sopenharmony_ci } 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci cfg80211_upload_connect_keys(wdev); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci rcu_read_lock(); 88962306a36Sopenharmony_ci for_each_valid_link(cr, link) { 89062306a36Sopenharmony_ci country_elem = 89162306a36Sopenharmony_ci ieee80211_bss_get_elem(cr->links[link].bss, 89262306a36Sopenharmony_ci WLAN_EID_COUNTRY); 89362306a36Sopenharmony_ci if (country_elem) 89462306a36Sopenharmony_ci break; 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci if (!country_elem) { 89762306a36Sopenharmony_ci rcu_read_unlock(); 89862306a36Sopenharmony_ci return; 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci country_datalen = country_elem->datalen; 90262306a36Sopenharmony_ci country_data = kmemdup(country_elem->data, country_datalen, GFP_ATOMIC); 90362306a36Sopenharmony_ci rcu_read_unlock(); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci if (!country_data) 90662306a36Sopenharmony_ci return; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci regulatory_hint_country_ie(wdev->wiphy, 90962306a36Sopenharmony_ci cr->links[link].bss->channel->band, 91062306a36Sopenharmony_ci country_data, country_datalen); 91162306a36Sopenharmony_ci kfree(country_data); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci if (!wdev->u.client.ssid_len) { 91462306a36Sopenharmony_ci rcu_read_lock(); 91562306a36Sopenharmony_ci for_each_valid_link(cr, link) { 91662306a36Sopenharmony_ci ssid = ieee80211_bss_get_elem(cr->links[link].bss, 91762306a36Sopenharmony_ci WLAN_EID_SSID); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci if (!ssid || !ssid->datalen) 92062306a36Sopenharmony_ci continue; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci memcpy(wdev->u.client.ssid, ssid->data, ssid->datalen); 92362306a36Sopenharmony_ci wdev->u.client.ssid_len = ssid->datalen; 92462306a36Sopenharmony_ci break; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci rcu_read_unlock(); 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci return; 93062306a36Sopenharmony_ciout: 93162306a36Sopenharmony_ci for_each_valid_link(cr, link) 93262306a36Sopenharmony_ci cfg80211_put_bss(wdev->wiphy, cr->links[link].bss); 93362306a36Sopenharmony_ci} 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_cistatic void cfg80211_update_link_bss(struct wireless_dev *wdev, 93662306a36Sopenharmony_ci struct cfg80211_bss **bss) 93762306a36Sopenharmony_ci{ 93862306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 93962306a36Sopenharmony_ci struct cfg80211_internal_bss *ibss; 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci if (!*bss) 94262306a36Sopenharmony_ci return; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci ibss = bss_from_pub(*bss); 94562306a36Sopenharmony_ci if (list_empty(&ibss->list)) { 94662306a36Sopenharmony_ci struct cfg80211_bss *found = NULL, *tmp = *bss; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci found = cfg80211_get_bss(wdev->wiphy, NULL, 94962306a36Sopenharmony_ci (*bss)->bssid, 95062306a36Sopenharmony_ci wdev->u.client.ssid, 95162306a36Sopenharmony_ci wdev->u.client.ssid_len, 95262306a36Sopenharmony_ci wdev->conn_bss_type, 95362306a36Sopenharmony_ci IEEE80211_PRIVACY_ANY); 95462306a36Sopenharmony_ci if (found) { 95562306a36Sopenharmony_ci /* The same BSS is already updated so use it 95662306a36Sopenharmony_ci * instead, as it has latest info. 95762306a36Sopenharmony_ci */ 95862306a36Sopenharmony_ci *bss = found; 95962306a36Sopenharmony_ci } else { 96062306a36Sopenharmony_ci /* Update with BSS provided by driver, it will 96162306a36Sopenharmony_ci * be freshly added and ref cnted, we can free 96262306a36Sopenharmony_ci * the old one. 96362306a36Sopenharmony_ci * 96462306a36Sopenharmony_ci * signal_valid can be false, as we are not 96562306a36Sopenharmony_ci * expecting the BSS to be found. 96662306a36Sopenharmony_ci * 96762306a36Sopenharmony_ci * keep the old timestamp to avoid confusion 96862306a36Sopenharmony_ci */ 96962306a36Sopenharmony_ci cfg80211_bss_update(rdev, ibss, false, 97062306a36Sopenharmony_ci ibss->ts); 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci cfg80211_put_bss(wdev->wiphy, tmp); 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci/* Consumes bss object(s) one way or another */ 97862306a36Sopenharmony_civoid cfg80211_connect_done(struct net_device *dev, 97962306a36Sopenharmony_ci struct cfg80211_connect_resp_params *params, 98062306a36Sopenharmony_ci gfp_t gfp) 98162306a36Sopenharmony_ci{ 98262306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 98362306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 98462306a36Sopenharmony_ci struct cfg80211_event *ev; 98562306a36Sopenharmony_ci unsigned long flags; 98662306a36Sopenharmony_ci u8 *next; 98762306a36Sopenharmony_ci size_t link_info_size = 0; 98862306a36Sopenharmony_ci unsigned int link; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci for_each_valid_link(params, link) { 99162306a36Sopenharmony_ci cfg80211_update_link_bss(wdev, ¶ms->links[link].bss); 99262306a36Sopenharmony_ci link_info_size += params->links[link].bssid ? ETH_ALEN : 0; 99362306a36Sopenharmony_ci link_info_size += params->links[link].addr ? ETH_ALEN : 0; 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci ev = kzalloc(sizeof(*ev) + (params->ap_mld_addr ? ETH_ALEN : 0) + 99762306a36Sopenharmony_ci params->req_ie_len + params->resp_ie_len + 99862306a36Sopenharmony_ci params->fils.kek_len + params->fils.pmk_len + 99962306a36Sopenharmony_ci (params->fils.pmkid ? WLAN_PMKID_LEN : 0) + link_info_size, 100062306a36Sopenharmony_ci gfp); 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci if (!ev) { 100362306a36Sopenharmony_ci for_each_valid_link(params, link) 100462306a36Sopenharmony_ci cfg80211_put_bss(wdev->wiphy, 100562306a36Sopenharmony_ci params->links[link].bss); 100662306a36Sopenharmony_ci return; 100762306a36Sopenharmony_ci } 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci ev->type = EVENT_CONNECT_RESULT; 101062306a36Sopenharmony_ci next = ((u8 *)ev) + sizeof(*ev); 101162306a36Sopenharmony_ci if (params->ap_mld_addr) { 101262306a36Sopenharmony_ci ev->cr.ap_mld_addr = next; 101362306a36Sopenharmony_ci memcpy((void *)ev->cr.ap_mld_addr, params->ap_mld_addr, 101462306a36Sopenharmony_ci ETH_ALEN); 101562306a36Sopenharmony_ci next += ETH_ALEN; 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci if (params->req_ie_len) { 101862306a36Sopenharmony_ci ev->cr.req_ie = next; 101962306a36Sopenharmony_ci ev->cr.req_ie_len = params->req_ie_len; 102062306a36Sopenharmony_ci memcpy((void *)ev->cr.req_ie, params->req_ie, 102162306a36Sopenharmony_ci params->req_ie_len); 102262306a36Sopenharmony_ci next += params->req_ie_len; 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci if (params->resp_ie_len) { 102562306a36Sopenharmony_ci ev->cr.resp_ie = next; 102662306a36Sopenharmony_ci ev->cr.resp_ie_len = params->resp_ie_len; 102762306a36Sopenharmony_ci memcpy((void *)ev->cr.resp_ie, params->resp_ie, 102862306a36Sopenharmony_ci params->resp_ie_len); 102962306a36Sopenharmony_ci next += params->resp_ie_len; 103062306a36Sopenharmony_ci } 103162306a36Sopenharmony_ci if (params->fils.kek_len) { 103262306a36Sopenharmony_ci ev->cr.fils.kek = next; 103362306a36Sopenharmony_ci ev->cr.fils.kek_len = params->fils.kek_len; 103462306a36Sopenharmony_ci memcpy((void *)ev->cr.fils.kek, params->fils.kek, 103562306a36Sopenharmony_ci params->fils.kek_len); 103662306a36Sopenharmony_ci next += params->fils.kek_len; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci if (params->fils.pmk_len) { 103962306a36Sopenharmony_ci ev->cr.fils.pmk = next; 104062306a36Sopenharmony_ci ev->cr.fils.pmk_len = params->fils.pmk_len; 104162306a36Sopenharmony_ci memcpy((void *)ev->cr.fils.pmk, params->fils.pmk, 104262306a36Sopenharmony_ci params->fils.pmk_len); 104362306a36Sopenharmony_ci next += params->fils.pmk_len; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci if (params->fils.pmkid) { 104662306a36Sopenharmony_ci ev->cr.fils.pmkid = next; 104762306a36Sopenharmony_ci memcpy((void *)ev->cr.fils.pmkid, params->fils.pmkid, 104862306a36Sopenharmony_ci WLAN_PMKID_LEN); 104962306a36Sopenharmony_ci next += WLAN_PMKID_LEN; 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci ev->cr.fils.update_erp_next_seq_num = params->fils.update_erp_next_seq_num; 105262306a36Sopenharmony_ci if (params->fils.update_erp_next_seq_num) 105362306a36Sopenharmony_ci ev->cr.fils.erp_next_seq_num = params->fils.erp_next_seq_num; 105462306a36Sopenharmony_ci ev->cr.valid_links = params->valid_links; 105562306a36Sopenharmony_ci for_each_valid_link(params, link) { 105662306a36Sopenharmony_ci if (params->links[link].bss) 105762306a36Sopenharmony_ci cfg80211_hold_bss( 105862306a36Sopenharmony_ci bss_from_pub(params->links[link].bss)); 105962306a36Sopenharmony_ci ev->cr.links[link].bss = params->links[link].bss; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci if (params->links[link].addr) { 106262306a36Sopenharmony_ci ev->cr.links[link].addr = next; 106362306a36Sopenharmony_ci memcpy((void *)ev->cr.links[link].addr, 106462306a36Sopenharmony_ci params->links[link].addr, 106562306a36Sopenharmony_ci ETH_ALEN); 106662306a36Sopenharmony_ci next += ETH_ALEN; 106762306a36Sopenharmony_ci } 106862306a36Sopenharmony_ci if (params->links[link].bssid) { 106962306a36Sopenharmony_ci ev->cr.links[link].bssid = next; 107062306a36Sopenharmony_ci memcpy((void *)ev->cr.links[link].bssid, 107162306a36Sopenharmony_ci params->links[link].bssid, 107262306a36Sopenharmony_ci ETH_ALEN); 107362306a36Sopenharmony_ci next += ETH_ALEN; 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci ev->cr.status = params->status; 107762306a36Sopenharmony_ci ev->cr.timeout_reason = params->timeout_reason; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci spin_lock_irqsave(&wdev->event_lock, flags); 108062306a36Sopenharmony_ci list_add_tail(&ev->list, &wdev->event_list); 108162306a36Sopenharmony_ci spin_unlock_irqrestore(&wdev->event_lock, flags); 108262306a36Sopenharmony_ci queue_work(cfg80211_wq, &rdev->event_work); 108362306a36Sopenharmony_ci} 108462306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_connect_done); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci/* Consumes bss object one way or another */ 108762306a36Sopenharmony_civoid __cfg80211_roamed(struct wireless_dev *wdev, 108862306a36Sopenharmony_ci struct cfg80211_roam_info *info) 108962306a36Sopenharmony_ci{ 109062306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 109162306a36Sopenharmony_ci union iwreq_data wrqu; 109262306a36Sopenharmony_ci#endif 109362306a36Sopenharmony_ci unsigned int link; 109462306a36Sopenharmony_ci const u8 *connected_addr; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci ASSERT_WDEV_LOCK(wdev); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && 109962306a36Sopenharmony_ci wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) 110062306a36Sopenharmony_ci goto out; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci if (WARN_ON(!wdev->connected)) 110362306a36Sopenharmony_ci goto out; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci if (info->valid_links) { 110662306a36Sopenharmony_ci if (WARN_ON(!info->ap_mld_addr)) 110762306a36Sopenharmony_ci goto out; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci for_each_valid_link(info, link) { 111062306a36Sopenharmony_ci if (WARN_ON(!info->links[link].addr)) 111162306a36Sopenharmony_ci goto out; 111262306a36Sopenharmony_ci } 111362306a36Sopenharmony_ci } 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci cfg80211_wdev_release_bsses(wdev); 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci for_each_valid_link(info, link) { 111862306a36Sopenharmony_ci if (WARN_ON(!info->links[link].bss)) 111962306a36Sopenharmony_ci goto out; 112062306a36Sopenharmony_ci } 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci memset(wdev->links, 0, sizeof(wdev->links)); 112362306a36Sopenharmony_ci wdev->valid_links = info->valid_links; 112462306a36Sopenharmony_ci for_each_valid_link(info, link) { 112562306a36Sopenharmony_ci cfg80211_hold_bss(bss_from_pub(info->links[link].bss)); 112662306a36Sopenharmony_ci wdev->links[link].client.current_bss = 112762306a36Sopenharmony_ci bss_from_pub(info->links[link].bss); 112862306a36Sopenharmony_ci } 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci connected_addr = info->valid_links ? 113162306a36Sopenharmony_ci info->ap_mld_addr : 113262306a36Sopenharmony_ci info->links[0].bss->bssid; 113362306a36Sopenharmony_ci ether_addr_copy(wdev->u.client.connected_addr, connected_addr); 113462306a36Sopenharmony_ci if (info->valid_links) { 113562306a36Sopenharmony_ci for_each_valid_link(info, link) 113662306a36Sopenharmony_ci memcpy(wdev->links[link].addr, info->links[link].addr, 113762306a36Sopenharmony_ci ETH_ALEN); 113862306a36Sopenharmony_ci } 113962306a36Sopenharmony_ci wdev->unprot_beacon_reported = 0; 114062306a36Sopenharmony_ci nl80211_send_roamed(wiphy_to_rdev(wdev->wiphy), 114162306a36Sopenharmony_ci wdev->netdev, info, GFP_KERNEL); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 114462306a36Sopenharmony_ci if (!info->valid_links) { 114562306a36Sopenharmony_ci if (info->req_ie) { 114662306a36Sopenharmony_ci memset(&wrqu, 0, sizeof(wrqu)); 114762306a36Sopenharmony_ci wrqu.data.length = info->req_ie_len; 114862306a36Sopenharmony_ci wireless_send_event(wdev->netdev, IWEVASSOCREQIE, 114962306a36Sopenharmony_ci &wrqu, info->req_ie); 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci if (info->resp_ie) { 115362306a36Sopenharmony_ci memset(&wrqu, 0, sizeof(wrqu)); 115462306a36Sopenharmony_ci wrqu.data.length = info->resp_ie_len; 115562306a36Sopenharmony_ci wireless_send_event(wdev->netdev, IWEVASSOCRESPIE, 115662306a36Sopenharmony_ci &wrqu, info->resp_ie); 115762306a36Sopenharmony_ci } 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci memset(&wrqu, 0, sizeof(wrqu)); 116062306a36Sopenharmony_ci wrqu.ap_addr.sa_family = ARPHRD_ETHER; 116162306a36Sopenharmony_ci memcpy(wrqu.ap_addr.sa_data, connected_addr, ETH_ALEN); 116262306a36Sopenharmony_ci memcpy(wdev->wext.prev_bssid, connected_addr, ETH_ALEN); 116362306a36Sopenharmony_ci wdev->wext.prev_bssid_valid = true; 116462306a36Sopenharmony_ci wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL); 116562306a36Sopenharmony_ci } 116662306a36Sopenharmony_ci#endif 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci return; 116962306a36Sopenharmony_ciout: 117062306a36Sopenharmony_ci for_each_valid_link(info, link) 117162306a36Sopenharmony_ci cfg80211_put_bss(wdev->wiphy, info->links[link].bss); 117262306a36Sopenharmony_ci} 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci/* Consumes info->links.bss object(s) one way or another */ 117562306a36Sopenharmony_civoid cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, 117662306a36Sopenharmony_ci gfp_t gfp) 117762306a36Sopenharmony_ci{ 117862306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 117962306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 118062306a36Sopenharmony_ci struct cfg80211_event *ev; 118162306a36Sopenharmony_ci unsigned long flags; 118262306a36Sopenharmony_ci u8 *next; 118362306a36Sopenharmony_ci unsigned int link; 118462306a36Sopenharmony_ci size_t link_info_size = 0; 118562306a36Sopenharmony_ci bool bss_not_found = false; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci for_each_valid_link(info, link) { 118862306a36Sopenharmony_ci link_info_size += info->links[link].addr ? ETH_ALEN : 0; 118962306a36Sopenharmony_ci link_info_size += info->links[link].bssid ? ETH_ALEN : 0; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci if (info->links[link].bss) 119262306a36Sopenharmony_ci continue; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci info->links[link].bss = 119562306a36Sopenharmony_ci cfg80211_get_bss(wdev->wiphy, 119662306a36Sopenharmony_ci info->links[link].channel, 119762306a36Sopenharmony_ci info->links[link].bssid, 119862306a36Sopenharmony_ci wdev->u.client.ssid, 119962306a36Sopenharmony_ci wdev->u.client.ssid_len, 120062306a36Sopenharmony_ci wdev->conn_bss_type, 120162306a36Sopenharmony_ci IEEE80211_PRIVACY_ANY); 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_ci if (!info->links[link].bss) { 120462306a36Sopenharmony_ci bss_not_found = true; 120562306a36Sopenharmony_ci break; 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci } 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci if (WARN_ON(bss_not_found)) 121062306a36Sopenharmony_ci goto out; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci ev = kzalloc(sizeof(*ev) + info->req_ie_len + info->resp_ie_len + 121362306a36Sopenharmony_ci info->fils.kek_len + info->fils.pmk_len + 121462306a36Sopenharmony_ci (info->fils.pmkid ? WLAN_PMKID_LEN : 0) + 121562306a36Sopenharmony_ci (info->ap_mld_addr ? ETH_ALEN : 0) + link_info_size, gfp); 121662306a36Sopenharmony_ci if (!ev) 121762306a36Sopenharmony_ci goto out; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci ev->type = EVENT_ROAMED; 122062306a36Sopenharmony_ci next = ((u8 *)ev) + sizeof(*ev); 122162306a36Sopenharmony_ci if (info->req_ie_len) { 122262306a36Sopenharmony_ci ev->rm.req_ie = next; 122362306a36Sopenharmony_ci ev->rm.req_ie_len = info->req_ie_len; 122462306a36Sopenharmony_ci memcpy((void *)ev->rm.req_ie, info->req_ie, info->req_ie_len); 122562306a36Sopenharmony_ci next += info->req_ie_len; 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci if (info->resp_ie_len) { 122862306a36Sopenharmony_ci ev->rm.resp_ie = next; 122962306a36Sopenharmony_ci ev->rm.resp_ie_len = info->resp_ie_len; 123062306a36Sopenharmony_ci memcpy((void *)ev->rm.resp_ie, info->resp_ie, 123162306a36Sopenharmony_ci info->resp_ie_len); 123262306a36Sopenharmony_ci next += info->resp_ie_len; 123362306a36Sopenharmony_ci } 123462306a36Sopenharmony_ci if (info->fils.kek_len) { 123562306a36Sopenharmony_ci ev->rm.fils.kek = next; 123662306a36Sopenharmony_ci ev->rm.fils.kek_len = info->fils.kek_len; 123762306a36Sopenharmony_ci memcpy((void *)ev->rm.fils.kek, info->fils.kek, 123862306a36Sopenharmony_ci info->fils.kek_len); 123962306a36Sopenharmony_ci next += info->fils.kek_len; 124062306a36Sopenharmony_ci } 124162306a36Sopenharmony_ci if (info->fils.pmk_len) { 124262306a36Sopenharmony_ci ev->rm.fils.pmk = next; 124362306a36Sopenharmony_ci ev->rm.fils.pmk_len = info->fils.pmk_len; 124462306a36Sopenharmony_ci memcpy((void *)ev->rm.fils.pmk, info->fils.pmk, 124562306a36Sopenharmony_ci info->fils.pmk_len); 124662306a36Sopenharmony_ci next += info->fils.pmk_len; 124762306a36Sopenharmony_ci } 124862306a36Sopenharmony_ci if (info->fils.pmkid) { 124962306a36Sopenharmony_ci ev->rm.fils.pmkid = next; 125062306a36Sopenharmony_ci memcpy((void *)ev->rm.fils.pmkid, info->fils.pmkid, 125162306a36Sopenharmony_ci WLAN_PMKID_LEN); 125262306a36Sopenharmony_ci next += WLAN_PMKID_LEN; 125362306a36Sopenharmony_ci } 125462306a36Sopenharmony_ci ev->rm.fils.update_erp_next_seq_num = info->fils.update_erp_next_seq_num; 125562306a36Sopenharmony_ci if (info->fils.update_erp_next_seq_num) 125662306a36Sopenharmony_ci ev->rm.fils.erp_next_seq_num = info->fils.erp_next_seq_num; 125762306a36Sopenharmony_ci if (info->ap_mld_addr) { 125862306a36Sopenharmony_ci ev->rm.ap_mld_addr = next; 125962306a36Sopenharmony_ci memcpy((void *)ev->rm.ap_mld_addr, info->ap_mld_addr, 126062306a36Sopenharmony_ci ETH_ALEN); 126162306a36Sopenharmony_ci next += ETH_ALEN; 126262306a36Sopenharmony_ci } 126362306a36Sopenharmony_ci ev->rm.valid_links = info->valid_links; 126462306a36Sopenharmony_ci for_each_valid_link(info, link) { 126562306a36Sopenharmony_ci ev->rm.links[link].bss = info->links[link].bss; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci if (info->links[link].addr) { 126862306a36Sopenharmony_ci ev->rm.links[link].addr = next; 126962306a36Sopenharmony_ci memcpy((void *)ev->rm.links[link].addr, 127062306a36Sopenharmony_ci info->links[link].addr, 127162306a36Sopenharmony_ci ETH_ALEN); 127262306a36Sopenharmony_ci next += ETH_ALEN; 127362306a36Sopenharmony_ci } 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci if (info->links[link].bssid) { 127662306a36Sopenharmony_ci ev->rm.links[link].bssid = next; 127762306a36Sopenharmony_ci memcpy((void *)ev->rm.links[link].bssid, 127862306a36Sopenharmony_ci info->links[link].bssid, 127962306a36Sopenharmony_ci ETH_ALEN); 128062306a36Sopenharmony_ci next += ETH_ALEN; 128162306a36Sopenharmony_ci } 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci spin_lock_irqsave(&wdev->event_lock, flags); 128562306a36Sopenharmony_ci list_add_tail(&ev->list, &wdev->event_list); 128662306a36Sopenharmony_ci spin_unlock_irqrestore(&wdev->event_lock, flags); 128762306a36Sopenharmony_ci queue_work(cfg80211_wq, &rdev->event_work); 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci return; 129062306a36Sopenharmony_ciout: 129162306a36Sopenharmony_ci for_each_valid_link(info, link) 129262306a36Sopenharmony_ci cfg80211_put_bss(wdev->wiphy, info->links[link].bss); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci} 129562306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_roamed); 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_civoid __cfg80211_port_authorized(struct wireless_dev *wdev, const u8 *bssid, 129862306a36Sopenharmony_ci const u8 *td_bitmap, u8 td_bitmap_len) 129962306a36Sopenharmony_ci{ 130062306a36Sopenharmony_ci ASSERT_WDEV_LOCK(wdev); 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && 130362306a36Sopenharmony_ci wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) 130462306a36Sopenharmony_ci return; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci if (WARN_ON(!wdev->connected) || 130762306a36Sopenharmony_ci WARN_ON(!ether_addr_equal(wdev->u.client.connected_addr, bssid))) 130862306a36Sopenharmony_ci return; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci nl80211_send_port_authorized(wiphy_to_rdev(wdev->wiphy), wdev->netdev, 131162306a36Sopenharmony_ci bssid, td_bitmap, td_bitmap_len); 131262306a36Sopenharmony_ci} 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_civoid cfg80211_port_authorized(struct net_device *dev, const u8 *bssid, 131562306a36Sopenharmony_ci const u8 *td_bitmap, u8 td_bitmap_len, gfp_t gfp) 131662306a36Sopenharmony_ci{ 131762306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 131862306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 131962306a36Sopenharmony_ci struct cfg80211_event *ev; 132062306a36Sopenharmony_ci unsigned long flags; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci if (WARN_ON(!bssid)) 132362306a36Sopenharmony_ci return; 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci ev = kzalloc(sizeof(*ev) + td_bitmap_len, gfp); 132662306a36Sopenharmony_ci if (!ev) 132762306a36Sopenharmony_ci return; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci ev->type = EVENT_PORT_AUTHORIZED; 133062306a36Sopenharmony_ci memcpy(ev->pa.bssid, bssid, ETH_ALEN); 133162306a36Sopenharmony_ci ev->pa.td_bitmap = ((u8 *)ev) + sizeof(*ev); 133262306a36Sopenharmony_ci ev->pa.td_bitmap_len = td_bitmap_len; 133362306a36Sopenharmony_ci memcpy((void *)ev->pa.td_bitmap, td_bitmap, td_bitmap_len); 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci /* 133662306a36Sopenharmony_ci * Use the wdev event list so that if there are pending 133762306a36Sopenharmony_ci * connected/roamed events, they will be reported first. 133862306a36Sopenharmony_ci */ 133962306a36Sopenharmony_ci spin_lock_irqsave(&wdev->event_lock, flags); 134062306a36Sopenharmony_ci list_add_tail(&ev->list, &wdev->event_list); 134162306a36Sopenharmony_ci spin_unlock_irqrestore(&wdev->event_lock, flags); 134262306a36Sopenharmony_ci queue_work(cfg80211_wq, &rdev->event_work); 134362306a36Sopenharmony_ci} 134462306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_port_authorized); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_civoid __cfg80211_disconnected(struct net_device *dev, const u8 *ie, 134762306a36Sopenharmony_ci size_t ie_len, u16 reason, bool from_ap) 134862306a36Sopenharmony_ci{ 134962306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 135062306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 135162306a36Sopenharmony_ci int i; 135262306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 135362306a36Sopenharmony_ci union iwreq_data wrqu; 135462306a36Sopenharmony_ci#endif 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci ASSERT_WDEV_LOCK(wdev); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && 135962306a36Sopenharmony_ci wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) 136062306a36Sopenharmony_ci return; 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci cfg80211_wdev_release_bsses(wdev); 136362306a36Sopenharmony_ci wdev->connected = false; 136462306a36Sopenharmony_ci wdev->u.client.ssid_len = 0; 136562306a36Sopenharmony_ci wdev->conn_owner_nlportid = 0; 136662306a36Sopenharmony_ci kfree_sensitive(wdev->connect_keys); 136762306a36Sopenharmony_ci wdev->connect_keys = NULL; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap); 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci /* stop critical protocol if supported */ 137262306a36Sopenharmony_ci if (rdev->ops->crit_proto_stop && rdev->crit_proto_nlportid) { 137362306a36Sopenharmony_ci rdev->crit_proto_nlportid = 0; 137462306a36Sopenharmony_ci rdev_crit_proto_stop(rdev, wdev); 137562306a36Sopenharmony_ci } 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci /* 137862306a36Sopenharmony_ci * Delete all the keys ... pairwise keys can't really 137962306a36Sopenharmony_ci * exist any more anyway, but default keys might. 138062306a36Sopenharmony_ci */ 138162306a36Sopenharmony_ci if (rdev->ops->del_key) { 138262306a36Sopenharmony_ci int max_key_idx = 5; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci if (wiphy_ext_feature_isset( 138562306a36Sopenharmony_ci wdev->wiphy, 138662306a36Sopenharmony_ci NL80211_EXT_FEATURE_BEACON_PROTECTION) || 138762306a36Sopenharmony_ci wiphy_ext_feature_isset( 138862306a36Sopenharmony_ci wdev->wiphy, 138962306a36Sopenharmony_ci NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT)) 139062306a36Sopenharmony_ci max_key_idx = 7; 139162306a36Sopenharmony_ci for (i = 0; i <= max_key_idx; i++) 139262306a36Sopenharmony_ci rdev_del_key(rdev, dev, -1, i, false, NULL); 139362306a36Sopenharmony_ci } 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci rdev_set_qos_map(rdev, dev, NULL); 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci#ifdef CONFIG_CFG80211_WEXT 139862306a36Sopenharmony_ci memset(&wrqu, 0, sizeof(wrqu)); 139962306a36Sopenharmony_ci wrqu.ap_addr.sa_family = ARPHRD_ETHER; 140062306a36Sopenharmony_ci wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); 140162306a36Sopenharmony_ci wdev->wext.connect.ssid_len = 0; 140262306a36Sopenharmony_ci#endif 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci schedule_work(&cfg80211_disconnect_work); 140562306a36Sopenharmony_ci} 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_civoid cfg80211_disconnected(struct net_device *dev, u16 reason, 140862306a36Sopenharmony_ci const u8 *ie, size_t ie_len, 140962306a36Sopenharmony_ci bool locally_generated, gfp_t gfp) 141062306a36Sopenharmony_ci{ 141162306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 141262306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 141362306a36Sopenharmony_ci struct cfg80211_event *ev; 141462306a36Sopenharmony_ci unsigned long flags; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci ev = kzalloc(sizeof(*ev) + ie_len, gfp); 141762306a36Sopenharmony_ci if (!ev) 141862306a36Sopenharmony_ci return; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci ev->type = EVENT_DISCONNECTED; 142162306a36Sopenharmony_ci ev->dc.ie = ((u8 *)ev) + sizeof(*ev); 142262306a36Sopenharmony_ci ev->dc.ie_len = ie_len; 142362306a36Sopenharmony_ci memcpy((void *)ev->dc.ie, ie, ie_len); 142462306a36Sopenharmony_ci ev->dc.reason = reason; 142562306a36Sopenharmony_ci ev->dc.locally_generated = locally_generated; 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci spin_lock_irqsave(&wdev->event_lock, flags); 142862306a36Sopenharmony_ci list_add_tail(&ev->list, &wdev->event_list); 142962306a36Sopenharmony_ci spin_unlock_irqrestore(&wdev->event_lock, flags); 143062306a36Sopenharmony_ci queue_work(cfg80211_wq, &rdev->event_work); 143162306a36Sopenharmony_ci} 143262306a36Sopenharmony_ciEXPORT_SYMBOL(cfg80211_disconnected); 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci/* 143562306a36Sopenharmony_ci * API calls for nl80211/wext compatibility code 143662306a36Sopenharmony_ci */ 143762306a36Sopenharmony_ciint cfg80211_connect(struct cfg80211_registered_device *rdev, 143862306a36Sopenharmony_ci struct net_device *dev, 143962306a36Sopenharmony_ci struct cfg80211_connect_params *connect, 144062306a36Sopenharmony_ci struct cfg80211_cached_keys *connkeys, 144162306a36Sopenharmony_ci const u8 *prev_bssid) 144262306a36Sopenharmony_ci{ 144362306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 144462306a36Sopenharmony_ci int err; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci ASSERT_WDEV_LOCK(wdev); 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci /* 144962306a36Sopenharmony_ci * If we have an ssid_len, we're trying to connect or are 145062306a36Sopenharmony_ci * already connected, so reject a new SSID unless it's the 145162306a36Sopenharmony_ci * same (which is the case for re-association.) 145262306a36Sopenharmony_ci */ 145362306a36Sopenharmony_ci if (wdev->u.client.ssid_len && 145462306a36Sopenharmony_ci (wdev->u.client.ssid_len != connect->ssid_len || 145562306a36Sopenharmony_ci memcmp(wdev->u.client.ssid, connect->ssid, wdev->u.client.ssid_len))) 145662306a36Sopenharmony_ci return -EALREADY; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci /* 145962306a36Sopenharmony_ci * If connected, reject (re-)association unless prev_bssid 146062306a36Sopenharmony_ci * matches the current BSSID. 146162306a36Sopenharmony_ci */ 146262306a36Sopenharmony_ci if (wdev->connected) { 146362306a36Sopenharmony_ci if (!prev_bssid) 146462306a36Sopenharmony_ci return -EALREADY; 146562306a36Sopenharmony_ci if (!ether_addr_equal(prev_bssid, 146662306a36Sopenharmony_ci wdev->u.client.connected_addr)) 146762306a36Sopenharmony_ci return -ENOTCONN; 146862306a36Sopenharmony_ci } 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci /* 147162306a36Sopenharmony_ci * Reject if we're in the process of connecting with WEP, 147262306a36Sopenharmony_ci * this case isn't very interesting and trying to handle 147362306a36Sopenharmony_ci * it would make the code much more complex. 147462306a36Sopenharmony_ci */ 147562306a36Sopenharmony_ci if (wdev->connect_keys) 147662306a36Sopenharmony_ci return -EINPROGRESS; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci cfg80211_oper_and_ht_capa(&connect->ht_capa_mask, 147962306a36Sopenharmony_ci rdev->wiphy.ht_capa_mod_mask); 148062306a36Sopenharmony_ci cfg80211_oper_and_vht_capa(&connect->vht_capa_mask, 148162306a36Sopenharmony_ci rdev->wiphy.vht_capa_mod_mask); 148262306a36Sopenharmony_ci 148362306a36Sopenharmony_ci if (connkeys && connkeys->def >= 0) { 148462306a36Sopenharmony_ci int idx; 148562306a36Sopenharmony_ci u32 cipher; 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci idx = connkeys->def; 148862306a36Sopenharmony_ci cipher = connkeys->params[idx].cipher; 148962306a36Sopenharmony_ci /* If given a WEP key we may need it for shared key auth */ 149062306a36Sopenharmony_ci if (cipher == WLAN_CIPHER_SUITE_WEP40 || 149162306a36Sopenharmony_ci cipher == WLAN_CIPHER_SUITE_WEP104) { 149262306a36Sopenharmony_ci connect->key_idx = idx; 149362306a36Sopenharmony_ci connect->key = connkeys->params[idx].key; 149462306a36Sopenharmony_ci connect->key_len = connkeys->params[idx].key_len; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci /* 149762306a36Sopenharmony_ci * If ciphers are not set (e.g. when going through 149862306a36Sopenharmony_ci * iwconfig), we have to set them appropriately here. 149962306a36Sopenharmony_ci */ 150062306a36Sopenharmony_ci if (connect->crypto.cipher_group == 0) 150162306a36Sopenharmony_ci connect->crypto.cipher_group = cipher; 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci if (connect->crypto.n_ciphers_pairwise == 0) { 150462306a36Sopenharmony_ci connect->crypto.n_ciphers_pairwise = 1; 150562306a36Sopenharmony_ci connect->crypto.ciphers_pairwise[0] = cipher; 150662306a36Sopenharmony_ci } 150762306a36Sopenharmony_ci } 150862306a36Sopenharmony_ci } else { 150962306a36Sopenharmony_ci if (WARN_ON(connkeys)) 151062306a36Sopenharmony_ci return -EINVAL; 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci /* connect can point to wdev->wext.connect which 151362306a36Sopenharmony_ci * can hold key data from a previous connection 151462306a36Sopenharmony_ci */ 151562306a36Sopenharmony_ci connect->key = NULL; 151662306a36Sopenharmony_ci connect->key_len = 0; 151762306a36Sopenharmony_ci connect->key_idx = 0; 151862306a36Sopenharmony_ci } 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci wdev->connect_keys = connkeys; 152162306a36Sopenharmony_ci memcpy(wdev->u.client.ssid, connect->ssid, connect->ssid_len); 152262306a36Sopenharmony_ci wdev->u.client.ssid_len = connect->ssid_len; 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci wdev->conn_bss_type = connect->pbss ? IEEE80211_BSS_TYPE_PBSS : 152562306a36Sopenharmony_ci IEEE80211_BSS_TYPE_ESS; 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci if (!rdev->ops->connect) 152862306a36Sopenharmony_ci err = cfg80211_sme_connect(wdev, connect, prev_bssid); 152962306a36Sopenharmony_ci else 153062306a36Sopenharmony_ci err = rdev_connect(rdev, dev, connect); 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci if (err) { 153362306a36Sopenharmony_ci wdev->connect_keys = NULL; 153462306a36Sopenharmony_ci /* 153562306a36Sopenharmony_ci * This could be reassoc getting refused, don't clear 153662306a36Sopenharmony_ci * ssid_len in that case. 153762306a36Sopenharmony_ci */ 153862306a36Sopenharmony_ci if (!wdev->connected) 153962306a36Sopenharmony_ci wdev->u.client.ssid_len = 0; 154062306a36Sopenharmony_ci return err; 154162306a36Sopenharmony_ci } 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci return 0; 154462306a36Sopenharmony_ci} 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ciint cfg80211_disconnect(struct cfg80211_registered_device *rdev, 154762306a36Sopenharmony_ci struct net_device *dev, u16 reason, bool wextev) 154862306a36Sopenharmony_ci{ 154962306a36Sopenharmony_ci struct wireless_dev *wdev = dev->ieee80211_ptr; 155062306a36Sopenharmony_ci int err = 0; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci ASSERT_WDEV_LOCK(wdev); 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci kfree_sensitive(wdev->connect_keys); 155562306a36Sopenharmony_ci wdev->connect_keys = NULL; 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci wdev->conn_owner_nlportid = 0; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci if (wdev->conn) 156062306a36Sopenharmony_ci err = cfg80211_sme_disconnect(wdev, reason); 156162306a36Sopenharmony_ci else if (!rdev->ops->disconnect) 156262306a36Sopenharmony_ci cfg80211_mlme_down(rdev, dev); 156362306a36Sopenharmony_ci else if (wdev->u.client.ssid_len) 156462306a36Sopenharmony_ci err = rdev_disconnect(rdev, dev, reason); 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci /* 156762306a36Sopenharmony_ci * Clear ssid_len unless we actually were fully connected, 156862306a36Sopenharmony_ci * in which case cfg80211_disconnected() will take care of 156962306a36Sopenharmony_ci * this later. 157062306a36Sopenharmony_ci */ 157162306a36Sopenharmony_ci if (!wdev->connected) 157262306a36Sopenharmony_ci wdev->u.client.ssid_len = 0; 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci return err; 157562306a36Sopenharmony_ci} 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci/* 157862306a36Sopenharmony_ci * Used to clean up after the connection / connection attempt owner socket 157962306a36Sopenharmony_ci * disconnects 158062306a36Sopenharmony_ci */ 158162306a36Sopenharmony_civoid cfg80211_autodisconnect_wk(struct work_struct *work) 158262306a36Sopenharmony_ci{ 158362306a36Sopenharmony_ci struct wireless_dev *wdev = 158462306a36Sopenharmony_ci container_of(work, struct wireless_dev, disconnect_wk); 158562306a36Sopenharmony_ci struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_ci wiphy_lock(wdev->wiphy); 158862306a36Sopenharmony_ci wdev_lock(wdev); 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci if (wdev->conn_owner_nlportid) { 159162306a36Sopenharmony_ci switch (wdev->iftype) { 159262306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 159362306a36Sopenharmony_ci __cfg80211_leave_ibss(rdev, wdev->netdev, false); 159462306a36Sopenharmony_ci break; 159562306a36Sopenharmony_ci case NL80211_IFTYPE_AP: 159662306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_GO: 159762306a36Sopenharmony_ci __cfg80211_stop_ap(rdev, wdev->netdev, -1, false); 159862306a36Sopenharmony_ci break; 159962306a36Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 160062306a36Sopenharmony_ci __cfg80211_leave_mesh(rdev, wdev->netdev); 160162306a36Sopenharmony_ci break; 160262306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 160362306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_CLIENT: 160462306a36Sopenharmony_ci /* 160562306a36Sopenharmony_ci * Use disconnect_bssid if still connecting and 160662306a36Sopenharmony_ci * ops->disconnect not implemented. Otherwise we can 160762306a36Sopenharmony_ci * use cfg80211_disconnect. 160862306a36Sopenharmony_ci */ 160962306a36Sopenharmony_ci if (rdev->ops->disconnect || wdev->connected) 161062306a36Sopenharmony_ci cfg80211_disconnect(rdev, wdev->netdev, 161162306a36Sopenharmony_ci WLAN_REASON_DEAUTH_LEAVING, 161262306a36Sopenharmony_ci true); 161362306a36Sopenharmony_ci else 161462306a36Sopenharmony_ci cfg80211_mlme_deauth(rdev, wdev->netdev, 161562306a36Sopenharmony_ci wdev->disconnect_bssid, 161662306a36Sopenharmony_ci NULL, 0, 161762306a36Sopenharmony_ci WLAN_REASON_DEAUTH_LEAVING, 161862306a36Sopenharmony_ci false); 161962306a36Sopenharmony_ci break; 162062306a36Sopenharmony_ci default: 162162306a36Sopenharmony_ci break; 162262306a36Sopenharmony_ci } 162362306a36Sopenharmony_ci } 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci wdev_unlock(wdev); 162662306a36Sopenharmony_ci wiphy_unlock(wdev->wiphy); 162762306a36Sopenharmony_ci} 1628