1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2b23aa676SSamuel Ortiz /*
3ceca7b71SJohannes Berg * SME code for cfg80211
4ceca7b71SJohannes Berg * both driver SME event handling and the SME implementation
5ceca7b71SJohannes Berg * (for nl80211's connect() and wext)
6b23aa676SSamuel Ortiz *
7b23aa676SSamuel Ortiz * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
8e9da6df7SJohannes Berg * Copyright (C) 2009, 2020, 2022-2023 Intel Corporation. All rights reserved.
929ce6ecbSAvraham Stern * Copyright 2017 Intel Deutschland GmbH
10b23aa676SSamuel Ortiz */
11b23aa676SSamuel Ortiz
12b23aa676SSamuel Ortiz #include <linux/etherdevice.h>
13b23aa676SSamuel Ortiz #include <linux/if_arp.h>
145a0e3ad6STejun Heo #include <linux/slab.h>
15b23aa676SSamuel Ortiz #include <linux/workqueue.h>
16a9a11622SJohannes Berg #include <linux/wireless.h>
17bc3b2d7fSPaul Gortmaker #include <linux/export.h>
18a9a11622SJohannes Berg #include <net/iw_handler.h>
19b23aa676SSamuel Ortiz #include <net/cfg80211.h>
20b23aa676SSamuel Ortiz #include <net/rtnetlink.h>
21b23aa676SSamuel Ortiz #include "nl80211.h"
228b19e6caSLuis R. Rodriguez #include "reg.h"
23e35e4d28SHila Gonen #include "rdev-ops.h"
24b23aa676SSamuel Ortiz
25ceca7b71SJohannes Berg /*
26ceca7b71SJohannes Berg * Software SME in cfg80211, using auth/assoc/deauth calls to the
2754f65de0SRandy Dunlap * driver. This is for implementing nl80211's connect/disconnect
28ceca7b71SJohannes Berg * and wireless extensions (if configured.)
29ceca7b71SJohannes Berg */
30ceca7b71SJohannes Berg
316829c878SJohannes Berg struct cfg80211_conn {
326829c878SJohannes Berg struct cfg80211_connect_params params;
336829c878SJohannes Berg /* these are sub-states of the _CONNECTING sme_state */
346829c878SJohannes Berg enum {
356829c878SJohannes Berg CFG80211_CONN_SCANNING,
366829c878SJohannes Berg CFG80211_CONN_SCAN_AGAIN,
376829c878SJohannes Berg CFG80211_CONN_AUTHENTICATE_NEXT,
386829c878SJohannes Berg CFG80211_CONN_AUTHENTICATING,
393093ebbeSPurushottam Kushwaha CFG80211_CONN_AUTH_FAILED_TIMEOUT,
406829c878SJohannes Berg CFG80211_CONN_ASSOCIATE_NEXT,
416829c878SJohannes Berg CFG80211_CONN_ASSOCIATING,
42923a0e7dSJohannes Berg CFG80211_CONN_ASSOC_FAILED,
433093ebbeSPurushottam Kushwaha CFG80211_CONN_ASSOC_FAILED_TIMEOUT,
44ceca7b71SJohannes Berg CFG80211_CONN_DEAUTH,
45e6f462dfSJohannes Berg CFG80211_CONN_ABANDON,
46ceca7b71SJohannes Berg CFG80211_CONN_CONNECTED,
476829c878SJohannes Berg } state;
48f401a6f7SJohannes Berg u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
4946b9d180SJohannes Berg const u8 *ie;
506829c878SJohannes Berg size_t ie_len;
51f401a6f7SJohannes Berg bool auto_auth, prev_bssid_valid;
526829c878SJohannes Berg };
536829c878SJohannes Berg
cfg80211_sme_free(struct wireless_dev * wdev)54ceca7b71SJohannes Berg static void cfg80211_sme_free(struct wireless_dev *wdev)
5509d989d1SLuis R. Rodriguez {
56ceca7b71SJohannes Berg if (!wdev->conn)
57ceca7b71SJohannes Berg return;
5809d989d1SLuis R. Rodriguez
59ceca7b71SJohannes Berg kfree(wdev->conn->ie);
60ceca7b71SJohannes Berg kfree(wdev->conn);
61ceca7b71SJohannes Berg wdev->conn = NULL;
6209d989d1SLuis R. Rodriguez }
636829c878SJohannes Berg
cfg80211_conn_scan(struct wireless_dev * wdev)646829c878SJohannes Berg static int cfg80211_conn_scan(struct wireless_dev *wdev)
656829c878SJohannes Berg {
66f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
676829c878SJohannes Berg struct cfg80211_scan_request *request;
686829c878SJohannes Berg int n_channels, err;
696829c878SJohannes Berg
70667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev);
716829c878SJohannes Berg
72f9d15d16SJohannes Berg if (rdev->scan_req || rdev->scan_msg)
736829c878SJohannes Berg return -EBUSY;
746829c878SJohannes Berg
75bdfbec2dSIlan Peer if (wdev->conn->params.channel)
766829c878SJohannes Berg n_channels = 1;
77bdfbec2dSIlan Peer else
78bdfbec2dSIlan Peer n_channels = ieee80211_get_num_supported_channels(wdev->wiphy);
796829c878SJohannes Berg
806829c878SJohannes Berg request = kzalloc(sizeof(*request) + sizeof(request->ssids[0]) +
816829c878SJohannes Berg sizeof(request->channels[0]) * n_channels,
826829c878SJohannes Berg GFP_KERNEL);
836829c878SJohannes Berg if (!request)
846829c878SJohannes Berg return -ENOMEM;
856829c878SJohannes Berg
86*bd7ddc5fSHaoyu Li request->n_channels = n_channels;
872a84ee86SKarl Beldan if (wdev->conn->params.channel) {
8857fbcce3SJohannes Berg enum nl80211_band band = wdev->conn->params.channel->band;
892a84ee86SKarl Beldan struct ieee80211_supported_band *sband =
902a84ee86SKarl Beldan wdev->wiphy->bands[band];
912a84ee86SKarl Beldan
922a84ee86SKarl Beldan if (!sband) {
932a84ee86SKarl Beldan kfree(request);
942a84ee86SKarl Beldan return -EINVAL;
952a84ee86SKarl Beldan }
966829c878SJohannes Berg request->channels[0] = wdev->conn->params.channel;
972a84ee86SKarl Beldan request->rates[band] = (1 << sband->n_bitrates) - 1;
982a84ee86SKarl Beldan } else {
996829c878SJohannes Berg int i = 0, j;
10057fbcce3SJohannes Berg enum nl80211_band band;
101e3081501SRajkumar Manoharan struct ieee80211_supported_band *bands;
102e3081501SRajkumar Manoharan struct ieee80211_channel *channel;
1036829c878SJohannes Berg
10457fbcce3SJohannes Berg for (band = 0; band < NUM_NL80211_BANDS; band++) {
105e3081501SRajkumar Manoharan bands = wdev->wiphy->bands[band];
106e3081501SRajkumar Manoharan if (!bands)
1076829c878SJohannes Berg continue;
108e3081501SRajkumar Manoharan for (j = 0; j < bands->n_channels; j++) {
109e3081501SRajkumar Manoharan channel = &bands->channels[j];
110e3081501SRajkumar Manoharan if (channel->flags & IEEE80211_CHAN_DISABLED)
111e3081501SRajkumar Manoharan continue;
112e3081501SRajkumar Manoharan request->channels[i++] = channel;
1136829c878SJohannes Berg }
114e3081501SRajkumar Manoharan request->rates[band] = (1 << bands->n_bitrates) - 1;
115e3081501SRajkumar Manoharan }
116e3081501SRajkumar Manoharan n_channels = i;
1176829c878SJohannes Berg }
1186829c878SJohannes Berg request->n_channels = n_channels;
119cacdc118SDmitry Antipov request->ssids = (void *)request +
120cacdc118SDmitry Antipov struct_size(request, channels, n_channels);
1216829c878SJohannes Berg request->n_ssids = 1;
1226829c878SJohannes Berg
1236829c878SJohannes Berg memcpy(request->ssids[0].ssid, wdev->conn->params.ssid,
1246829c878SJohannes Berg wdev->conn->params.ssid_len);
1256829c878SJohannes Berg request->ssids[0].ssid_len = wdev->conn->params.ssid_len;
1266829c878SJohannes Berg
127818965d3SJouni Malinen eth_broadcast_addr(request->bssid);
128818965d3SJouni Malinen
129fd014284SJohannes Berg request->wdev = wdev;
13079c97e97SJohannes Berg request->wiphy = &rdev->wiphy;
13115d6030bSSam Leffler request->scan_start = jiffies;
1326829c878SJohannes Berg
13379c97e97SJohannes Berg rdev->scan_req = request;
1346829c878SJohannes Berg
135e35e4d28SHila Gonen err = rdev_scan(rdev, request);
1366829c878SJohannes Berg if (!err) {
1376829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_SCANNING;
138fd014284SJohannes Berg nl80211_send_scan_start(rdev, wdev);
139463d0183SJohannes Berg dev_hold(wdev->netdev);
1406829c878SJohannes Berg } else {
14179c97e97SJohannes Berg rdev->scan_req = NULL;
1426829c878SJohannes Berg kfree(request);
1436829c878SJohannes Berg }
1446829c878SJohannes Berg return err;
1456829c878SJohannes Berg }
1466829c878SJohannes Berg
cfg80211_conn_do_work(struct wireless_dev * wdev,enum nl80211_timeout_reason * treason)1473093ebbeSPurushottam Kushwaha static int cfg80211_conn_do_work(struct wireless_dev *wdev,
1483093ebbeSPurushottam Kushwaha enum nl80211_timeout_reason *treason)
1496829c878SJohannes Berg {
150f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
15119957bb3SJohannes Berg struct cfg80211_connect_params *params;
152325839daSJohannes Berg struct cfg80211_auth_request auth_req = {};
153f62fab73SJohannes Berg struct cfg80211_assoc_request req = {};
15419957bb3SJohannes Berg int err;
1556829c878SJohannes Berg
156667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev);
157667503ddSJohannes Berg
1586829c878SJohannes Berg if (!wdev->conn)
1596829c878SJohannes Berg return 0;
1606829c878SJohannes Berg
16119957bb3SJohannes Berg params = &wdev->conn->params;
16219957bb3SJohannes Berg
1636829c878SJohannes Berg switch (wdev->conn->state) {
164ceca7b71SJohannes Berg case CFG80211_CONN_SCANNING:
165ceca7b71SJohannes Berg /* didn't find it during scan ... */
166ceca7b71SJohannes Berg return -ENOENT;
1676829c878SJohannes Berg case CFG80211_CONN_SCAN_AGAIN:
1686829c878SJohannes Berg return cfg80211_conn_scan(wdev);
1696829c878SJohannes Berg case CFG80211_CONN_AUTHENTICATE_NEXT:
1702fd05115SJohannes Berg if (WARN_ON(!rdev->ops->auth))
1712fd05115SJohannes Berg return -EOPNOTSUPP;
17219957bb3SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATING;
173325839daSJohannes Berg auth_req.key = params->key;
174325839daSJohannes Berg auth_req.key_len = params->key_len;
175325839daSJohannes Berg auth_req.key_idx = params->key_idx;
176325839daSJohannes Berg auth_req.auth_type = params->auth_type;
177325839daSJohannes Berg auth_req.bss = cfg80211_get_bss(&rdev->wiphy, params->channel,
17819957bb3SJohannes Berg params->bssid,
17919957bb3SJohannes Berg params->ssid, params->ssid_len,
180325839daSJohannes Berg IEEE80211_BSS_TYPE_ESS,
181325839daSJohannes Berg IEEE80211_PRIVACY_ANY);
182d648c230SJohannes Berg auth_req.link_id = -1;
183325839daSJohannes Berg err = cfg80211_mlme_auth(rdev, wdev->netdev, &auth_req);
184325839daSJohannes Berg cfg80211_put_bss(&rdev->wiphy, auth_req.bss);
185325839daSJohannes Berg return err;
1863093ebbeSPurushottam Kushwaha case CFG80211_CONN_AUTH_FAILED_TIMEOUT:
1873093ebbeSPurushottam Kushwaha *treason = NL80211_TIMEOUT_AUTH;
188923a0e7dSJohannes Berg return -ENOTCONN;
1896829c878SJohannes Berg case CFG80211_CONN_ASSOCIATE_NEXT:
1902fd05115SJohannes Berg if (WARN_ON(!rdev->ops->assoc))
1912fd05115SJohannes Berg return -EOPNOTSUPP;
19219957bb3SJohannes Berg wdev->conn->state = CFG80211_CONN_ASSOCIATING;
193f401a6f7SJohannes Berg if (wdev->conn->prev_bssid_valid)
194f62fab73SJohannes Berg req.prev_bssid = wdev->conn->prev_bssid;
195f62fab73SJohannes Berg req.ie = params->ie;
196f62fab73SJohannes Berg req.ie_len = params->ie_len;
197f62fab73SJohannes Berg req.use_mfp = params->mfp != NL80211_MFP_NO;
198f62fab73SJohannes Berg req.crypto = params->crypto;
199f62fab73SJohannes Berg req.flags = params->flags;
200f62fab73SJohannes Berg req.ht_capa = params->ht_capa;
201f62fab73SJohannes Berg req.ht_capa_mask = params->ht_capa_mask;
202f62fab73SJohannes Berg req.vht_capa = params->vht_capa;
203f62fab73SJohannes Berg req.vht_capa_mask = params->vht_capa_mask;
204d648c230SJohannes Berg req.link_id = -1;
205f62fab73SJohannes Berg
2060f759448SJohannes Berg req.bss = cfg80211_get_bss(&rdev->wiphy, params->channel,
2070f759448SJohannes Berg params->bssid,
2080f759448SJohannes Berg params->ssid, params->ssid_len,
2090f759448SJohannes Berg IEEE80211_BSS_TYPE_ESS,
2100f759448SJohannes Berg IEEE80211_PRIVACY_ANY);
2110f759448SJohannes Berg if (!req.bss) {
2120f759448SJohannes Berg err = -ENOENT;
2130f759448SJohannes Berg } else {
2140f759448SJohannes Berg err = cfg80211_mlme_assoc(rdev, wdev->netdev, &req);
2150f759448SJohannes Berg cfg80211_put_bss(&rdev->wiphy, req.bss);
2160f759448SJohannes Berg }
2170f759448SJohannes Berg
21819957bb3SJohannes Berg if (err)
21991bf9b26SJohannes Berg cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
220667503ddSJohannes Berg NULL, 0,
221d5cdfacbSJouni Malinen WLAN_REASON_DEAUTH_LEAVING,
222d5cdfacbSJouni Malinen false);
22319957bb3SJohannes Berg return err;
2243093ebbeSPurushottam Kushwaha case CFG80211_CONN_ASSOC_FAILED_TIMEOUT:
2253093ebbeSPurushottam Kushwaha *treason = NL80211_TIMEOUT_ASSOC;
2267b506ff6SMiaohe Lin fallthrough;
227923a0e7dSJohannes Berg case CFG80211_CONN_ASSOC_FAILED:
228923a0e7dSJohannes Berg cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
229923a0e7dSJohannes Berg NULL, 0,
230923a0e7dSJohannes Berg WLAN_REASON_DEAUTH_LEAVING, false);
231923a0e7dSJohannes Berg return -ENOTCONN;
232ceca7b71SJohannes Berg case CFG80211_CONN_DEAUTH:
23391bf9b26SJohannes Berg cfg80211_mlme_deauth(rdev, wdev->netdev, params->bssid,
2347d930bc3SJohannes Berg NULL, 0,
235d5cdfacbSJouni Malinen WLAN_REASON_DEAUTH_LEAVING, false);
2367b506ff6SMiaohe Lin fallthrough;
237e6f462dfSJohannes Berg case CFG80211_CONN_ABANDON:
238923a0e7dSJohannes Berg /* free directly, disconnected event already sent */
239923a0e7dSJohannes Berg cfg80211_sme_free(wdev);
240ceca7b71SJohannes Berg return 0;
2416829c878SJohannes Berg default:
2426829c878SJohannes Berg return 0;
2436829c878SJohannes Berg }
2446829c878SJohannes Berg }
2456829c878SJohannes Berg
cfg80211_conn_work(struct work_struct * work)2466829c878SJohannes Berg void cfg80211_conn_work(struct work_struct *work)
2476829c878SJohannes Berg {
24879c97e97SJohannes Berg struct cfg80211_registered_device *rdev =
2496829c878SJohannes Berg container_of(work, struct cfg80211_registered_device, conn_work);
2506829c878SJohannes Berg struct wireless_dev *wdev;
2517400f42eSJohannes Berg u8 bssid_buf[ETH_ALEN], *bssid = NULL;
2523093ebbeSPurushottam Kushwaha enum nl80211_timeout_reason treason;
2536829c878SJohannes Berg
254a05829a7SJohannes Berg wiphy_lock(&rdev->wiphy);
2556829c878SJohannes Berg
25653873f13SJohannes Berg list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
257c8157976SJohannes Berg if (!wdev->netdev)
258c8157976SJohannes Berg continue;
259c8157976SJohannes Berg
260667503ddSJohannes Berg wdev_lock(wdev);
261667503ddSJohannes Berg if (!netif_running(wdev->netdev)) {
262667503ddSJohannes Berg wdev_unlock(wdev);
2636829c878SJohannes Berg continue;
264667503ddSJohannes Berg }
265ceca7b71SJohannes Berg if (!wdev->conn ||
266ceca7b71SJohannes Berg wdev->conn->state == CFG80211_CONN_CONNECTED) {
267667503ddSJohannes Berg wdev_unlock(wdev);
2686829c878SJohannes Berg continue;
269667503ddSJohannes Berg }
2707400f42eSJohannes Berg if (wdev->conn->params.bssid) {
2717400f42eSJohannes Berg memcpy(bssid_buf, wdev->conn->params.bssid, ETH_ALEN);
2727400f42eSJohannes Berg bssid = bssid_buf;
2737400f42eSJohannes Berg }
2743093ebbeSPurushottam Kushwaha treason = NL80211_TIMEOUT_UNSPECIFIED;
2753093ebbeSPurushottam Kushwaha if (cfg80211_conn_do_work(wdev, &treason)) {
2765349a0f7SVidyullatha Kanchanapally struct cfg80211_connect_resp_params cr;
2775349a0f7SVidyullatha Kanchanapally
2785349a0f7SVidyullatha Kanchanapally memset(&cr, 0, sizeof(cr));
2795349a0f7SVidyullatha Kanchanapally cr.status = -1;
280efbabc11SVeerendranath Jakkam cr.links[0].bssid = bssid;
2815349a0f7SVidyullatha Kanchanapally cr.timeout_reason = treason;
2825349a0f7SVidyullatha Kanchanapally __cfg80211_connect_result(wdev->netdev, &cr, false);
283ceca7b71SJohannes Berg }
284667503ddSJohannes Berg wdev_unlock(wdev);
2856829c878SJohannes Berg }
2866829c878SJohannes Berg
287a05829a7SJohannes Berg wiphy_unlock(&rdev->wiphy);
2886829c878SJohannes Berg }
2896829c878SJohannes Berg
cfg80211_step_auth_next(struct cfg80211_conn * conn,struct cfg80211_bss * bss)290015b8cc5SAlexander Wetzel static void cfg80211_step_auth_next(struct cfg80211_conn *conn,
291015b8cc5SAlexander Wetzel struct cfg80211_bss *bss)
292015b8cc5SAlexander Wetzel {
293015b8cc5SAlexander Wetzel memcpy(conn->bssid, bss->bssid, ETH_ALEN);
294015b8cc5SAlexander Wetzel conn->params.bssid = conn->bssid;
295015b8cc5SAlexander Wetzel conn->params.channel = bss->channel;
296015b8cc5SAlexander Wetzel conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
297015b8cc5SAlexander Wetzel }
298015b8cc5SAlexander Wetzel
2990e3a39b5SBen Greear /* Returned bss is reference counted and must be cleaned up appropriately. */
cfg80211_get_conn_bss(struct wireless_dev * wdev)300bbac31f4SJohannes Berg static struct cfg80211_bss *cfg80211_get_conn_bss(struct wireless_dev *wdev)
3016829c878SJohannes Berg {
302f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
3036829c878SJohannes Berg struct cfg80211_bss *bss;
3046829c878SJohannes Berg
305667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev);
306667503ddSJohannes Berg
307ed9d0102SJouni Malinen bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel,
308ed9d0102SJouni Malinen wdev->conn->params.bssid,
3096829c878SJohannes Berg wdev->conn->params.ssid,
3106829c878SJohannes Berg wdev->conn->params.ssid_len,
31134d50519SLior David wdev->conn_bss_type,
3126eb18137SDedy Lansky IEEE80211_PRIVACY(wdev->conn->params.privacy));
3136829c878SJohannes Berg if (!bss)
314bbac31f4SJohannes Berg return NULL;
3156829c878SJohannes Berg
316015b8cc5SAlexander Wetzel cfg80211_step_auth_next(wdev->conn, bss);
31779c97e97SJohannes Berg schedule_work(&rdev->conn_work);
3186829c878SJohannes Berg
319bbac31f4SJohannes Berg return bss;
3206829c878SJohannes Berg }
3216829c878SJohannes Berg
__cfg80211_sme_scan_done(struct net_device * dev)322667503ddSJohannes Berg static void __cfg80211_sme_scan_done(struct net_device *dev)
3236829c878SJohannes Berg {
3246829c878SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr;
325f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
326bbac31f4SJohannes Berg struct cfg80211_bss *bss;
3276829c878SJohannes Berg
328667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev);
329667503ddSJohannes Berg
330d4b1a687SZhu Yi if (!wdev->conn)
3316829c878SJohannes Berg return;
3326829c878SJohannes Berg
3336829c878SJohannes Berg if (wdev->conn->state != CFG80211_CONN_SCANNING &&
3346829c878SJohannes Berg wdev->conn->state != CFG80211_CONN_SCAN_AGAIN)
3356829c878SJohannes Berg return;
3366829c878SJohannes Berg
337bbac31f4SJohannes Berg bss = cfg80211_get_conn_bss(wdev);
338ceca7b71SJohannes Berg if (bss)
3395b112d3dSJohannes Berg cfg80211_put_bss(&rdev->wiphy, bss);
3406829c878SJohannes Berg else
341ceca7b71SJohannes Berg schedule_work(&rdev->conn_work);
3426829c878SJohannes Berg }
3436829c878SJohannes Berg
cfg80211_sme_scan_done(struct net_device * dev)344667503ddSJohannes Berg void cfg80211_sme_scan_done(struct net_device *dev)
345667503ddSJohannes Berg {
346667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr;
347667503ddSJohannes Berg
348667503ddSJohannes Berg wdev_lock(wdev);
349667503ddSJohannes Berg __cfg80211_sme_scan_done(dev);
350667503ddSJohannes Berg wdev_unlock(wdev);
351667503ddSJohannes Berg }
352667503ddSJohannes Berg
cfg80211_sme_rx_auth(struct wireless_dev * wdev,const u8 * buf,size_t len)353ceca7b71SJohannes Berg void cfg80211_sme_rx_auth(struct wireless_dev *wdev, const u8 *buf, size_t len)
3546829c878SJohannes Berg {
3556829c878SJohannes Berg struct wiphy *wiphy = wdev->wiphy;
356f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
3576829c878SJohannes Berg struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf;
3586829c878SJohannes Berg u16 status_code = le16_to_cpu(mgmt->u.auth.status_code);
3596829c878SJohannes Berg
360667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev);
361667503ddSJohannes Berg
362ceca7b71SJohannes Berg if (!wdev->conn || wdev->conn->state == CFG80211_CONN_CONNECTED)
3636829c878SJohannes Berg return;
3646829c878SJohannes Berg
3656829c878SJohannes Berg if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG &&
3666829c878SJohannes Berg wdev->conn->auto_auth &&
3676829c878SJohannes Berg wdev->conn->params.auth_type != NL80211_AUTHTYPE_NETWORK_EAP) {
3686829c878SJohannes Berg /* select automatically between only open, shared, leap */
3696829c878SJohannes Berg switch (wdev->conn->params.auth_type) {
3706829c878SJohannes Berg case NL80211_AUTHTYPE_OPEN_SYSTEM:
371fffd0934SJohannes Berg if (wdev->connect_keys)
3726829c878SJohannes Berg wdev->conn->params.auth_type =
3736829c878SJohannes Berg NL80211_AUTHTYPE_SHARED_KEY;
374fffd0934SJohannes Berg else
375fffd0934SJohannes Berg wdev->conn->params.auth_type =
376fffd0934SJohannes Berg NL80211_AUTHTYPE_NETWORK_EAP;
3776829c878SJohannes Berg break;
3786829c878SJohannes Berg case NL80211_AUTHTYPE_SHARED_KEY:
3796829c878SJohannes Berg wdev->conn->params.auth_type =
3806829c878SJohannes Berg NL80211_AUTHTYPE_NETWORK_EAP;
3816829c878SJohannes Berg break;
3826829c878SJohannes Berg default:
3836829c878SJohannes Berg /* huh? */
3846829c878SJohannes Berg wdev->conn->params.auth_type =
3856829c878SJohannes Berg NL80211_AUTHTYPE_OPEN_SYSTEM;
3866829c878SJohannes Berg break;
3876829c878SJohannes Berg }
3886829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_AUTHENTICATE_NEXT;
3896829c878SJohannes Berg schedule_work(&rdev->conn_work);
39019957bb3SJohannes Berg } else if (status_code != WLAN_STATUS_SUCCESS) {
3915349a0f7SVidyullatha Kanchanapally struct cfg80211_connect_resp_params cr;
3925349a0f7SVidyullatha Kanchanapally
3935349a0f7SVidyullatha Kanchanapally memset(&cr, 0, sizeof(cr));
3945349a0f7SVidyullatha Kanchanapally cr.status = status_code;
395efbabc11SVeerendranath Jakkam cr.links[0].bssid = mgmt->bssid;
3965349a0f7SVidyullatha Kanchanapally cr.timeout_reason = NL80211_TIMEOUT_UNSPECIFIED;
3975349a0f7SVidyullatha Kanchanapally __cfg80211_connect_result(wdev->netdev, &cr, false);
398ceca7b71SJohannes Berg } else if (wdev->conn->state == CFG80211_CONN_AUTHENTICATING) {
3996829c878SJohannes Berg wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
4006829c878SJohannes Berg schedule_work(&rdev->conn_work);
4016829c878SJohannes Berg }
4026829c878SJohannes Berg }
403b23aa676SSamuel Ortiz
cfg80211_sme_rx_assoc_resp(struct wireless_dev * wdev,u16 status)404ceca7b71SJohannes Berg bool cfg80211_sme_rx_assoc_resp(struct wireless_dev *wdev, u16 status)
405f401a6f7SJohannes Berg {
406f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
407f401a6f7SJohannes Berg
408ceca7b71SJohannes Berg if (!wdev->conn)
409f401a6f7SJohannes Berg return false;
410f401a6f7SJohannes Berg
411ceca7b71SJohannes Berg if (status == WLAN_STATUS_SUCCESS) {
412ceca7b71SJohannes Berg wdev->conn->state = CFG80211_CONN_CONNECTED;
413f401a6f7SJohannes Berg return false;
414ceca7b71SJohannes Berg }
415f401a6f7SJohannes Berg
416ceca7b71SJohannes Berg if (wdev->conn->prev_bssid_valid) {
417f401a6f7SJohannes Berg /*
418f401a6f7SJohannes Berg * Some stupid APs don't accept reassoc, so we
419ceca7b71SJohannes Berg * need to fall back to trying regular assoc;
420ceca7b71SJohannes Berg * return true so no event is sent to userspace.
421f401a6f7SJohannes Berg */
422f401a6f7SJohannes Berg wdev->conn->prev_bssid_valid = false;
423f401a6f7SJohannes Berg wdev->conn->state = CFG80211_CONN_ASSOCIATE_NEXT;
424f401a6f7SJohannes Berg schedule_work(&rdev->conn_work);
425f401a6f7SJohannes Berg return true;
426f401a6f7SJohannes Berg }
427f401a6f7SJohannes Berg
428923a0e7dSJohannes Berg wdev->conn->state = CFG80211_CONN_ASSOC_FAILED;
429ceca7b71SJohannes Berg schedule_work(&rdev->conn_work);
430ceca7b71SJohannes Berg return false;
431ceca7b71SJohannes Berg }
4327d930bc3SJohannes Berg
cfg80211_sme_deauth(struct wireless_dev * wdev)433ceca7b71SJohannes Berg void cfg80211_sme_deauth(struct wireless_dev *wdev)
434ceca7b71SJohannes Berg {
435ceca7b71SJohannes Berg cfg80211_sme_free(wdev);
436ceca7b71SJohannes Berg }
437ceca7b71SJohannes Berg
cfg80211_sme_auth_timeout(struct wireless_dev * wdev)438ceca7b71SJohannes Berg void cfg80211_sme_auth_timeout(struct wireless_dev *wdev)
439ceca7b71SJohannes Berg {
440f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
441923a0e7dSJohannes Berg
442923a0e7dSJohannes Berg if (!wdev->conn)
443923a0e7dSJohannes Berg return;
444923a0e7dSJohannes Berg
4453093ebbeSPurushottam Kushwaha wdev->conn->state = CFG80211_CONN_AUTH_FAILED_TIMEOUT;
446923a0e7dSJohannes Berg schedule_work(&rdev->conn_work);
447ceca7b71SJohannes Berg }
448ceca7b71SJohannes Berg
cfg80211_sme_disassoc(struct wireless_dev * wdev)449ceca7b71SJohannes Berg void cfg80211_sme_disassoc(struct wireless_dev *wdev)
450ceca7b71SJohannes Berg {
451f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
452ceca7b71SJohannes Berg
453ceca7b71SJohannes Berg if (!wdev->conn)
454ceca7b71SJohannes Berg return;
455ceca7b71SJohannes Berg
456ceca7b71SJohannes Berg wdev->conn->state = CFG80211_CONN_DEAUTH;
4577d930bc3SJohannes Berg schedule_work(&rdev->conn_work);
4587d930bc3SJohannes Berg }
4597d930bc3SJohannes Berg
cfg80211_sme_assoc_timeout(struct wireless_dev * wdev)460ceca7b71SJohannes Berg void cfg80211_sme_assoc_timeout(struct wireless_dev *wdev)
461ceca7b71SJohannes Berg {
462f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
463923a0e7dSJohannes Berg
464923a0e7dSJohannes Berg if (!wdev->conn)
465923a0e7dSJohannes Berg return;
466923a0e7dSJohannes Berg
4673093ebbeSPurushottam Kushwaha wdev->conn->state = CFG80211_CONN_ASSOC_FAILED_TIMEOUT;
468923a0e7dSJohannes Berg schedule_work(&rdev->conn_work);
469ceca7b71SJohannes Berg }
470ceca7b71SJohannes Berg
cfg80211_sme_abandon_assoc(struct wireless_dev * wdev)471e6f462dfSJohannes Berg void cfg80211_sme_abandon_assoc(struct wireless_dev *wdev)
472e6f462dfSJohannes Berg {
473e6f462dfSJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
474e6f462dfSJohannes Berg
475e6f462dfSJohannes Berg if (!wdev->conn)
476e6f462dfSJohannes Berg return;
477e6f462dfSJohannes Berg
478e6f462dfSJohannes Berg wdev->conn->state = CFG80211_CONN_ABANDON;
479e6f462dfSJohannes Berg schedule_work(&rdev->conn_work);
480e6f462dfSJohannes Berg }
481e6f462dfSJohannes Berg
cfg80211_wdev_release_bsses(struct wireless_dev * wdev)4827b0a0e3cSJohannes Berg static void cfg80211_wdev_release_bsses(struct wireless_dev *wdev)
4837b0a0e3cSJohannes Berg {
4847b0a0e3cSJohannes Berg unsigned int link;
4857b0a0e3cSJohannes Berg
4867b0a0e3cSJohannes Berg for_each_valid_link(wdev, link) {
4877b0a0e3cSJohannes Berg if (!wdev->links[link].client.current_bss)
4887b0a0e3cSJohannes Berg continue;
4897b0a0e3cSJohannes Berg cfg80211_unhold_bss(wdev->links[link].client.current_bss);
4907b0a0e3cSJohannes Berg cfg80211_put_bss(wdev->wiphy,
4917b0a0e3cSJohannes Berg &wdev->links[link].client.current_bss->pub);
4927b0a0e3cSJohannes Berg wdev->links[link].client.current_bss = NULL;
4937b0a0e3cSJohannes Berg }
4947b0a0e3cSJohannes Berg }
4957b0a0e3cSJohannes Berg
cfg80211_wdev_release_link_bsses(struct wireless_dev * wdev,u16 link_mask)496065563b2SVeerendranath Jakkam void cfg80211_wdev_release_link_bsses(struct wireless_dev *wdev, u16 link_mask)
497065563b2SVeerendranath Jakkam {
498065563b2SVeerendranath Jakkam unsigned int link;
499065563b2SVeerendranath Jakkam
500065563b2SVeerendranath Jakkam for_each_valid_link(wdev, link) {
501065563b2SVeerendranath Jakkam if (!wdev->links[link].client.current_bss ||
502065563b2SVeerendranath Jakkam !(link_mask & BIT(link)))
503065563b2SVeerendranath Jakkam continue;
504065563b2SVeerendranath Jakkam cfg80211_unhold_bss(wdev->links[link].client.current_bss);
505065563b2SVeerendranath Jakkam cfg80211_put_bss(wdev->wiphy,
506065563b2SVeerendranath Jakkam &wdev->links[link].client.current_bss->pub);
507065563b2SVeerendranath Jakkam wdev->links[link].client.current_bss = NULL;
508065563b2SVeerendranath Jakkam }
509065563b2SVeerendranath Jakkam }
510065563b2SVeerendranath Jakkam
cfg80211_sme_get_conn_ies(struct wireless_dev * wdev,const u8 * ies,size_t ies_len,const u8 ** out_ies,size_t * out_ies_len)51146b9d180SJohannes Berg static int cfg80211_sme_get_conn_ies(struct wireless_dev *wdev,
51246b9d180SJohannes Berg const u8 *ies, size_t ies_len,
51346b9d180SJohannes Berg const u8 **out_ies, size_t *out_ies_len)
51446b9d180SJohannes Berg {
51546b9d180SJohannes Berg struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
51646b9d180SJohannes Berg u8 *buf;
51746b9d180SJohannes Berg size_t offs;
51846b9d180SJohannes Berg
51946b9d180SJohannes Berg if (!rdev->wiphy.extended_capabilities_len ||
52046b9d180SJohannes Berg (ies && cfg80211_find_ie(WLAN_EID_EXT_CAPABILITY, ies, ies_len))) {
52146b9d180SJohannes Berg *out_ies = kmemdup(ies, ies_len, GFP_KERNEL);
52246b9d180SJohannes Berg if (!*out_ies)
52346b9d180SJohannes Berg return -ENOMEM;
52446b9d180SJohannes Berg *out_ies_len = ies_len;
52546b9d180SJohannes Berg return 0;
52646b9d180SJohannes Berg }
52746b9d180SJohannes Berg
52846b9d180SJohannes Berg buf = kmalloc(ies_len + rdev->wiphy.extended_capabilities_len + 2,
52946b9d180SJohannes Berg GFP_KERNEL);
53046b9d180SJohannes Berg if (!buf)
53146b9d180SJohannes Berg return -ENOMEM;
53246b9d180SJohannes Berg
53346b9d180SJohannes Berg if (ies_len) {
53446b9d180SJohannes Berg static const u8 before_extcapa[] = {
53546b9d180SJohannes Berg /* not listing IEs expected to be created by driver */
53646b9d180SJohannes Berg WLAN_EID_RSN,
53746b9d180SJohannes Berg WLAN_EID_QOS_CAPA,
53846b9d180SJohannes Berg WLAN_EID_RRM_ENABLED_CAPABILITIES,
53946b9d180SJohannes Berg WLAN_EID_MOBILITY_DOMAIN,
54046b9d180SJohannes Berg WLAN_EID_SUPPORTED_REGULATORY_CLASSES,
54146b9d180SJohannes Berg WLAN_EID_BSS_COEX_2040,
54246b9d180SJohannes Berg };
54346b9d180SJohannes Berg
54446b9d180SJohannes Berg offs = ieee80211_ie_split(ies, ies_len, before_extcapa,
54546b9d180SJohannes Berg ARRAY_SIZE(before_extcapa), 0);
54646b9d180SJohannes Berg memcpy(buf, ies, offs);
54746b9d180SJohannes Berg /* leave a whole for extended capabilities IE */
54846b9d180SJohannes Berg memcpy(buf + offs + rdev->wiphy.extended_capabilities_len + 2,
54946b9d180SJohannes Berg ies + offs, ies_len - offs);
55046b9d180SJohannes Berg } else {
55146b9d180SJohannes Berg offs = 0;
55246b9d180SJohannes Berg }
55346b9d180SJohannes Berg
55446b9d180SJohannes Berg /* place extended capabilities IE (with only driver capabilities) */
55546b9d180SJohannes Berg buf[offs] = WLAN_EID_EXT_CAPABILITY;
55646b9d180SJohannes Berg buf[offs + 1] = rdev->wiphy.extended_capabilities_len;
55746b9d180SJohannes Berg memcpy(buf + offs + 2,
55846b9d180SJohannes Berg rdev->wiphy.extended_capabilities,
55946b9d180SJohannes Berg rdev->wiphy.extended_capabilities_len);
56046b9d180SJohannes Berg
56146b9d180SJohannes Berg *out_ies = buf;
56246b9d180SJohannes Berg *out_ies_len = ies_len + rdev->wiphy.extended_capabilities_len + 2;
56346b9d180SJohannes Berg
56446b9d180SJohannes Berg return 0;
56546b9d180SJohannes Berg }
56646b9d180SJohannes Berg
cfg80211_sme_connect(struct wireless_dev * wdev,struct cfg80211_connect_params * connect,const u8 * prev_bssid)567ceca7b71SJohannes Berg static int cfg80211_sme_connect(struct wireless_dev *wdev,
568ceca7b71SJohannes Berg struct cfg80211_connect_params *connect,
569ceca7b71SJohannes Berg const u8 *prev_bssid)
570ceca7b71SJohannes Berg {
571f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
572ceca7b71SJohannes Berg struct cfg80211_bss *bss;
573ceca7b71SJohannes Berg int err;
574ceca7b71SJohannes Berg
575ceca7b71SJohannes Berg if (!rdev->ops->auth || !rdev->ops->assoc)
576ceca7b71SJohannes Berg return -EOPNOTSUPP;
577ceca7b71SJohannes Berg
5787b0a0e3cSJohannes Berg cfg80211_wdev_release_bsses(wdev);
5794ce2bd9cSJouni Malinen
5807b0a0e3cSJohannes Berg if (wdev->connected) {
5814ce2bd9cSJouni Malinen cfg80211_sme_free(wdev);
5827b0a0e3cSJohannes Berg wdev->connected = false;
5834ce2bd9cSJouni Malinen }
584ceca7b71SJohannes Berg
5851b5ab825SDu Cheng if (wdev->conn)
586ceca7b71SJohannes Berg return -EINPROGRESS;
587ceca7b71SJohannes Berg
588ceca7b71SJohannes Berg wdev->conn = kzalloc(sizeof(*wdev->conn), GFP_KERNEL);
589ceca7b71SJohannes Berg if (!wdev->conn)
590ceca7b71SJohannes Berg return -ENOMEM;
591ceca7b71SJohannes Berg
592ceca7b71SJohannes Berg /*
593ceca7b71SJohannes Berg * Copy all parameters, and treat explicitly IEs, BSSID, SSID.
594ceca7b71SJohannes Berg */
595ceca7b71SJohannes Berg memcpy(&wdev->conn->params, connect, sizeof(*connect));
596ceca7b71SJohannes Berg if (connect->bssid) {
597ceca7b71SJohannes Berg wdev->conn->params.bssid = wdev->conn->bssid;
598ceca7b71SJohannes Berg memcpy(wdev->conn->bssid, connect->bssid, ETH_ALEN);
599ceca7b71SJohannes Berg }
600ceca7b71SJohannes Berg
60146b9d180SJohannes Berg if (cfg80211_sme_get_conn_ies(wdev, connect->ie, connect->ie_len,
60246b9d180SJohannes Berg &wdev->conn->ie,
60346b9d180SJohannes Berg &wdev->conn->params.ie_len)) {
604ceca7b71SJohannes Berg kfree(wdev->conn);
605ceca7b71SJohannes Berg wdev->conn = NULL;
606ceca7b71SJohannes Berg return -ENOMEM;
607ceca7b71SJohannes Berg }
60846b9d180SJohannes Berg wdev->conn->params.ie = wdev->conn->ie;
609ceca7b71SJohannes Berg
610ceca7b71SJohannes Berg if (connect->auth_type == NL80211_AUTHTYPE_AUTOMATIC) {
611ceca7b71SJohannes Berg wdev->conn->auto_auth = true;
612ceca7b71SJohannes Berg /* start with open system ... should mostly work */
613ceca7b71SJohannes Berg wdev->conn->params.auth_type =
614ceca7b71SJohannes Berg NL80211_AUTHTYPE_OPEN_SYSTEM;
615ceca7b71SJohannes Berg } else {
616ceca7b71SJohannes Berg wdev->conn->auto_auth = false;
617ceca7b71SJohannes Berg }
618ceca7b71SJohannes Berg
6197b0a0e3cSJohannes Berg wdev->conn->params.ssid = wdev->u.client.ssid;
6207b0a0e3cSJohannes Berg wdev->conn->params.ssid_len = wdev->u.client.ssid_len;
621ceca7b71SJohannes Berg
622ceca7b71SJohannes Berg /* see if we have the bss already */
623015b8cc5SAlexander Wetzel bss = cfg80211_get_bss(wdev->wiphy, wdev->conn->params.channel,
624015b8cc5SAlexander Wetzel wdev->conn->params.bssid,
625015b8cc5SAlexander Wetzel wdev->conn->params.ssid,
626015b8cc5SAlexander Wetzel wdev->conn->params.ssid_len,
627015b8cc5SAlexander Wetzel wdev->conn_bss_type,
628015b8cc5SAlexander Wetzel IEEE80211_PRIVACY(wdev->conn->params.privacy));
629ceca7b71SJohannes Berg
630ceca7b71SJohannes Berg if (prev_bssid) {
631ceca7b71SJohannes Berg memcpy(wdev->conn->prev_bssid, prev_bssid, ETH_ALEN);
632ceca7b71SJohannes Berg wdev->conn->prev_bssid_valid = true;
633ceca7b71SJohannes Berg }
634ceca7b71SJohannes Berg
635ceca7b71SJohannes Berg /* we're good if we have a matching bss struct */
636ceca7b71SJohannes Berg if (bss) {
6373093ebbeSPurushottam Kushwaha enum nl80211_timeout_reason treason;
6383093ebbeSPurushottam Kushwaha
639015b8cc5SAlexander Wetzel cfg80211_step_auth_next(wdev->conn, bss);
6403093ebbeSPurushottam Kushwaha err = cfg80211_conn_do_work(wdev, &treason);
641ceca7b71SJohannes Berg cfg80211_put_bss(wdev->wiphy, bss);
642ceca7b71SJohannes Berg } else {
643ceca7b71SJohannes Berg /* otherwise we'll need to scan for the AP first */
644ceca7b71SJohannes Berg err = cfg80211_conn_scan(wdev);
645ceca7b71SJohannes Berg
646ceca7b71SJohannes Berg /*
647ceca7b71SJohannes Berg * If we can't scan right now, then we need to scan again
648ceca7b71SJohannes Berg * after the current scan finished, since the parameters
649ceca7b71SJohannes Berg * changed (unless we find a good AP anyway).
650ceca7b71SJohannes Berg */
651ceca7b71SJohannes Berg if (err == -EBUSY) {
652ceca7b71SJohannes Berg err = 0;
653ceca7b71SJohannes Berg wdev->conn->state = CFG80211_CONN_SCAN_AGAIN;
654ceca7b71SJohannes Berg }
655ceca7b71SJohannes Berg }
656ceca7b71SJohannes Berg
657ceca7b71SJohannes Berg if (err)
658ceca7b71SJohannes Berg cfg80211_sme_free(wdev);
659ceca7b71SJohannes Berg
660ceca7b71SJohannes Berg return err;
661ceca7b71SJohannes Berg }
662ceca7b71SJohannes Berg
cfg80211_sme_disconnect(struct wireless_dev * wdev,u16 reason)663ceca7b71SJohannes Berg static int cfg80211_sme_disconnect(struct wireless_dev *wdev, u16 reason)
664ceca7b71SJohannes Berg {
665f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
666ceca7b71SJohannes Berg int err;
667ceca7b71SJohannes Berg
668ceca7b71SJohannes Berg if (!wdev->conn)
669ceca7b71SJohannes Berg return 0;
670ceca7b71SJohannes Berg
671ceca7b71SJohannes Berg if (!rdev->ops->deauth)
672ceca7b71SJohannes Berg return -EOPNOTSUPP;
673ceca7b71SJohannes Berg
674ceca7b71SJohannes Berg if (wdev->conn->state == CFG80211_CONN_SCANNING ||
675ceca7b71SJohannes Berg wdev->conn->state == CFG80211_CONN_SCAN_AGAIN) {
676ceca7b71SJohannes Berg err = 0;
677ceca7b71SJohannes Berg goto out;
678ceca7b71SJohannes Berg }
679ceca7b71SJohannes Berg
680ceca7b71SJohannes Berg /* wdev->conn->params.bssid must be set if > SCANNING */
681ceca7b71SJohannes Berg err = cfg80211_mlme_deauth(rdev, wdev->netdev,
682ceca7b71SJohannes Berg wdev->conn->params.bssid,
683ceca7b71SJohannes Berg NULL, 0, reason, false);
684ceca7b71SJohannes Berg out:
685ceca7b71SJohannes Berg cfg80211_sme_free(wdev);
686ceca7b71SJohannes Berg return err;
687ceca7b71SJohannes Berg }
688ceca7b71SJohannes Berg
689ceca7b71SJohannes Berg /*
690ceca7b71SJohannes Berg * code shared for in-device and software SME
691ceca7b71SJohannes Berg */
692ceca7b71SJohannes Berg
cfg80211_is_all_idle(void)693ceca7b71SJohannes Berg static bool cfg80211_is_all_idle(void)
694ceca7b71SJohannes Berg {
695ceca7b71SJohannes Berg struct cfg80211_registered_device *rdev;
696ceca7b71SJohannes Berg struct wireless_dev *wdev;
697ceca7b71SJohannes Berg bool is_all_idle = true;
698ceca7b71SJohannes Berg
699ceca7b71SJohannes Berg /*
700ceca7b71SJohannes Berg * All devices must be idle as otherwise if you are actively
701ceca7b71SJohannes Berg * scanning some new beacon hints could be learned and would
702ceca7b71SJohannes Berg * count as new regulatory hints.
703113f3aaaSSriram R * Also if there is any other active beaconing interface we
704113f3aaaSSriram R * need not issue a disconnect hint and reset any info such
705113f3aaaSSriram R * as chan dfs state, etc.
706ceca7b71SJohannes Berg */
707ceca7b71SJohannes Berg list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
70853873f13SJohannes Berg list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
709ceca7b71SJohannes Berg wdev_lock(wdev);
7107b0a0e3cSJohannes Berg if (wdev->conn || wdev->connected ||
711113f3aaaSSriram R cfg80211_beaconing_iface_active(wdev))
712ceca7b71SJohannes Berg is_all_idle = false;
713ceca7b71SJohannes Berg wdev_unlock(wdev);
714ceca7b71SJohannes Berg }
715ceca7b71SJohannes Berg }
716ceca7b71SJohannes Berg
717ceca7b71SJohannes Berg return is_all_idle;
718ceca7b71SJohannes Berg }
719ceca7b71SJohannes Berg
disconnect_work(struct work_struct * work)720ceca7b71SJohannes Berg static void disconnect_work(struct work_struct *work)
721ceca7b71SJohannes Berg {
722ceca7b71SJohannes Berg rtnl_lock();
723ceca7b71SJohannes Berg if (cfg80211_is_all_idle())
724ceca7b71SJohannes Berg regulatory_hint_disconnect();
725ceca7b71SJohannes Berg rtnl_unlock();
726ceca7b71SJohannes Berg }
727ceca7b71SJohannes Berg
728e005bd7dSJohannes Berg DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
729ceca7b71SJohannes Berg
730efbabc11SVeerendranath Jakkam static void
cfg80211_connect_result_release_bsses(struct wireless_dev * wdev,struct cfg80211_connect_resp_params * cr)731efbabc11SVeerendranath Jakkam cfg80211_connect_result_release_bsses(struct wireless_dev *wdev,
732efbabc11SVeerendranath Jakkam struct cfg80211_connect_resp_params *cr)
733efbabc11SVeerendranath Jakkam {
734efbabc11SVeerendranath Jakkam unsigned int link;
735efbabc11SVeerendranath Jakkam
736efbabc11SVeerendranath Jakkam for_each_valid_link(cr, link) {
737efbabc11SVeerendranath Jakkam if (!cr->links[link].bss)
738efbabc11SVeerendranath Jakkam continue;
739efbabc11SVeerendranath Jakkam cfg80211_unhold_bss(bss_from_pub(cr->links[link].bss));
740efbabc11SVeerendranath Jakkam cfg80211_put_bss(wdev->wiphy, cr->links[link].bss);
741efbabc11SVeerendranath Jakkam }
742efbabc11SVeerendranath Jakkam }
743efbabc11SVeerendranath Jakkam
744ceca7b71SJohannes Berg /*
745ceca7b71SJohannes Berg * API calls for drivers implementing connect/disconnect and
746ceca7b71SJohannes Berg * SME event handling
747ceca7b71SJohannes Berg */
748ceca7b71SJohannes Berg
7496f390908SBen Greear /* This method must consume bss one way or another */
__cfg80211_connect_result(struct net_device * dev,struct cfg80211_connect_resp_params * cr,bool wextev)7505349a0f7SVidyullatha Kanchanapally void __cfg80211_connect_result(struct net_device *dev,
7515349a0f7SVidyullatha Kanchanapally struct cfg80211_connect_resp_params *cr,
7525349a0f7SVidyullatha Kanchanapally bool wextev)
753b23aa676SSamuel Ortiz {
754b23aa676SSamuel Ortiz struct wireless_dev *wdev = dev->ieee80211_ptr;
755e3d331c9SJohannes Berg const struct element *country_elem = NULL;
756c38c7018SMarc Bornand const struct element *ssid;
757fb8b53acSJohannes Berg const u8 *country_data;
758fb8b53acSJohannes Berg u8 country_datalen;
7593d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
760b23aa676SSamuel Ortiz union iwreq_data wrqu;
761b23aa676SSamuel Ortiz #endif
762efbabc11SVeerendranath Jakkam unsigned int link;
763efbabc11SVeerendranath Jakkam const u8 *connected_addr;
764efbabc11SVeerendranath Jakkam bool bss_not_found = false;
765b23aa676SSamuel Ortiz
766667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev);
767667503ddSJohannes Berg
768074ac8dfSJohannes Berg if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
769efbabc11SVeerendranath Jakkam wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
770efbabc11SVeerendranath Jakkam goto out;
771efbabc11SVeerendranath Jakkam
772efbabc11SVeerendranath Jakkam if (cr->valid_links) {
773efbabc11SVeerendranath Jakkam if (WARN_ON(!cr->ap_mld_addr))
774efbabc11SVeerendranath Jakkam goto out;
775efbabc11SVeerendranath Jakkam
776efbabc11SVeerendranath Jakkam for_each_valid_link(cr, link) {
777efbabc11SVeerendranath Jakkam if (WARN_ON(!cr->links[link].addr))
778efbabc11SVeerendranath Jakkam goto out;
779efbabc11SVeerendranath Jakkam }
7805ec245e4SVeerendranath Jakkam
7815ec245e4SVeerendranath Jakkam if (WARN_ON(wdev->connect_keys))
7825ec245e4SVeerendranath Jakkam goto out;
7836f390908SBen Greear }
784b23aa676SSamuel Ortiz
7854d797fceSJouni Malinen wdev->unprot_beacon_reported = 0;
7865349a0f7SVidyullatha Kanchanapally nl80211_send_connect_result(wiphy_to_rdev(wdev->wiphy), dev, cr,
7875349a0f7SVidyullatha Kanchanapally GFP_KERNEL);
788efbabc11SVeerendranath Jakkam connected_addr = cr->valid_links ? cr->ap_mld_addr : cr->links[0].bssid;
789e45cd82aSJohannes Berg
7903d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
791efbabc11SVeerendranath Jakkam if (wextev && !cr->valid_links) {
7925349a0f7SVidyullatha Kanchanapally if (cr->req_ie && cr->status == WLAN_STATUS_SUCCESS) {
793e45cd82aSJohannes Berg memset(&wrqu, 0, sizeof(wrqu));
7945349a0f7SVidyullatha Kanchanapally wrqu.data.length = cr->req_ie_len;
7955349a0f7SVidyullatha Kanchanapally wireless_send_event(dev, IWEVASSOCREQIE, &wrqu,
7965349a0f7SVidyullatha Kanchanapally cr->req_ie);
797e45cd82aSJohannes Berg }
798e45cd82aSJohannes Berg
7995349a0f7SVidyullatha Kanchanapally if (cr->resp_ie && cr->status == WLAN_STATUS_SUCCESS) {
800e45cd82aSJohannes Berg memset(&wrqu, 0, sizeof(wrqu));
8015349a0f7SVidyullatha Kanchanapally wrqu.data.length = cr->resp_ie_len;
8025349a0f7SVidyullatha Kanchanapally wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu,
8035349a0f7SVidyullatha Kanchanapally cr->resp_ie);
804e45cd82aSJohannes Berg }
805e45cd82aSJohannes Berg
806e45cd82aSJohannes Berg memset(&wrqu, 0, sizeof(wrqu));
807e45cd82aSJohannes Berg wrqu.ap_addr.sa_family = ARPHRD_ETHER;
808efbabc11SVeerendranath Jakkam if (connected_addr && cr->status == WLAN_STATUS_SUCCESS) {
809efbabc11SVeerendranath Jakkam memcpy(wrqu.ap_addr.sa_data, connected_addr, ETH_ALEN);
810efbabc11SVeerendranath Jakkam memcpy(wdev->wext.prev_bssid, connected_addr, ETH_ALEN);
811f401a6f7SJohannes Berg wdev->wext.prev_bssid_valid = true;
812f401a6f7SJohannes Berg }
813e45cd82aSJohannes Berg wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
814e45cd82aSJohannes Berg }
815e45cd82aSJohannes Berg #endif
816e45cd82aSJohannes Berg
817efbabc11SVeerendranath Jakkam if (cr->status == WLAN_STATUS_SUCCESS) {
818baa56dfeSVeerendranath Jakkam if (!wiphy_to_rdev(wdev->wiphy)->ops->connect) {
819efbabc11SVeerendranath Jakkam for_each_valid_link(cr, link) {
820efbabc11SVeerendranath Jakkam if (WARN_ON_ONCE(!cr->links[link].bss))
821efbabc11SVeerendranath Jakkam break;
822efbabc11SVeerendranath Jakkam }
823baa56dfeSVeerendranath Jakkam }
824efbabc11SVeerendranath Jakkam
825efbabc11SVeerendranath Jakkam for_each_valid_link(cr, link) {
82653ad07e9SJohannes Berg /* don't do extra lookups for failures */
82753ad07e9SJohannes Berg if (cr->links[link].status != WLAN_STATUS_SUCCESS)
82853ad07e9SJohannes Berg continue;
82953ad07e9SJohannes Berg
830efbabc11SVeerendranath Jakkam if (cr->links[link].bss)
831efbabc11SVeerendranath Jakkam continue;
832efbabc11SVeerendranath Jakkam
833efbabc11SVeerendranath Jakkam cr->links[link].bss =
834efbabc11SVeerendranath Jakkam cfg80211_get_bss(wdev->wiphy, NULL,
835efbabc11SVeerendranath Jakkam cr->links[link].bssid,
836efbabc11SVeerendranath Jakkam wdev->u.client.ssid,
837efbabc11SVeerendranath Jakkam wdev->u.client.ssid_len,
83834d50519SLior David wdev->conn_bss_type,
8396eb18137SDedy Lansky IEEE80211_PRIVACY_ANY);
840efbabc11SVeerendranath Jakkam if (!cr->links[link].bss) {
841efbabc11SVeerendranath Jakkam bss_not_found = true;
842efbabc11SVeerendranath Jakkam break;
843efbabc11SVeerendranath Jakkam }
844efbabc11SVeerendranath Jakkam cfg80211_hold_bss(bss_from_pub(cr->links[link].bss));
845efbabc11SVeerendranath Jakkam }
8464c4d684aSUjjal Roy }
8474c4d684aSUjjal Roy
8487b0a0e3cSJohannes Berg cfg80211_wdev_release_bsses(wdev);
849df7fc0f9SJohannes Berg
8505349a0f7SVidyullatha Kanchanapally if (cr->status != WLAN_STATUS_SUCCESS) {
851453431a5SWaiman Long kfree_sensitive(wdev->connect_keys);
852fffd0934SJohannes Berg wdev->connect_keys = NULL;
8537b0a0e3cSJohannes Berg wdev->u.client.ssid_len = 0;
854bd2522b1SAndrzej Zaborowski wdev->conn_owner_nlportid = 0;
855efbabc11SVeerendranath Jakkam cfg80211_connect_result_release_bsses(wdev, cr);
856c1fbb258SEliad Peller cfg80211_sme_free(wdev);
857fffd0934SJohannes Berg return;
858fffd0934SJohannes Berg }
859fffd0934SJohannes Berg
860efbabc11SVeerendranath Jakkam if (WARN_ON(bss_not_found)) {
861efbabc11SVeerendranath Jakkam cfg80211_connect_result_release_bsses(wdev, cr);
862b23aa676SSamuel Ortiz return;
863efbabc11SVeerendranath Jakkam }
864f1940c57SJohannes Berg
865efbabc11SVeerendranath Jakkam memset(wdev->links, 0, sizeof(wdev->links));
86653ad07e9SJohannes Berg for_each_valid_link(cr, link) {
86753ad07e9SJohannes Berg if (cr->links[link].status == WLAN_STATUS_SUCCESS)
86853ad07e9SJohannes Berg continue;
86953ad07e9SJohannes Berg cr->valid_links &= ~BIT(link);
87053ad07e9SJohannes Berg /* don't require bss pointer for failed links */
87153ad07e9SJohannes Berg if (!cr->links[link].bss)
87253ad07e9SJohannes Berg continue;
87353ad07e9SJohannes Berg cfg80211_unhold_bss(bss_from_pub(cr->links[link].bss));
87453ad07e9SJohannes Berg cfg80211_put_bss(wdev->wiphy, cr->links[link].bss);
87553ad07e9SJohannes Berg }
876efbabc11SVeerendranath Jakkam wdev->valid_links = cr->valid_links;
877efbabc11SVeerendranath Jakkam for_each_valid_link(cr, link)
878efbabc11SVeerendranath Jakkam wdev->links[link].client.current_bss =
879efbabc11SVeerendranath Jakkam bss_from_pub(cr->links[link].bss);
8807b0a0e3cSJohannes Berg wdev->connected = true;
881efbabc11SVeerendranath Jakkam ether_addr_copy(wdev->u.client.connected_addr, connected_addr);
882efbabc11SVeerendranath Jakkam if (cr->valid_links) {
883efbabc11SVeerendranath Jakkam for_each_valid_link(cr, link)
884efbabc11SVeerendranath Jakkam memcpy(wdev->links[link].addr, cr->links[link].addr,
885efbabc11SVeerendranath Jakkam ETH_ALEN);
886efbabc11SVeerendranath Jakkam }
887b23aa676SSamuel Ortiz
888fffd0934SJohannes Berg cfg80211_upload_connect_keys(wdev);
8898b19e6caSLuis R. Rodriguez
8909caf0364SJohannes Berg rcu_read_lock();
891efbabc11SVeerendranath Jakkam for_each_valid_link(cr, link) {
892efbabc11SVeerendranath Jakkam country_elem =
893efbabc11SVeerendranath Jakkam ieee80211_bss_get_elem(cr->links[link].bss,
894efbabc11SVeerendranath Jakkam WLAN_EID_COUNTRY);
895efbabc11SVeerendranath Jakkam if (country_elem)
896efbabc11SVeerendranath Jakkam break;
897efbabc11SVeerendranath Jakkam }
898fb8b53acSJohannes Berg if (!country_elem) {
8999caf0364SJohannes Berg rcu_read_unlock();
9009caf0364SJohannes Berg return;
9019caf0364SJohannes Berg }
9029caf0364SJohannes Berg
903fb8b53acSJohannes Berg country_datalen = country_elem->datalen;
904fb8b53acSJohannes Berg country_data = kmemdup(country_elem->data, country_datalen, GFP_ATOMIC);
9059caf0364SJohannes Berg rcu_read_unlock();
9068b19e6caSLuis R. Rodriguez
907fb8b53acSJohannes Berg if (!country_data)
9088b19e6caSLuis R. Rodriguez return;
9098b19e6caSLuis R. Rodriguez
910efbabc11SVeerendranath Jakkam regulatory_hint_country_ie(wdev->wiphy,
911efbabc11SVeerendranath Jakkam cr->links[link].bss->channel->band,
912fb8b53acSJohannes Berg country_data, country_datalen);
913fb8b53acSJohannes Berg kfree(country_data);
914efbabc11SVeerendranath Jakkam
915c38c7018SMarc Bornand if (!wdev->u.client.ssid_len) {
916c38c7018SMarc Bornand rcu_read_lock();
917c38c7018SMarc Bornand for_each_valid_link(cr, link) {
918c38c7018SMarc Bornand ssid = ieee80211_bss_get_elem(cr->links[link].bss,
919c38c7018SMarc Bornand WLAN_EID_SSID);
920c38c7018SMarc Bornand
921c38c7018SMarc Bornand if (!ssid || !ssid->datalen)
922c38c7018SMarc Bornand continue;
923c38c7018SMarc Bornand
924c38c7018SMarc Bornand memcpy(wdev->u.client.ssid, ssid->data, ssid->datalen);
925c38c7018SMarc Bornand wdev->u.client.ssid_len = ssid->datalen;
926c38c7018SMarc Bornand break;
927c38c7018SMarc Bornand }
928c38c7018SMarc Bornand rcu_read_unlock();
929c38c7018SMarc Bornand }
930c38c7018SMarc Bornand
931efbabc11SVeerendranath Jakkam return;
932efbabc11SVeerendranath Jakkam out:
933efbabc11SVeerendranath Jakkam for_each_valid_link(cr, link)
934efbabc11SVeerendranath Jakkam cfg80211_put_bss(wdev->wiphy, cr->links[link].bss);
935b23aa676SSamuel Ortiz }
936f2129354SJohannes Berg
cfg80211_update_link_bss(struct wireless_dev * wdev,struct cfg80211_bss ** bss)937efbabc11SVeerendranath Jakkam static void cfg80211_update_link_bss(struct wireless_dev *wdev,
938efbabc11SVeerendranath Jakkam struct cfg80211_bss **bss)
939f2129354SJohannes Berg {
940f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
941efbabc11SVeerendranath Jakkam struct cfg80211_internal_bss *ibss;
942667503ddSJohannes Berg
943efbabc11SVeerendranath Jakkam if (!*bss)
944efbabc11SVeerendranath Jakkam return;
945e7054989SKanchanapally, Vidyullatha
946efbabc11SVeerendranath Jakkam ibss = bss_from_pub(*bss);
947a3ce17d1SChaitanya Tata if (list_empty(&ibss->list)) {
948efbabc11SVeerendranath Jakkam struct cfg80211_bss *found = NULL, *tmp = *bss;
949a3ce17d1SChaitanya Tata
950a3ce17d1SChaitanya Tata found = cfg80211_get_bss(wdev->wiphy, NULL,
951efbabc11SVeerendranath Jakkam (*bss)->bssid,
952efbabc11SVeerendranath Jakkam wdev->u.client.ssid,
953efbabc11SVeerendranath Jakkam wdev->u.client.ssid_len,
954a3ce17d1SChaitanya Tata wdev->conn_bss_type,
955a3ce17d1SChaitanya Tata IEEE80211_PRIVACY_ANY);
956a3ce17d1SChaitanya Tata if (found) {
957a3ce17d1SChaitanya Tata /* The same BSS is already updated so use it
958a3ce17d1SChaitanya Tata * instead, as it has latest info.
959a3ce17d1SChaitanya Tata */
960efbabc11SVeerendranath Jakkam *bss = found;
961a3ce17d1SChaitanya Tata } else {
962a3ce17d1SChaitanya Tata /* Update with BSS provided by driver, it will
963a3ce17d1SChaitanya Tata * be freshly added and ref cnted, we can free
964a3ce17d1SChaitanya Tata * the old one.
965a3ce17d1SChaitanya Tata *
966a3ce17d1SChaitanya Tata * signal_valid can be false, as we are not
967a3ce17d1SChaitanya Tata * expecting the BSS to be found.
968a3ce17d1SChaitanya Tata *
969a3ce17d1SChaitanya Tata * keep the old timestamp to avoid confusion
970a3ce17d1SChaitanya Tata */
971a3ce17d1SChaitanya Tata cfg80211_bss_update(rdev, ibss, false,
972a3ce17d1SChaitanya Tata ibss->ts);
973a3ce17d1SChaitanya Tata }
974a3ce17d1SChaitanya Tata
975a3ce17d1SChaitanya Tata cfg80211_put_bss(wdev->wiphy, tmp);
976e7054989SKanchanapally, Vidyullatha }
977e7054989SKanchanapally, Vidyullatha }
978e7054989SKanchanapally, Vidyullatha
979efbabc11SVeerendranath Jakkam /* Consumes bss object(s) one way or another */
cfg80211_connect_done(struct net_device * dev,struct cfg80211_connect_resp_params * params,gfp_t gfp)980efbabc11SVeerendranath Jakkam void cfg80211_connect_done(struct net_device *dev,
981efbabc11SVeerendranath Jakkam struct cfg80211_connect_resp_params *params,
982efbabc11SVeerendranath Jakkam gfp_t gfp)
983efbabc11SVeerendranath Jakkam {
984efbabc11SVeerendranath Jakkam struct wireless_dev *wdev = dev->ieee80211_ptr;
985efbabc11SVeerendranath Jakkam struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
986efbabc11SVeerendranath Jakkam struct cfg80211_event *ev;
987efbabc11SVeerendranath Jakkam unsigned long flags;
988efbabc11SVeerendranath Jakkam u8 *next;
989efbabc11SVeerendranath Jakkam size_t link_info_size = 0;
990efbabc11SVeerendranath Jakkam unsigned int link;
991efbabc11SVeerendranath Jakkam
992efbabc11SVeerendranath Jakkam for_each_valid_link(params, link) {
993efbabc11SVeerendranath Jakkam cfg80211_update_link_bss(wdev, ¶ms->links[link].bss);
994efbabc11SVeerendranath Jakkam link_info_size += params->links[link].bssid ? ETH_ALEN : 0;
995efbabc11SVeerendranath Jakkam link_info_size += params->links[link].addr ? ETH_ALEN : 0;
996efbabc11SVeerendranath Jakkam }
997efbabc11SVeerendranath Jakkam
998efbabc11SVeerendranath Jakkam ev = kzalloc(sizeof(*ev) + (params->ap_mld_addr ? ETH_ALEN : 0) +
999a3caf744SVidyullatha Kanchanapally params->req_ie_len + params->resp_ie_len +
100076804d28SArend Van Spriel params->fils.kek_len + params->fils.pmk_len +
1001efbabc11SVeerendranath Jakkam (params->fils.pmkid ? WLAN_PMKID_LEN : 0) + link_info_size,
1002efbabc11SVeerendranath Jakkam gfp);
1003efbabc11SVeerendranath Jakkam
1004e7054989SKanchanapally, Vidyullatha if (!ev) {
1005efbabc11SVeerendranath Jakkam for_each_valid_link(params, link)
1006efbabc11SVeerendranath Jakkam cfg80211_put_bss(wdev->wiphy,
1007efbabc11SVeerendranath Jakkam params->links[link].bss);
1008e7054989SKanchanapally, Vidyullatha return;
1009e7054989SKanchanapally, Vidyullatha }
1010667503ddSJohannes Berg
1011667503ddSJohannes Berg ev->type = EVENT_CONNECT_RESULT;
10125349a0f7SVidyullatha Kanchanapally next = ((u8 *)ev) + sizeof(*ev);
1013efbabc11SVeerendranath Jakkam if (params->ap_mld_addr) {
1014efbabc11SVeerendranath Jakkam ev->cr.ap_mld_addr = next;
1015efbabc11SVeerendranath Jakkam memcpy((void *)ev->cr.ap_mld_addr, params->ap_mld_addr,
1016efbabc11SVeerendranath Jakkam ETH_ALEN);
10175349a0f7SVidyullatha Kanchanapally next += ETH_ALEN;
10187834704bSNishant Sarmukadam }
10195349a0f7SVidyullatha Kanchanapally if (params->req_ie_len) {
10205349a0f7SVidyullatha Kanchanapally ev->cr.req_ie = next;
10215349a0f7SVidyullatha Kanchanapally ev->cr.req_ie_len = params->req_ie_len;
10225349a0f7SVidyullatha Kanchanapally memcpy((void *)ev->cr.req_ie, params->req_ie,
10235349a0f7SVidyullatha Kanchanapally params->req_ie_len);
10245349a0f7SVidyullatha Kanchanapally next += params->req_ie_len;
10257834704bSNishant Sarmukadam }
10265349a0f7SVidyullatha Kanchanapally if (params->resp_ie_len) {
10275349a0f7SVidyullatha Kanchanapally ev->cr.resp_ie = next;
10285349a0f7SVidyullatha Kanchanapally ev->cr.resp_ie_len = params->resp_ie_len;
10295349a0f7SVidyullatha Kanchanapally memcpy((void *)ev->cr.resp_ie, params->resp_ie,
10305349a0f7SVidyullatha Kanchanapally params->resp_ie_len);
10315349a0f7SVidyullatha Kanchanapally next += params->resp_ie_len;
10325349a0f7SVidyullatha Kanchanapally }
103376804d28SArend Van Spriel if (params->fils.kek_len) {
103476804d28SArend Van Spriel ev->cr.fils.kek = next;
103576804d28SArend Van Spriel ev->cr.fils.kek_len = params->fils.kek_len;
103676804d28SArend Van Spriel memcpy((void *)ev->cr.fils.kek, params->fils.kek,
103776804d28SArend Van Spriel params->fils.kek_len);
103876804d28SArend Van Spriel next += params->fils.kek_len;
1039a3caf744SVidyullatha Kanchanapally }
104076804d28SArend Van Spriel if (params->fils.pmk_len) {
104176804d28SArend Van Spriel ev->cr.fils.pmk = next;
104276804d28SArend Van Spriel ev->cr.fils.pmk_len = params->fils.pmk_len;
104376804d28SArend Van Spriel memcpy((void *)ev->cr.fils.pmk, params->fils.pmk,
104476804d28SArend Van Spriel params->fils.pmk_len);
104576804d28SArend Van Spriel next += params->fils.pmk_len;
1046a3caf744SVidyullatha Kanchanapally }
104776804d28SArend Van Spriel if (params->fils.pmkid) {
104876804d28SArend Van Spriel ev->cr.fils.pmkid = next;
104976804d28SArend Van Spriel memcpy((void *)ev->cr.fils.pmkid, params->fils.pmkid,
105076804d28SArend Van Spriel WLAN_PMKID_LEN);
1051a3caf744SVidyullatha Kanchanapally next += WLAN_PMKID_LEN;
1052a3caf744SVidyullatha Kanchanapally }
105376804d28SArend Van Spriel ev->cr.fils.update_erp_next_seq_num = params->fils.update_erp_next_seq_num;
105476804d28SArend Van Spriel if (params->fils.update_erp_next_seq_num)
105576804d28SArend Van Spriel ev->cr.fils.erp_next_seq_num = params->fils.erp_next_seq_num;
1056efbabc11SVeerendranath Jakkam ev->cr.valid_links = params->valid_links;
1057efbabc11SVeerendranath Jakkam for_each_valid_link(params, link) {
1058efbabc11SVeerendranath Jakkam if (params->links[link].bss)
1059efbabc11SVeerendranath Jakkam cfg80211_hold_bss(
1060efbabc11SVeerendranath Jakkam bss_from_pub(params->links[link].bss));
1061efbabc11SVeerendranath Jakkam ev->cr.links[link].bss = params->links[link].bss;
1062fcfb8ceaSVeerendranath Jakkam ev->cr.links[link].status = params->links[link].status;
1063efbabc11SVeerendranath Jakkam
1064efbabc11SVeerendranath Jakkam if (params->links[link].addr) {
1065efbabc11SVeerendranath Jakkam ev->cr.links[link].addr = next;
1066efbabc11SVeerendranath Jakkam memcpy((void *)ev->cr.links[link].addr,
1067efbabc11SVeerendranath Jakkam params->links[link].addr,
1068efbabc11SVeerendranath Jakkam ETH_ALEN);
1069efbabc11SVeerendranath Jakkam next += ETH_ALEN;
1070efbabc11SVeerendranath Jakkam }
1071efbabc11SVeerendranath Jakkam if (params->links[link].bssid) {
1072efbabc11SVeerendranath Jakkam ev->cr.links[link].bssid = next;
1073efbabc11SVeerendranath Jakkam memcpy((void *)ev->cr.links[link].bssid,
1074efbabc11SVeerendranath Jakkam params->links[link].bssid,
1075efbabc11SVeerendranath Jakkam ETH_ALEN);
1076efbabc11SVeerendranath Jakkam next += ETH_ALEN;
1077efbabc11SVeerendranath Jakkam }
1078efbabc11SVeerendranath Jakkam }
10795349a0f7SVidyullatha Kanchanapally ev->cr.status = params->status;
10805349a0f7SVidyullatha Kanchanapally ev->cr.timeout_reason = params->timeout_reason;
1081667503ddSJohannes Berg
1082667503ddSJohannes Berg spin_lock_irqsave(&wdev->event_lock, flags);
1083667503ddSJohannes Berg list_add_tail(&ev->list, &wdev->event_list);
1084667503ddSJohannes Berg spin_unlock_irqrestore(&wdev->event_lock, flags);
1085e60d7443SAlban Browaeys queue_work(cfg80211_wq, &rdev->event_work);
1086f2129354SJohannes Berg }
10875349a0f7SVidyullatha Kanchanapally EXPORT_SYMBOL(cfg80211_connect_done);
1088b23aa676SSamuel Ortiz
10890e3a39b5SBen Greear /* Consumes bss object one way or another */
__cfg80211_roamed(struct wireless_dev * wdev,struct cfg80211_roam_info * info)1090ed9d0102SJouni Malinen void __cfg80211_roamed(struct wireless_dev *wdev,
109129ce6ecbSAvraham Stern struct cfg80211_roam_info *info)
1092b23aa676SSamuel Ortiz {
10933d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
1094b23aa676SSamuel Ortiz union iwreq_data wrqu;
1095b23aa676SSamuel Ortiz #endif
1096efbabc11SVeerendranath Jakkam unsigned int link;
1097efbabc11SVeerendranath Jakkam const u8 *connected_addr;
1098efbabc11SVeerendranath Jakkam
1099667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev);
1100667503ddSJohannes Berg
1101074ac8dfSJohannes Berg if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
1102074ac8dfSJohannes Berg wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
1103adbde344SVasanthakumar Thiagarajan goto out;
1104b23aa676SSamuel Ortiz
11057b0a0e3cSJohannes Berg if (WARN_ON(!wdev->connected))
1106adbde344SVasanthakumar Thiagarajan goto out;
1107b23aa676SSamuel Ortiz
1108efbabc11SVeerendranath Jakkam if (info->valid_links) {
1109efbabc11SVeerendranath Jakkam if (WARN_ON(!info->ap_mld_addr))
1110efbabc11SVeerendranath Jakkam goto out;
1111efbabc11SVeerendranath Jakkam
1112efbabc11SVeerendranath Jakkam for_each_valid_link(info, link) {
1113efbabc11SVeerendranath Jakkam if (WARN_ON(!info->links[link].addr))
1114efbabc11SVeerendranath Jakkam goto out;
1115efbabc11SVeerendranath Jakkam }
1116efbabc11SVeerendranath Jakkam }
1117efbabc11SVeerendranath Jakkam
11187b0a0e3cSJohannes Berg cfg80211_wdev_release_bsses(wdev);
1119b23aa676SSamuel Ortiz
1120efbabc11SVeerendranath Jakkam for_each_valid_link(info, link) {
1121efbabc11SVeerendranath Jakkam if (WARN_ON(!info->links[link].bss))
1122efbabc11SVeerendranath Jakkam goto out;
1123efbabc11SVeerendranath Jakkam }
112429ce6ecbSAvraham Stern
1125efbabc11SVeerendranath Jakkam memset(wdev->links, 0, sizeof(wdev->links));
1126efbabc11SVeerendranath Jakkam wdev->valid_links = info->valid_links;
1127efbabc11SVeerendranath Jakkam for_each_valid_link(info, link) {
1128efbabc11SVeerendranath Jakkam cfg80211_hold_bss(bss_from_pub(info->links[link].bss));
1129efbabc11SVeerendranath Jakkam wdev->links[link].client.current_bss =
1130efbabc11SVeerendranath Jakkam bss_from_pub(info->links[link].bss);
1131efbabc11SVeerendranath Jakkam }
1132b23aa676SSamuel Ortiz
1133efbabc11SVeerendranath Jakkam connected_addr = info->valid_links ?
1134efbabc11SVeerendranath Jakkam info->ap_mld_addr :
1135efbabc11SVeerendranath Jakkam info->links[0].bss->bssid;
1136efbabc11SVeerendranath Jakkam ether_addr_copy(wdev->u.client.connected_addr, connected_addr);
1137efbabc11SVeerendranath Jakkam if (info->valid_links) {
1138efbabc11SVeerendranath Jakkam for_each_valid_link(info, link)
1139efbabc11SVeerendranath Jakkam memcpy(wdev->links[link].addr, info->links[link].addr,
1140efbabc11SVeerendranath Jakkam ETH_ALEN);
1141efbabc11SVeerendranath Jakkam }
11424d797fceSJouni Malinen wdev->unprot_beacon_reported = 0;
1143f26cbf40SZhao, Gang nl80211_send_roamed(wiphy_to_rdev(wdev->wiphy),
114429ce6ecbSAvraham Stern wdev->netdev, info, GFP_KERNEL);
1145b23aa676SSamuel Ortiz
11463d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
1147efbabc11SVeerendranath Jakkam if (!info->valid_links) {
114829ce6ecbSAvraham Stern if (info->req_ie) {
1149b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu));
115029ce6ecbSAvraham Stern wrqu.data.length = info->req_ie_len;
11513409ff77SZhu Yi wireless_send_event(wdev->netdev, IWEVASSOCREQIE,
115229ce6ecbSAvraham Stern &wrqu, info->req_ie);
1153b23aa676SSamuel Ortiz }
1154b23aa676SSamuel Ortiz
115529ce6ecbSAvraham Stern if (info->resp_ie) {
1156b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu));
115729ce6ecbSAvraham Stern wrqu.data.length = info->resp_ie_len;
1158667503ddSJohannes Berg wireless_send_event(wdev->netdev, IWEVASSOCRESPIE,
115929ce6ecbSAvraham Stern &wrqu, info->resp_ie);
1160b23aa676SSamuel Ortiz }
1161b23aa676SSamuel Ortiz
1162b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu));
1163b23aa676SSamuel Ortiz wrqu.ap_addr.sa_family = ARPHRD_ETHER;
1164efbabc11SVeerendranath Jakkam memcpy(wrqu.ap_addr.sa_data, connected_addr, ETH_ALEN);
1165efbabc11SVeerendranath Jakkam memcpy(wdev->wext.prev_bssid, connected_addr, ETH_ALEN);
1166f401a6f7SJohannes Berg wdev->wext.prev_bssid_valid = true;
1167667503ddSJohannes Berg wireless_send_event(wdev->netdev, SIOCGIWAP, &wrqu, NULL);
1168efbabc11SVeerendranath Jakkam }
1169b23aa676SSamuel Ortiz #endif
1170adbde344SVasanthakumar Thiagarajan
1171adbde344SVasanthakumar Thiagarajan return;
1172adbde344SVasanthakumar Thiagarajan out:
1173efbabc11SVeerendranath Jakkam for_each_valid_link(info, link)
1174efbabc11SVeerendranath Jakkam cfg80211_put_bss(wdev->wiphy, info->links[link].bss);
1175b23aa676SSamuel Ortiz }
1176667503ddSJohannes Berg
1177efbabc11SVeerendranath Jakkam /* Consumes info->links.bss object(s) one way or another */
cfg80211_roamed(struct net_device * dev,struct cfg80211_roam_info * info,gfp_t gfp)117829ce6ecbSAvraham Stern void cfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info,
117929ce6ecbSAvraham Stern gfp_t gfp)
1180adbde344SVasanthakumar Thiagarajan {
1181adbde344SVasanthakumar Thiagarajan struct wireless_dev *wdev = dev->ieee80211_ptr;
1182f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
1183667503ddSJohannes Berg struct cfg80211_event *ev;
1184667503ddSJohannes Berg unsigned long flags;
1185e841b7b1SArend Van Spriel u8 *next;
1186efbabc11SVeerendranath Jakkam unsigned int link;
1187efbabc11SVeerendranath Jakkam size_t link_info_size = 0;
1188efbabc11SVeerendranath Jakkam bool bss_not_found = false;
1189667503ddSJohannes Berg
1190efbabc11SVeerendranath Jakkam for_each_valid_link(info, link) {
1191efbabc11SVeerendranath Jakkam link_info_size += info->links[link].addr ? ETH_ALEN : 0;
1192efbabc11SVeerendranath Jakkam link_info_size += info->links[link].bssid ? ETH_ALEN : 0;
1193efbabc11SVeerendranath Jakkam
1194efbabc11SVeerendranath Jakkam if (info->links[link].bss)
1195efbabc11SVeerendranath Jakkam continue;
1196efbabc11SVeerendranath Jakkam
1197efbabc11SVeerendranath Jakkam info->links[link].bss =
1198efbabc11SVeerendranath Jakkam cfg80211_get_bss(wdev->wiphy,
1199efbabc11SVeerendranath Jakkam info->links[link].channel,
1200efbabc11SVeerendranath Jakkam info->links[link].bssid,
1201efbabc11SVeerendranath Jakkam wdev->u.client.ssid,
12027b0a0e3cSJohannes Berg wdev->u.client.ssid_len,
120329ce6ecbSAvraham Stern wdev->conn_bss_type,
120429ce6ecbSAvraham Stern IEEE80211_PRIVACY_ANY);
1205efbabc11SVeerendranath Jakkam
1206efbabc11SVeerendranath Jakkam if (!info->links[link].bss) {
1207efbabc11SVeerendranath Jakkam bss_not_found = true;
1208efbabc11SVeerendranath Jakkam break;
1209efbabc11SVeerendranath Jakkam }
121029ce6ecbSAvraham Stern }
121129ce6ecbSAvraham Stern
1212efbabc11SVeerendranath Jakkam if (WARN_ON(bss_not_found))
1213efbabc11SVeerendranath Jakkam goto out;
1214667503ddSJohannes Berg
1215e841b7b1SArend Van Spriel ev = kzalloc(sizeof(*ev) + info->req_ie_len + info->resp_ie_len +
1216e841b7b1SArend Van Spriel info->fils.kek_len + info->fils.pmk_len +
1217efbabc11SVeerendranath Jakkam (info->fils.pmkid ? WLAN_PMKID_LEN : 0) +
1218efbabc11SVeerendranath Jakkam (info->ap_mld_addr ? ETH_ALEN : 0) + link_info_size, gfp);
1219efbabc11SVeerendranath Jakkam if (!ev)
1220efbabc11SVeerendranath Jakkam goto out;
1221adbde344SVasanthakumar Thiagarajan
1222667503ddSJohannes Berg ev->type = EVENT_ROAMED;
1223e841b7b1SArend Van Spriel next = ((u8 *)ev) + sizeof(*ev);
1224e841b7b1SArend Van Spriel if (info->req_ie_len) {
1225e841b7b1SArend Van Spriel ev->rm.req_ie = next;
122629ce6ecbSAvraham Stern ev->rm.req_ie_len = info->req_ie_len;
122729ce6ecbSAvraham Stern memcpy((void *)ev->rm.req_ie, info->req_ie, info->req_ie_len);
1228e841b7b1SArend Van Spriel next += info->req_ie_len;
1229e841b7b1SArend Van Spriel }
1230e841b7b1SArend Van Spriel if (info->resp_ie_len) {
1231e841b7b1SArend Van Spriel ev->rm.resp_ie = next;
123229ce6ecbSAvraham Stern ev->rm.resp_ie_len = info->resp_ie_len;
1233e841b7b1SArend Van Spriel memcpy((void *)ev->rm.resp_ie, info->resp_ie,
1234e841b7b1SArend Van Spriel info->resp_ie_len);
1235e841b7b1SArend Van Spriel next += info->resp_ie_len;
1236e841b7b1SArend Van Spriel }
1237e841b7b1SArend Van Spriel if (info->fils.kek_len) {
1238e841b7b1SArend Van Spriel ev->rm.fils.kek = next;
1239e841b7b1SArend Van Spriel ev->rm.fils.kek_len = info->fils.kek_len;
1240e841b7b1SArend Van Spriel memcpy((void *)ev->rm.fils.kek, info->fils.kek,
1241e841b7b1SArend Van Spriel info->fils.kek_len);
1242e841b7b1SArend Van Spriel next += info->fils.kek_len;
1243e841b7b1SArend Van Spriel }
1244e841b7b1SArend Van Spriel if (info->fils.pmk_len) {
1245e841b7b1SArend Van Spriel ev->rm.fils.pmk = next;
1246e841b7b1SArend Van Spriel ev->rm.fils.pmk_len = info->fils.pmk_len;
1247e841b7b1SArend Van Spriel memcpy((void *)ev->rm.fils.pmk, info->fils.pmk,
1248e841b7b1SArend Van Spriel info->fils.pmk_len);
1249e841b7b1SArend Van Spriel next += info->fils.pmk_len;
1250e841b7b1SArend Van Spriel }
1251e841b7b1SArend Van Spriel if (info->fils.pmkid) {
1252e841b7b1SArend Van Spriel ev->rm.fils.pmkid = next;
1253e841b7b1SArend Van Spriel memcpy((void *)ev->rm.fils.pmkid, info->fils.pmkid,
1254e841b7b1SArend Van Spriel WLAN_PMKID_LEN);
1255e841b7b1SArend Van Spriel next += WLAN_PMKID_LEN;
1256e841b7b1SArend Van Spriel }
1257e841b7b1SArend Van Spriel ev->rm.fils.update_erp_next_seq_num = info->fils.update_erp_next_seq_num;
1258e841b7b1SArend Van Spriel if (info->fils.update_erp_next_seq_num)
1259e841b7b1SArend Van Spriel ev->rm.fils.erp_next_seq_num = info->fils.erp_next_seq_num;
1260efbabc11SVeerendranath Jakkam if (info->ap_mld_addr) {
1261efbabc11SVeerendranath Jakkam ev->rm.ap_mld_addr = next;
1262efbabc11SVeerendranath Jakkam memcpy((void *)ev->rm.ap_mld_addr, info->ap_mld_addr,
1263efbabc11SVeerendranath Jakkam ETH_ALEN);
1264efbabc11SVeerendranath Jakkam next += ETH_ALEN;
1265efbabc11SVeerendranath Jakkam }
1266efbabc11SVeerendranath Jakkam ev->rm.valid_links = info->valid_links;
1267efbabc11SVeerendranath Jakkam for_each_valid_link(info, link) {
1268efbabc11SVeerendranath Jakkam ev->rm.links[link].bss = info->links[link].bss;
1269efbabc11SVeerendranath Jakkam
1270efbabc11SVeerendranath Jakkam if (info->links[link].addr) {
1271efbabc11SVeerendranath Jakkam ev->rm.links[link].addr = next;
1272efbabc11SVeerendranath Jakkam memcpy((void *)ev->rm.links[link].addr,
1273efbabc11SVeerendranath Jakkam info->links[link].addr,
1274efbabc11SVeerendranath Jakkam ETH_ALEN);
1275efbabc11SVeerendranath Jakkam next += ETH_ALEN;
1276efbabc11SVeerendranath Jakkam }
1277efbabc11SVeerendranath Jakkam
1278efbabc11SVeerendranath Jakkam if (info->links[link].bssid) {
1279efbabc11SVeerendranath Jakkam ev->rm.links[link].bssid = next;
1280efbabc11SVeerendranath Jakkam memcpy((void *)ev->rm.links[link].bssid,
1281efbabc11SVeerendranath Jakkam info->links[link].bssid,
1282efbabc11SVeerendranath Jakkam ETH_ALEN);
1283efbabc11SVeerendranath Jakkam next += ETH_ALEN;
1284efbabc11SVeerendranath Jakkam }
1285efbabc11SVeerendranath Jakkam }
1286667503ddSJohannes Berg
1287667503ddSJohannes Berg spin_lock_irqsave(&wdev->event_lock, flags);
1288667503ddSJohannes Berg list_add_tail(&ev->list, &wdev->event_list);
1289667503ddSJohannes Berg spin_unlock_irqrestore(&wdev->event_lock, flags);
1290e60d7443SAlban Browaeys queue_work(cfg80211_wq, &rdev->event_work);
1291efbabc11SVeerendranath Jakkam
1292efbabc11SVeerendranath Jakkam return;
1293efbabc11SVeerendranath Jakkam out:
1294efbabc11SVeerendranath Jakkam for_each_valid_link(info, link)
1295efbabc11SVeerendranath Jakkam cfg80211_put_bss(wdev->wiphy, info->links[link].bss);
1296efbabc11SVeerendranath Jakkam
1297667503ddSJohannes Berg }
129829ce6ecbSAvraham Stern EXPORT_SYMBOL(cfg80211_roamed);
1299b23aa676SSamuel Ortiz
__cfg80211_port_authorized(struct wireless_dev * wdev,const u8 * bssid,const u8 * td_bitmap,u8 td_bitmap_len)13000ff57171SVinayak Yadawad void __cfg80211_port_authorized(struct wireless_dev *wdev, const u8 *bssid,
13010ff57171SVinayak Yadawad const u8 *td_bitmap, u8 td_bitmap_len)
1302503c1fb9SAvraham Stern {
1303503c1fb9SAvraham Stern ASSERT_WDEV_LOCK(wdev);
1304503c1fb9SAvraham Stern
13058d70f33eSVinayak Yadawad if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
13068d70f33eSVinayak Yadawad wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
1307503c1fb9SAvraham Stern return;
1308503c1fb9SAvraham Stern
13097b0a0e3cSJohannes Berg if (WARN_ON(!wdev->connected) ||
13107b0a0e3cSJohannes Berg WARN_ON(!ether_addr_equal(wdev->u.client.connected_addr, bssid)))
1311503c1fb9SAvraham Stern return;
1312503c1fb9SAvraham Stern
1313503c1fb9SAvraham Stern nl80211_send_port_authorized(wiphy_to_rdev(wdev->wiphy), wdev->netdev,
13140ff57171SVinayak Yadawad bssid, td_bitmap, td_bitmap_len);
1315503c1fb9SAvraham Stern }
1316503c1fb9SAvraham Stern
cfg80211_port_authorized(struct net_device * dev,const u8 * bssid,const u8 * td_bitmap,u8 td_bitmap_len,gfp_t gfp)1317503c1fb9SAvraham Stern void cfg80211_port_authorized(struct net_device *dev, const u8 *bssid,
13180ff57171SVinayak Yadawad const u8 *td_bitmap, u8 td_bitmap_len, gfp_t gfp)
1319503c1fb9SAvraham Stern {
1320503c1fb9SAvraham Stern struct wireless_dev *wdev = dev->ieee80211_ptr;
1321503c1fb9SAvraham Stern struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
1322503c1fb9SAvraham Stern struct cfg80211_event *ev;
1323503c1fb9SAvraham Stern unsigned long flags;
1324503c1fb9SAvraham Stern
1325503c1fb9SAvraham Stern if (WARN_ON(!bssid))
1326503c1fb9SAvraham Stern return;
1327503c1fb9SAvraham Stern
13280ff57171SVinayak Yadawad ev = kzalloc(sizeof(*ev) + td_bitmap_len, gfp);
1329503c1fb9SAvraham Stern if (!ev)
1330503c1fb9SAvraham Stern return;
1331503c1fb9SAvraham Stern
1332503c1fb9SAvraham Stern ev->type = EVENT_PORT_AUTHORIZED;
1333503c1fb9SAvraham Stern memcpy(ev->pa.bssid, bssid, ETH_ALEN);
13340ff57171SVinayak Yadawad ev->pa.td_bitmap = ((u8 *)ev) + sizeof(*ev);
13350ff57171SVinayak Yadawad ev->pa.td_bitmap_len = td_bitmap_len;
13360ff57171SVinayak Yadawad memcpy((void *)ev->pa.td_bitmap, td_bitmap, td_bitmap_len);
1337503c1fb9SAvraham Stern
1338503c1fb9SAvraham Stern /*
1339503c1fb9SAvraham Stern * Use the wdev event list so that if there are pending
1340503c1fb9SAvraham Stern * connected/roamed events, they will be reported first.
1341503c1fb9SAvraham Stern */
1342503c1fb9SAvraham Stern spin_lock_irqsave(&wdev->event_lock, flags);
1343503c1fb9SAvraham Stern list_add_tail(&ev->list, &wdev->event_list);
1344503c1fb9SAvraham Stern spin_unlock_irqrestore(&wdev->event_lock, flags);
1345503c1fb9SAvraham Stern queue_work(cfg80211_wq, &rdev->event_work);
1346503c1fb9SAvraham Stern }
1347503c1fb9SAvraham Stern EXPORT_SYMBOL(cfg80211_port_authorized);
1348503c1fb9SAvraham Stern
__cfg80211_disconnected(struct net_device * dev,const u8 * ie,size_t ie_len,u16 reason,bool from_ap)1349667503ddSJohannes Berg void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
13506829c878SJohannes Berg size_t ie_len, u16 reason, bool from_ap)
1351b23aa676SSamuel Ortiz {
1352b23aa676SSamuel Ortiz struct wireless_dev *wdev = dev->ieee80211_ptr;
1353f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
1354fffd0934SJohannes Berg int i;
13553d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
1356b23aa676SSamuel Ortiz union iwreq_data wrqu;
1357b23aa676SSamuel Ortiz #endif
1358b23aa676SSamuel Ortiz
1359667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev);
1360667503ddSJohannes Berg
1361074ac8dfSJohannes Berg if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION &&
1362074ac8dfSJohannes Berg wdev->iftype != NL80211_IFTYPE_P2P_CLIENT))
1363b23aa676SSamuel Ortiz return;
1364b23aa676SSamuel Ortiz
13657b0a0e3cSJohannes Berg cfg80211_wdev_release_bsses(wdev);
13667b0a0e3cSJohannes Berg wdev->connected = false;
13677b0a0e3cSJohannes Berg wdev->u.client.ssid_len = 0;
1368bd2522b1SAndrzej Zaborowski wdev->conn_owner_nlportid = 0;
1369453431a5SWaiman Long kfree_sensitive(wdev->connect_keys);
13703027a8e7SAvraham Stern wdev->connect_keys = NULL;
1371b23aa676SSamuel Ortiz
1372fffd0934SJohannes Berg nl80211_send_disconnected(rdev, dev, reason, ie, ie_len, from_ap);
1373fffd0934SJohannes Berg
1374b8607152SArend van Spriel /* stop critical protocol if supported */
1375b8607152SArend van Spriel if (rdev->ops->crit_proto_stop && rdev->crit_proto_nlportid) {
1376b8607152SArend van Spriel rdev->crit_proto_nlportid = 0;
1377b8607152SArend van Spriel rdev_crit_proto_stop(rdev, wdev);
1378b8607152SArend van Spriel }
1379b8607152SArend van Spriel
1380fffd0934SJohannes Berg /*
1381fffd0934SJohannes Berg * Delete all the keys ... pairwise keys can't really
1382fffd0934SJohannes Berg * exist any more anyway, but default keys might.
1383fffd0934SJohannes Berg */
138456be393fSJouni Malinen if (rdev->ops->del_key) {
138556be393fSJouni Malinen int max_key_idx = 5;
138656be393fSJouni Malinen
138756be393fSJouni Malinen if (wiphy_ext_feature_isset(
138856be393fSJouni Malinen wdev->wiphy,
13890e47901dSJohannes Berg NL80211_EXT_FEATURE_BEACON_PROTECTION) ||
13900e47901dSJohannes Berg wiphy_ext_feature_isset(
13910e47901dSJohannes Berg wdev->wiphy,
13920e47901dSJohannes Berg NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT))
139356be393fSJouni Malinen max_key_idx = 7;
139456be393fSJouni Malinen for (i = 0; i <= max_key_idx; i++)
1395e7a7b84eSVeerendranath Jakkam rdev_del_key(rdev, dev, -1, i, false, NULL);
139656be393fSJouni Malinen }
1397b23aa676SSamuel Ortiz
1398fa9ffc74SKyeyoon Park rdev_set_qos_map(rdev, dev, NULL);
1399fa9ffc74SKyeyoon Park
14003d23e349SJohannes Berg #ifdef CONFIG_CFG80211_WEXT
1401b23aa676SSamuel Ortiz memset(&wrqu, 0, sizeof(wrqu));
1402b23aa676SSamuel Ortiz wrqu.ap_addr.sa_family = ARPHRD_ETHER;
1403b23aa676SSamuel Ortiz wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
14045f612033SAbhijeet Kolekar wdev->wext.connect.ssid_len = 0;
1405b23aa676SSamuel Ortiz #endif
140609d989d1SLuis R. Rodriguez
140709d989d1SLuis R. Rodriguez schedule_work(&cfg80211_disconnect_work);
1408b23aa676SSamuel Ortiz }
1409b23aa676SSamuel Ortiz
cfg80211_disconnected(struct net_device * dev,u16 reason,const u8 * ie,size_t ie_len,bool locally_generated,gfp_t gfp)1410b23aa676SSamuel Ortiz void cfg80211_disconnected(struct net_device *dev, u16 reason,
141180279fb7SJohannes Berg const u8 *ie, size_t ie_len,
141280279fb7SJohannes Berg bool locally_generated, gfp_t gfp)
1413b23aa676SSamuel Ortiz {
1414667503ddSJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr;
1415f26cbf40SZhao, Gang struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
1416667503ddSJohannes Berg struct cfg80211_event *ev;
1417667503ddSJohannes Berg unsigned long flags;
1418667503ddSJohannes Berg
1419667503ddSJohannes Berg ev = kzalloc(sizeof(*ev) + ie_len, gfp);
1420667503ddSJohannes Berg if (!ev)
1421667503ddSJohannes Berg return;
1422667503ddSJohannes Berg
1423667503ddSJohannes Berg ev->type = EVENT_DISCONNECTED;
1424667503ddSJohannes Berg ev->dc.ie = ((u8 *)ev) + sizeof(*ev);
1425667503ddSJohannes Berg ev->dc.ie_len = ie_len;
1426667503ddSJohannes Berg memcpy((void *)ev->dc.ie, ie, ie_len);
1427667503ddSJohannes Berg ev->dc.reason = reason;
142880279fb7SJohannes Berg ev->dc.locally_generated = locally_generated;
1429667503ddSJohannes Berg
1430667503ddSJohannes Berg spin_lock_irqsave(&wdev->event_lock, flags);
1431667503ddSJohannes Berg list_add_tail(&ev->list, &wdev->event_list);
1432667503ddSJohannes Berg spin_unlock_irqrestore(&wdev->event_lock, flags);
1433e60d7443SAlban Browaeys queue_work(cfg80211_wq, &rdev->event_work);
1434b23aa676SSamuel Ortiz }
1435b23aa676SSamuel Ortiz EXPORT_SYMBOL(cfg80211_disconnected);
1436b23aa676SSamuel Ortiz
1437ceca7b71SJohannes Berg /*
1438ceca7b71SJohannes Berg * API calls for nl80211/wext compatibility code
1439ceca7b71SJohannes Berg */
cfg80211_connect(struct cfg80211_registered_device * rdev,struct net_device * dev,struct cfg80211_connect_params * connect,struct cfg80211_cached_keys * connkeys,const u8 * prev_bssid)144083739b03SJohannes Berg int cfg80211_connect(struct cfg80211_registered_device *rdev,
1441b23aa676SSamuel Ortiz struct net_device *dev,
1442fffd0934SJohannes Berg struct cfg80211_connect_params *connect,
1443f401a6f7SJohannes Berg struct cfg80211_cached_keys *connkeys,
1444f401a6f7SJohannes Berg const u8 *prev_bssid)
1445b23aa676SSamuel Ortiz {
1446b23aa676SSamuel Ortiz struct wireless_dev *wdev = dev->ieee80211_ptr;
1447667503ddSJohannes Berg int err;
1448667503ddSJohannes Berg
1449667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev);
1450b23aa676SSamuel Ortiz
145151e13359SJohannes Berg /*
145251e13359SJohannes Berg * If we have an ssid_len, we're trying to connect or are
145351e13359SJohannes Berg * already connected, so reject a new SSID unless it's the
145451e13359SJohannes Berg * same (which is the case for re-association.)
145551e13359SJohannes Berg */
14567b0a0e3cSJohannes Berg if (wdev->u.client.ssid_len &&
14577b0a0e3cSJohannes Berg (wdev->u.client.ssid_len != connect->ssid_len ||
14587b0a0e3cSJohannes Berg memcmp(wdev->u.client.ssid, connect->ssid, wdev->u.client.ssid_len)))
145951e13359SJohannes Berg return -EALREADY;
146051e13359SJohannes Berg
146151e13359SJohannes Berg /*
146251e13359SJohannes Berg * If connected, reject (re-)association unless prev_bssid
146351e13359SJohannes Berg * matches the current BSSID.
146451e13359SJohannes Berg */
14657b0a0e3cSJohannes Berg if (wdev->connected) {
146651e13359SJohannes Berg if (!prev_bssid)
146751e13359SJohannes Berg return -EALREADY;
14687b0a0e3cSJohannes Berg if (!ether_addr_equal(prev_bssid,
14697b0a0e3cSJohannes Berg wdev->u.client.connected_addr))
147051e13359SJohannes Berg return -ENOTCONN;
1471fffd0934SJohannes Berg }
1472fffd0934SJohannes Berg
147351e13359SJohannes Berg /*
147451e13359SJohannes Berg * Reject if we're in the process of connecting with WEP,
147551e13359SJohannes Berg * this case isn't very interesting and trying to handle
147651e13359SJohannes Berg * it would make the code much more complex.
147751e13359SJohannes Berg */
147851e13359SJohannes Berg if (wdev->connect_keys)
147951e13359SJohannes Berg return -EINPROGRESS;
148051e13359SJohannes Berg
14817e7c8926SBen Greear cfg80211_oper_and_ht_capa(&connect->ht_capa_mask,
14827e7c8926SBen Greear rdev->wiphy.ht_capa_mod_mask);
148381c5dce2SSergey Matyukevich cfg80211_oper_and_vht_capa(&connect->vht_capa_mask,
148481c5dce2SSergey Matyukevich rdev->wiphy.vht_capa_mod_mask);
14857e7c8926SBen Greear
1486fffd0934SJohannes Berg if (connkeys && connkeys->def >= 0) {
1487fffd0934SJohannes Berg int idx;
1488bcba8eaeSSamuel Ortiz u32 cipher;
1489fffd0934SJohannes Berg
1490fffd0934SJohannes Berg idx = connkeys->def;
1491bcba8eaeSSamuel Ortiz cipher = connkeys->params[idx].cipher;
1492fffd0934SJohannes Berg /* If given a WEP key we may need it for shared key auth */
1493bcba8eaeSSamuel Ortiz if (cipher == WLAN_CIPHER_SUITE_WEP40 ||
1494bcba8eaeSSamuel Ortiz cipher == WLAN_CIPHER_SUITE_WEP104) {
1495fffd0934SJohannes Berg connect->key_idx = idx;
1496fffd0934SJohannes Berg connect->key = connkeys->params[idx].key;
1497fffd0934SJohannes Berg connect->key_len = connkeys->params[idx].key_len;
1498bcba8eaeSSamuel Ortiz
1499bcba8eaeSSamuel Ortiz /*
1500bcba8eaeSSamuel Ortiz * If ciphers are not set (e.g. when going through
1501bcba8eaeSSamuel Ortiz * iwconfig), we have to set them appropriately here.
1502bcba8eaeSSamuel Ortiz */
1503bcba8eaeSSamuel Ortiz if (connect->crypto.cipher_group == 0)
1504bcba8eaeSSamuel Ortiz connect->crypto.cipher_group = cipher;
1505bcba8eaeSSamuel Ortiz
1506bcba8eaeSSamuel Ortiz if (connect->crypto.n_ciphers_pairwise == 0) {
1507bcba8eaeSSamuel Ortiz connect->crypto.n_ciphers_pairwise = 1;
1508bcba8eaeSSamuel Ortiz connect->crypto.ciphers_pairwise[0] = cipher;
1509bcba8eaeSSamuel Ortiz }
1510fffd0934SJohannes Berg }
1511f1c1f17aSJohannes Berg } else {
1512f1c1f17aSJohannes Berg if (WARN_ON(connkeys))
1513f1c1f17aSJohannes Berg return -EINVAL;
1514015b8cc5SAlexander Wetzel
1515015b8cc5SAlexander Wetzel /* connect can point to wdev->wext.connect which
1516015b8cc5SAlexander Wetzel * can hold key data from a previous connection
1517015b8cc5SAlexander Wetzel */
1518015b8cc5SAlexander Wetzel connect->key = NULL;
1519015b8cc5SAlexander Wetzel connect->key_len = 0;
1520015b8cc5SAlexander Wetzel connect->key_idx = 0;
1521fffd0934SJohannes Berg }
1522fffd0934SJohannes Berg
1523ceca7b71SJohannes Berg wdev->connect_keys = connkeys;
15247b0a0e3cSJohannes Berg memcpy(wdev->u.client.ssid, connect->ssid, connect->ssid_len);
15257b0a0e3cSJohannes Berg wdev->u.client.ssid_len = connect->ssid_len;
15266829c878SJohannes Berg
152734d50519SLior David wdev->conn_bss_type = connect->pbss ? IEEE80211_BSS_TYPE_PBSS :
152834d50519SLior David IEEE80211_BSS_TYPE_ESS;
152934d50519SLior David
1530ceca7b71SJohannes Berg if (!rdev->ops->connect)
1531ceca7b71SJohannes Berg err = cfg80211_sme_connect(wdev, connect, prev_bssid);
1532ceca7b71SJohannes Berg else
1533ceca7b71SJohannes Berg err = rdev_connect(rdev, dev, connect);
15346829c878SJohannes Berg
153519957bb3SJohannes Berg if (err) {
1536fffd0934SJohannes Berg wdev->connect_keys = NULL;
153751e13359SJohannes Berg /*
153851e13359SJohannes Berg * This could be reassoc getting refused, don't clear
153951e13359SJohannes Berg * ssid_len in that case.
154051e13359SJohannes Berg */
15417b0a0e3cSJohannes Berg if (!wdev->connected)
15427b0a0e3cSJohannes Berg wdev->u.client.ssid_len = 0;
1543b23aa676SSamuel Ortiz return err;
1544b23aa676SSamuel Ortiz }
1545b23aa676SSamuel Ortiz
1546b23aa676SSamuel Ortiz return 0;
1547b23aa676SSamuel Ortiz }
1548b23aa676SSamuel Ortiz
cfg80211_disconnect(struct cfg80211_registered_device * rdev,struct net_device * dev,u16 reason,bool wextev)154983739b03SJohannes Berg int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
1550f2129354SJohannes Berg struct net_device *dev, u16 reason, bool wextev)
1551b23aa676SSamuel Ortiz {
15526829c878SJohannes Berg struct wireless_dev *wdev = dev->ieee80211_ptr;
1553dee8a973SJohannes Berg int err = 0;
1554b23aa676SSamuel Ortiz
1555667503ddSJohannes Berg ASSERT_WDEV_LOCK(wdev);
1556667503ddSJohannes Berg
1557453431a5SWaiman Long kfree_sensitive(wdev->connect_keys);
1558fffd0934SJohannes Berg wdev->connect_keys = NULL;
1559fffd0934SJohannes Berg
1560bd2522b1SAndrzej Zaborowski wdev->conn_owner_nlportid = 0;
1561bd2522b1SAndrzej Zaborowski
1562dee8a973SJohannes Berg if (wdev->conn)
1563ceca7b71SJohannes Berg err = cfg80211_sme_disconnect(wdev, reason);
1564dee8a973SJohannes Berg else if (!rdev->ops->disconnect)
156519957bb3SJohannes Berg cfg80211_mlme_down(rdev, dev);
15667b0a0e3cSJohannes Berg else if (wdev->u.client.ssid_len)
1567e35e4d28SHila Gonen err = rdev_disconnect(rdev, dev, reason);
1568ceca7b71SJohannes Berg
156951e13359SJohannes Berg /*
157051e13359SJohannes Berg * Clear ssid_len unless we actually were fully connected,
157151e13359SJohannes Berg * in which case cfg80211_disconnected() will take care of
157251e13359SJohannes Berg * this later.
157351e13359SJohannes Berg */
15747b0a0e3cSJohannes Berg if (!wdev->connected)
15757b0a0e3cSJohannes Berg wdev->u.client.ssid_len = 0;
157651e13359SJohannes Berg
1577b23aa676SSamuel Ortiz return err;
1578b23aa676SSamuel Ortiz }
1579bd2522b1SAndrzej Zaborowski
1580bd2522b1SAndrzej Zaborowski /*
1581bd2522b1SAndrzej Zaborowski * Used to clean up after the connection / connection attempt owner socket
1582bd2522b1SAndrzej Zaborowski * disconnects
1583bd2522b1SAndrzej Zaborowski */
cfg80211_autodisconnect_wk(struct work_struct * work)1584bd2522b1SAndrzej Zaborowski void cfg80211_autodisconnect_wk(struct work_struct *work)
1585bd2522b1SAndrzej Zaborowski {
1586bd2522b1SAndrzej Zaborowski struct wireless_dev *wdev =
1587bd2522b1SAndrzej Zaborowski container_of(work, struct wireless_dev, disconnect_wk);
1588bd2522b1SAndrzej Zaborowski struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
1589bd2522b1SAndrzej Zaborowski
1590e9da6df7SJohannes Berg wiphy_lock(wdev->wiphy);
1591bd2522b1SAndrzej Zaborowski wdev_lock(wdev);
1592bd2522b1SAndrzej Zaborowski
1593bd2522b1SAndrzej Zaborowski if (wdev->conn_owner_nlportid) {
159437b1c004SDenis Kenzior switch (wdev->iftype) {
159537b1c004SDenis Kenzior case NL80211_IFTYPE_ADHOC:
15965a128a08SMarkus Theil __cfg80211_leave_ibss(rdev, wdev->netdev, false);
159737b1c004SDenis Kenzior break;
159837b1c004SDenis Kenzior case NL80211_IFTYPE_AP:
159937b1c004SDenis Kenzior case NL80211_IFTYPE_P2P_GO:
16007b0a0e3cSJohannes Berg __cfg80211_stop_ap(rdev, wdev->netdev, -1, false);
160137b1c004SDenis Kenzior break;
160237b1c004SDenis Kenzior case NL80211_IFTYPE_MESH_POINT:
16035a128a08SMarkus Theil __cfg80211_leave_mesh(rdev, wdev->netdev);
160437b1c004SDenis Kenzior break;
160537b1c004SDenis Kenzior case NL80211_IFTYPE_STATION:
160637b1c004SDenis Kenzior case NL80211_IFTYPE_P2P_CLIENT:
1607bd2522b1SAndrzej Zaborowski /*
160837b1c004SDenis Kenzior * Use disconnect_bssid if still connecting and
160937b1c004SDenis Kenzior * ops->disconnect not implemented. Otherwise we can
161037b1c004SDenis Kenzior * use cfg80211_disconnect.
1611bd2522b1SAndrzej Zaborowski */
16127b0a0e3cSJohannes Berg if (rdev->ops->disconnect || wdev->connected)
1613bd2522b1SAndrzej Zaborowski cfg80211_disconnect(rdev, wdev->netdev,
161437b1c004SDenis Kenzior WLAN_REASON_DEAUTH_LEAVING,
161537b1c004SDenis Kenzior true);
1616bd2522b1SAndrzej Zaborowski else
1617bd2522b1SAndrzej Zaborowski cfg80211_mlme_deauth(rdev, wdev->netdev,
161837b1c004SDenis Kenzior wdev->disconnect_bssid,
161937b1c004SDenis Kenzior NULL, 0,
162037b1c004SDenis Kenzior WLAN_REASON_DEAUTH_LEAVING,
162137b1c004SDenis Kenzior false);
162237b1c004SDenis Kenzior break;
162337b1c004SDenis Kenzior default:
162437b1c004SDenis Kenzior break;
162537b1c004SDenis Kenzior }
1626bd2522b1SAndrzej Zaborowski }
1627bd2522b1SAndrzej Zaborowski
1628bd2522b1SAndrzej Zaborowski wdev_unlock(wdev);
1629e9da6df7SJohannes Berg wiphy_unlock(wdev->wiphy);
1630bd2522b1SAndrzej Zaborowski }
1631