| 1 | // SPDX-License-Identifier: GPL-2.0 | 
|---|
| 2 | /* | 
|---|
| 3 | * cfg80211 MLME SAP interface | 
|---|
| 4 | * | 
|---|
| 5 | * Copyright (c) 2009, Jouni Malinen <j@w1.fi> | 
|---|
| 6 | * Copyright (c) 2015		Intel Deutschland GmbH | 
|---|
| 7 | * Copyright (C) 2019-2020, 2022-2025 Intel Corporation | 
|---|
| 8 | */ | 
|---|
| 9 |  | 
|---|
| 10 | #include <linux/kernel.h> | 
|---|
| 11 | #include <linux/module.h> | 
|---|
| 12 | #include <linux/etherdevice.h> | 
|---|
| 13 | #include <linux/netdevice.h> | 
|---|
| 14 | #include <linux/nl80211.h> | 
|---|
| 15 | #include <linux/slab.h> | 
|---|
| 16 | #include <linux/wireless.h> | 
|---|
| 17 | #include <net/cfg80211.h> | 
|---|
| 18 | #include <net/iw_handler.h> | 
|---|
| 19 | #include "core.h" | 
|---|
| 20 | #include "nl80211.h" | 
|---|
| 21 | #include "rdev-ops.h" | 
|---|
| 22 |  | 
|---|
| 23 |  | 
|---|
| 24 | void cfg80211_rx_assoc_resp(struct net_device *dev, | 
|---|
| 25 | const struct cfg80211_rx_assoc_resp_data *data) | 
|---|
| 26 | { | 
|---|
| 27 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 28 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 29 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 30 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)data->buf; | 
|---|
| 31 | struct cfg80211_connect_resp_params cr = { | 
|---|
| 32 | .timeout_reason = NL80211_TIMEOUT_UNSPECIFIED, | 
|---|
| 33 | .req_ie = data->req_ies, | 
|---|
| 34 | .req_ie_len = data->req_ies_len, | 
|---|
| 35 | .resp_ie = mgmt->u.assoc_resp.variable, | 
|---|
| 36 | .resp_ie_len = data->len - | 
|---|
| 37 | offsetof(struct ieee80211_mgmt, | 
|---|
| 38 | u.assoc_resp.variable), | 
|---|
| 39 | .status = le16_to_cpu(mgmt->u.assoc_resp.status_code), | 
|---|
| 40 | .ap_mld_addr = data->ap_mld_addr, | 
|---|
| 41 | }; | 
|---|
| 42 | unsigned int link_id; | 
|---|
| 43 |  | 
|---|
| 44 | for (link_id = 0; link_id < ARRAY_SIZE(data->links); link_id++) { | 
|---|
| 45 | cr.links[link_id].status = data->links[link_id].status; | 
|---|
| 46 | cr.links[link_id].bss = data->links[link_id].bss; | 
|---|
| 47 |  | 
|---|
| 48 | WARN_ON_ONCE(cr.links[link_id].status != WLAN_STATUS_SUCCESS && | 
|---|
| 49 | (!cr.ap_mld_addr || !cr.links[link_id].bss)); | 
|---|
| 50 |  | 
|---|
| 51 | if (!cr.links[link_id].bss) | 
|---|
| 52 | continue; | 
|---|
| 53 | cr.links[link_id].bssid = data->links[link_id].bss->bssid; | 
|---|
| 54 | cr.links[link_id].addr = data->links[link_id].addr; | 
|---|
| 55 | /* need to have local link addresses for MLO connections */ | 
|---|
| 56 | WARN_ON(cr.ap_mld_addr && | 
|---|
| 57 | !is_valid_ether_addr(cr.links[link_id].addr)); | 
|---|
| 58 |  | 
|---|
| 59 | BUG_ON(!cr.links[link_id].bss->channel); | 
|---|
| 60 |  | 
|---|
| 61 | if (cr.links[link_id].bss->channel->band == NL80211_BAND_S1GHZ) { | 
|---|
| 62 | WARN_ON(link_id); | 
|---|
| 63 | cr.resp_ie = (u8 *)&mgmt->u.s1g_assoc_resp.variable; | 
|---|
| 64 | cr.resp_ie_len = data->len - | 
|---|
| 65 | offsetof(struct ieee80211_mgmt, | 
|---|
| 66 | u.s1g_assoc_resp.variable); | 
|---|
| 67 | } | 
|---|
| 68 |  | 
|---|
| 69 | if (cr.ap_mld_addr) | 
|---|
| 70 | cr.valid_links |= BIT(link_id); | 
|---|
| 71 | } | 
|---|
| 72 |  | 
|---|
| 73 | trace_cfg80211_send_rx_assoc(netdev: dev, data); | 
|---|
| 74 |  | 
|---|
| 75 | /* | 
|---|
| 76 | * This is a bit of a hack, we don't notify userspace of | 
|---|
| 77 | * a (re-)association reply if we tried to send a reassoc | 
|---|
| 78 | * and got a reject -- we only try again with an assoc | 
|---|
| 79 | * frame instead of reassoc. | 
|---|
| 80 | */ | 
|---|
| 81 | if (cfg80211_sme_rx_assoc_resp(wdev, status: cr.status)) { | 
|---|
| 82 | for (link_id = 0; link_id < ARRAY_SIZE(data->links); link_id++) { | 
|---|
| 83 | struct cfg80211_bss *bss = data->links[link_id].bss; | 
|---|
| 84 |  | 
|---|
| 85 | if (!bss) | 
|---|
| 86 | continue; | 
|---|
| 87 |  | 
|---|
| 88 | cfg80211_unhold_bss(bss: bss_from_pub(pub: bss)); | 
|---|
| 89 | cfg80211_put_bss(wiphy, bss); | 
|---|
| 90 | } | 
|---|
| 91 | return; | 
|---|
| 92 | } | 
|---|
| 93 |  | 
|---|
| 94 | nl80211_send_rx_assoc(rdev, netdev: dev, data); | 
|---|
| 95 | /* update current_bss etc., consumes the bss reference */ | 
|---|
| 96 | __cfg80211_connect_result(dev, params: &cr, wextev: cr.status == WLAN_STATUS_SUCCESS); | 
|---|
| 97 | } | 
|---|
| 98 | EXPORT_SYMBOL(cfg80211_rx_assoc_resp); | 
|---|
| 99 |  | 
|---|
| 100 | static void cfg80211_process_auth(struct wireless_dev *wdev, | 
|---|
| 101 | const u8 *buf, size_t len) | 
|---|
| 102 | { | 
|---|
| 103 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); | 
|---|
| 104 |  | 
|---|
| 105 | nl80211_send_rx_auth(rdev, netdev: wdev->netdev, buf, len, GFP_KERNEL); | 
|---|
| 106 | cfg80211_sme_rx_auth(wdev, buf, len); | 
|---|
| 107 | } | 
|---|
| 108 |  | 
|---|
| 109 | static void cfg80211_process_deauth(struct wireless_dev *wdev, | 
|---|
| 110 | const u8 *buf, size_t len, | 
|---|
| 111 | bool reconnect) | 
|---|
| 112 | { | 
|---|
| 113 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); | 
|---|
| 114 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; | 
|---|
| 115 | const u8 *bssid = mgmt->bssid; | 
|---|
| 116 | u16 reason_code = le16_to_cpu(mgmt->u.deauth.reason_code); | 
|---|
| 117 | bool from_ap = !ether_addr_equal(addr1: mgmt->sa, addr2: wdev->netdev->dev_addr); | 
|---|
| 118 |  | 
|---|
| 119 | nl80211_send_deauth(rdev, netdev: wdev->netdev, buf, len, reconnect, GFP_KERNEL); | 
|---|
| 120 |  | 
|---|
| 121 | if (!wdev->connected || !ether_addr_equal(addr1: wdev->u.client.connected_addr, addr2: bssid)) | 
|---|
| 122 | return; | 
|---|
| 123 |  | 
|---|
| 124 | __cfg80211_disconnected(dev: wdev->netdev, NULL, ie_len: 0, reason: reason_code, from_ap); | 
|---|
| 125 | cfg80211_sme_deauth(wdev); | 
|---|
| 126 | } | 
|---|
| 127 |  | 
|---|
| 128 | static void cfg80211_process_disassoc(struct wireless_dev *wdev, | 
|---|
| 129 | const u8 *buf, size_t len, | 
|---|
| 130 | bool reconnect) | 
|---|
| 131 | { | 
|---|
| 132 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); | 
|---|
| 133 | struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)buf; | 
|---|
| 134 | const u8 *bssid = mgmt->bssid; | 
|---|
| 135 | u16 reason_code = le16_to_cpu(mgmt->u.disassoc.reason_code); | 
|---|
| 136 | bool from_ap = !ether_addr_equal(addr1: mgmt->sa, addr2: wdev->netdev->dev_addr); | 
|---|
| 137 |  | 
|---|
| 138 | nl80211_send_disassoc(rdev, netdev: wdev->netdev, buf, len, reconnect, | 
|---|
| 139 | GFP_KERNEL); | 
|---|
| 140 |  | 
|---|
| 141 | if (WARN_ON(!wdev->connected || | 
|---|
| 142 | !ether_addr_equal(wdev->u.client.connected_addr, bssid))) | 
|---|
| 143 | return; | 
|---|
| 144 |  | 
|---|
| 145 | __cfg80211_disconnected(dev: wdev->netdev, NULL, ie_len: 0, reason: reason_code, from_ap); | 
|---|
| 146 | cfg80211_sme_disassoc(wdev); | 
|---|
| 147 | } | 
|---|
| 148 |  | 
|---|
| 149 | void cfg80211_rx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len) | 
|---|
| 150 | { | 
|---|
| 151 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 152 | struct ieee80211_mgmt *mgmt = (void *)buf; | 
|---|
| 153 |  | 
|---|
| 154 | lockdep_assert_wiphy(wdev->wiphy); | 
|---|
| 155 |  | 
|---|
| 156 | trace_cfg80211_rx_mlme_mgmt(netdev: dev, buf, len); | 
|---|
| 157 |  | 
|---|
| 158 | if (WARN_ON(len < 2)) | 
|---|
| 159 | return; | 
|---|
| 160 |  | 
|---|
| 161 | if (ieee80211_is_auth(fc: mgmt->frame_control)) | 
|---|
| 162 | cfg80211_process_auth(wdev, buf, len); | 
|---|
| 163 | else if (ieee80211_is_deauth(fc: mgmt->frame_control)) | 
|---|
| 164 | cfg80211_process_deauth(wdev, buf, len, reconnect: false); | 
|---|
| 165 | else if (ieee80211_is_disassoc(fc: mgmt->frame_control)) | 
|---|
| 166 | cfg80211_process_disassoc(wdev, buf, len, reconnect: false); | 
|---|
| 167 | } | 
|---|
| 168 | EXPORT_SYMBOL(cfg80211_rx_mlme_mgmt); | 
|---|
| 169 |  | 
|---|
| 170 | void cfg80211_auth_timeout(struct net_device *dev, const u8 *addr) | 
|---|
| 171 | { | 
|---|
| 172 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 173 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 174 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 175 |  | 
|---|
| 176 | trace_cfg80211_send_auth_timeout(netdev: dev, mac: addr); | 
|---|
| 177 |  | 
|---|
| 178 | nl80211_send_auth_timeout(rdev, netdev: dev, addr, GFP_KERNEL); | 
|---|
| 179 | cfg80211_sme_auth_timeout(wdev); | 
|---|
| 180 | } | 
|---|
| 181 | EXPORT_SYMBOL(cfg80211_auth_timeout); | 
|---|
| 182 |  | 
|---|
| 183 | void cfg80211_assoc_failure(struct net_device *dev, | 
|---|
| 184 | struct cfg80211_assoc_failure *data) | 
|---|
| 185 | { | 
|---|
| 186 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 187 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 188 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 189 | const u8 *addr = data->ap_mld_addr ?: data->bss[0]->bssid; | 
|---|
| 190 | int i; | 
|---|
| 191 |  | 
|---|
| 192 | trace_cfg80211_send_assoc_failure(netdev: dev, data); | 
|---|
| 193 |  | 
|---|
| 194 | if (data->timeout) { | 
|---|
| 195 | nl80211_send_assoc_timeout(rdev, netdev: dev, addr, GFP_KERNEL); | 
|---|
| 196 | cfg80211_sme_assoc_timeout(wdev); | 
|---|
| 197 | } else { | 
|---|
| 198 | cfg80211_sme_abandon_assoc(wdev); | 
|---|
| 199 | } | 
|---|
| 200 |  | 
|---|
| 201 | for (i = 0; i < ARRAY_SIZE(data->bss); i++) { | 
|---|
| 202 | struct cfg80211_bss *bss = data->bss[i]; | 
|---|
| 203 |  | 
|---|
| 204 | if (!bss) | 
|---|
| 205 | continue; | 
|---|
| 206 |  | 
|---|
| 207 | cfg80211_unhold_bss(bss: bss_from_pub(pub: bss)); | 
|---|
| 208 | cfg80211_put_bss(wiphy, bss); | 
|---|
| 209 | } | 
|---|
| 210 | } | 
|---|
| 211 | EXPORT_SYMBOL(cfg80211_assoc_failure); | 
|---|
| 212 |  | 
|---|
| 213 | void cfg80211_tx_mlme_mgmt(struct net_device *dev, const u8 *buf, size_t len, | 
|---|
| 214 | bool reconnect) | 
|---|
| 215 | { | 
|---|
| 216 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 217 | struct ieee80211_mgmt *mgmt = (void *)buf; | 
|---|
| 218 |  | 
|---|
| 219 | lockdep_assert_wiphy(wdev->wiphy); | 
|---|
| 220 |  | 
|---|
| 221 | trace_cfg80211_tx_mlme_mgmt(netdev: dev, buf, len, reconnect); | 
|---|
| 222 |  | 
|---|
| 223 | if (WARN_ON(len < 2)) | 
|---|
| 224 | return; | 
|---|
| 225 |  | 
|---|
| 226 | if (ieee80211_is_deauth(fc: mgmt->frame_control)) | 
|---|
| 227 | cfg80211_process_deauth(wdev, buf, len, reconnect); | 
|---|
| 228 | else | 
|---|
| 229 | cfg80211_process_disassoc(wdev, buf, len, reconnect); | 
|---|
| 230 | } | 
|---|
| 231 | EXPORT_SYMBOL(cfg80211_tx_mlme_mgmt); | 
|---|
| 232 |  | 
|---|
| 233 | void cfg80211_michael_mic_failure(struct net_device *dev, const u8 *addr, | 
|---|
| 234 | enum nl80211_key_type key_type, int key_id, | 
|---|
| 235 | const u8 *tsc, gfp_t gfp) | 
|---|
| 236 | { | 
|---|
| 237 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | 
|---|
| 238 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 239 | #ifdef CONFIG_CFG80211_WEXT | 
|---|
| 240 | union iwreq_data wrqu; | 
|---|
| 241 | char *buf = kmalloc(128, gfp); | 
|---|
| 242 |  | 
|---|
| 243 | if (buf) { | 
|---|
| 244 | memset(&wrqu, 0, sizeof(wrqu)); | 
|---|
| 245 | wrqu.data.length = | 
|---|
| 246 | sprintf(buf, "MLME-MICHAELMICFAILURE." | 
|---|
| 247 | "indication(keyid=%d %scast addr=%pM)", | 
|---|
| 248 | key_id, key_type == NL80211_KEYTYPE_GROUP | 
|---|
| 249 | ? "broad": "uni", addr); | 
|---|
| 250 | wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); | 
|---|
| 251 | kfree(buf); | 
|---|
| 252 | } | 
|---|
| 253 | #endif | 
|---|
| 254 |  | 
|---|
| 255 | trace_cfg80211_michael_mic_failure(netdev: dev, addr, key_type, key_id, tsc); | 
|---|
| 256 | nl80211_michael_mic_failure(rdev, netdev: dev, addr, key_type, key_id, tsc, gfp); | 
|---|
| 257 | } | 
|---|
| 258 | EXPORT_SYMBOL(cfg80211_michael_mic_failure); | 
|---|
| 259 |  | 
|---|
| 260 | /* some MLME handling for userspace SME */ | 
|---|
| 261 | int cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, | 
|---|
| 262 | struct net_device *dev, | 
|---|
| 263 | struct cfg80211_auth_request *req) | 
|---|
| 264 | { | 
|---|
| 265 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 266 |  | 
|---|
| 267 | lockdep_assert_wiphy(wdev->wiphy); | 
|---|
| 268 |  | 
|---|
| 269 | if (!req->bss) | 
|---|
| 270 | return -ENOENT; | 
|---|
| 271 |  | 
|---|
| 272 | if (req->link_id >= 0 && | 
|---|
| 273 | !(wdev->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO)) | 
|---|
| 274 | return -EINVAL; | 
|---|
| 275 |  | 
|---|
| 276 | if (req->auth_type == NL80211_AUTHTYPE_SHARED_KEY) { | 
|---|
| 277 | if (!req->key || !req->key_len || | 
|---|
| 278 | req->key_idx < 0 || req->key_idx > 3) | 
|---|
| 279 | return -EINVAL; | 
|---|
| 280 | } | 
|---|
| 281 |  | 
|---|
| 282 | if (wdev->connected && | 
|---|
| 283 | ether_addr_equal(addr1: req->bss->bssid, addr2: wdev->u.client.connected_addr)) | 
|---|
| 284 | return -EALREADY; | 
|---|
| 285 |  | 
|---|
| 286 | if (ether_addr_equal(addr1: req->bss->bssid, addr2: dev->dev_addr) || | 
|---|
| 287 | (req->link_id >= 0 && | 
|---|
| 288 | ether_addr_equal(addr1: req->ap_mld_addr, addr2: dev->dev_addr))) | 
|---|
| 289 | return -EINVAL; | 
|---|
| 290 |  | 
|---|
| 291 | return rdev_auth(rdev, dev, req); | 
|---|
| 292 | } | 
|---|
| 293 |  | 
|---|
| 294 | /*  Do a logical ht_capa &= ht_capa_mask.  */ | 
|---|
| 295 | void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa, | 
|---|
| 296 | const struct ieee80211_ht_cap *ht_capa_mask) | 
|---|
| 297 | { | 
|---|
| 298 | int i; | 
|---|
| 299 | u8 *p1, *p2; | 
|---|
| 300 | if (!ht_capa_mask) { | 
|---|
| 301 | memset(s: ht_capa, c: 0, n: sizeof(*ht_capa)); | 
|---|
| 302 | return; | 
|---|
| 303 | } | 
|---|
| 304 |  | 
|---|
| 305 | p1 = (u8*)(ht_capa); | 
|---|
| 306 | p2 = (u8*)(ht_capa_mask); | 
|---|
| 307 | for (i = 0; i < sizeof(*ht_capa); i++) | 
|---|
| 308 | p1[i] &= p2[i]; | 
|---|
| 309 | } | 
|---|
| 310 |  | 
|---|
| 311 | /*  Do a logical vht_capa &= vht_capa_mask.  */ | 
|---|
| 312 | void cfg80211_oper_and_vht_capa(struct ieee80211_vht_cap *vht_capa, | 
|---|
| 313 | const struct ieee80211_vht_cap *vht_capa_mask) | 
|---|
| 314 | { | 
|---|
| 315 | int i; | 
|---|
| 316 | u8 *p1, *p2; | 
|---|
| 317 | if (!vht_capa_mask) { | 
|---|
| 318 | memset(s: vht_capa, c: 0, n: sizeof(*vht_capa)); | 
|---|
| 319 | return; | 
|---|
| 320 | } | 
|---|
| 321 |  | 
|---|
| 322 | p1 = (u8*)(vht_capa); | 
|---|
| 323 | p2 = (u8*)(vht_capa_mask); | 
|---|
| 324 | for (i = 0; i < sizeof(*vht_capa); i++) | 
|---|
| 325 | p1[i] &= p2[i]; | 
|---|
| 326 | } | 
|---|
| 327 |  | 
|---|
| 328 | static int | 
|---|
| 329 | cfg80211_mlme_check_mlo_compat(const struct ieee80211_multi_link_elem *mle_a, | 
|---|
| 330 | const struct ieee80211_multi_link_elem *mle_b, | 
|---|
| 331 | struct netlink_ext_ack *extack) | 
|---|
| 332 | { | 
|---|
| 333 | const struct ieee80211_mle_basic_common_info *common_a, *common_b; | 
|---|
| 334 |  | 
|---|
| 335 | common_a = (const void *)mle_a->variable; | 
|---|
| 336 | common_b = (const void *)mle_b->variable; | 
|---|
| 337 |  | 
|---|
| 338 | if (memcmp(common_a->mld_mac_addr, common_b->mld_mac_addr, ETH_ALEN)) { | 
|---|
| 339 | NL_SET_ERR_MSG(extack, "AP MLD address mismatch"); | 
|---|
| 340 | return -EINVAL; | 
|---|
| 341 | } | 
|---|
| 342 |  | 
|---|
| 343 | if (ieee80211_mle_get_eml_cap(data: (const u8 *)mle_a) != | 
|---|
| 344 | ieee80211_mle_get_eml_cap(data: (const u8 *)mle_b)) { | 
|---|
| 345 | NL_SET_ERR_MSG(extack, "link EML capabilities mismatch"); | 
|---|
| 346 | return -EINVAL; | 
|---|
| 347 | } | 
|---|
| 348 |  | 
|---|
| 349 | if (ieee80211_mle_get_mld_capa_op(data: (const u8 *)mle_a) != | 
|---|
| 350 | ieee80211_mle_get_mld_capa_op(data: (const u8 *)mle_b)) { | 
|---|
| 351 | NL_SET_ERR_MSG(extack, "link MLD capabilities/ops mismatch"); | 
|---|
| 352 | return -EINVAL; | 
|---|
| 353 | } | 
|---|
| 354 |  | 
|---|
| 355 | /* | 
|---|
| 356 | * Only verify the values in Extended MLD Capabilities that are | 
|---|
| 357 | * not reserved when transmitted by an AP (and expected to remain the | 
|---|
| 358 | * same over time). | 
|---|
| 359 | * The Recommended Max Simultaneous Links subfield in particular is | 
|---|
| 360 | * reserved when included in a unicast Probe Response frame and may | 
|---|
| 361 | * also change when the AP adds/removes links. The BTM MLD | 
|---|
| 362 | * Recommendation For Multiple APs Support subfield is reserved when | 
|---|
| 363 | * transmitted by an AP. All other bits are currently reserved. | 
|---|
| 364 | * See IEEE P802.11be/D7.0, Table 9-417o. | 
|---|
| 365 | */ | 
|---|
| 366 | if ((ieee80211_mle_get_ext_mld_capa_op(data: (const u8 *)mle_a) & | 
|---|
| 367 | (IEEE80211_EHT_ML_EXT_MLD_CAPA_OP_PARAM_UPDATE | | 
|---|
| 368 | IEEE80211_EHT_ML_EXT_MLD_CAPA_NSTR_UPDATE | | 
|---|
| 369 | IEEE80211_EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ON_ONE_LINK)) != | 
|---|
| 370 | (ieee80211_mle_get_ext_mld_capa_op(data: (const u8 *)mle_b) & | 
|---|
| 371 | (IEEE80211_EHT_ML_EXT_MLD_CAPA_OP_PARAM_UPDATE | | 
|---|
| 372 | IEEE80211_EHT_ML_EXT_MLD_CAPA_NSTR_UPDATE | | 
|---|
| 373 | IEEE80211_EHT_ML_EXT_MLD_CAPA_EMLSR_ENA_ON_ONE_LINK))) { | 
|---|
| 374 | NL_SET_ERR_MSG(extack, | 
|---|
| 375 | "extended link MLD capabilities/ops mismatch"); | 
|---|
| 376 | return -EINVAL; | 
|---|
| 377 | } | 
|---|
| 378 |  | 
|---|
| 379 | return 0; | 
|---|
| 380 | } | 
|---|
| 381 |  | 
|---|
| 382 | static int cfg80211_mlme_check_mlo(struct net_device *dev, | 
|---|
| 383 | struct cfg80211_assoc_request *req, | 
|---|
| 384 | struct netlink_ext_ack *extack) | 
|---|
| 385 | { | 
|---|
| 386 | const struct ieee80211_multi_link_elem *mles[ARRAY_SIZE(req->links)] = {}; | 
|---|
| 387 | int i; | 
|---|
| 388 |  | 
|---|
| 389 | if (req->link_id < 0) | 
|---|
| 390 | return 0; | 
|---|
| 391 |  | 
|---|
| 392 | if (!req->links[req->link_id].bss) { | 
|---|
| 393 | NL_SET_ERR_MSG(extack, "no BSS for assoc link"); | 
|---|
| 394 | return -EINVAL; | 
|---|
| 395 | } | 
|---|
| 396 |  | 
|---|
| 397 | rcu_read_lock(); | 
|---|
| 398 | for (i = 0; i < ARRAY_SIZE(req->links); i++) { | 
|---|
| 399 | const struct cfg80211_bss_ies *ies; | 
|---|
| 400 | const struct element *ml; | 
|---|
| 401 |  | 
|---|
| 402 | if (!req->links[i].bss) | 
|---|
| 403 | continue; | 
|---|
| 404 |  | 
|---|
| 405 | if (ether_addr_equal(addr1: req->links[i].bss->bssid, addr2: dev->dev_addr)) { | 
|---|
| 406 | NL_SET_ERR_MSG(extack, "BSSID must not be our address"); | 
|---|
| 407 | req->links[i].error = -EINVAL; | 
|---|
| 408 | goto error; | 
|---|
| 409 | } | 
|---|
| 410 |  | 
|---|
| 411 | ies = rcu_dereference(req->links[i].bss->ies); | 
|---|
| 412 | ml = cfg80211_find_ext_elem(ext_eid: WLAN_EID_EXT_EHT_MULTI_LINK, | 
|---|
| 413 | ies: ies->data, len: ies->len); | 
|---|
| 414 | if (!ml) { | 
|---|
| 415 | NL_SET_ERR_MSG(extack, "MLO BSS w/o ML element"); | 
|---|
| 416 | req->links[i].error = -EINVAL; | 
|---|
| 417 | goto error; | 
|---|
| 418 | } | 
|---|
| 419 |  | 
|---|
| 420 | if (!ieee80211_mle_type_ok(data: ml->data + 1, | 
|---|
| 421 | IEEE80211_ML_CONTROL_TYPE_BASIC, | 
|---|
| 422 | len: ml->datalen - 1)) { | 
|---|
| 423 | NL_SET_ERR_MSG(extack, "BSS with invalid ML element"); | 
|---|
| 424 | req->links[i].error = -EINVAL; | 
|---|
| 425 | goto error; | 
|---|
| 426 | } | 
|---|
| 427 |  | 
|---|
| 428 | mles[i] = (const void *)(ml->data + 1); | 
|---|
| 429 |  | 
|---|
| 430 | if (ieee80211_mle_get_link_id(data: (const u8 *)mles[i]) != i) { | 
|---|
| 431 | NL_SET_ERR_MSG(extack, "link ID mismatch"); | 
|---|
| 432 | req->links[i].error = -EINVAL; | 
|---|
| 433 | goto error; | 
|---|
| 434 | } | 
|---|
| 435 | } | 
|---|
| 436 |  | 
|---|
| 437 | if (WARN_ON(!mles[req->link_id])) | 
|---|
| 438 | goto error; | 
|---|
| 439 |  | 
|---|
| 440 | for (i = 0; i < ARRAY_SIZE(req->links); i++) { | 
|---|
| 441 | if (i == req->link_id || !req->links[i].bss) | 
|---|
| 442 | continue; | 
|---|
| 443 |  | 
|---|
| 444 | if (WARN_ON(!mles[i])) | 
|---|
| 445 | goto error; | 
|---|
| 446 |  | 
|---|
| 447 | if (cfg80211_mlme_check_mlo_compat(mle_a: mles[req->link_id], mle_b: mles[i], | 
|---|
| 448 | extack)) { | 
|---|
| 449 | req->links[i].error = -EINVAL; | 
|---|
| 450 | goto error; | 
|---|
| 451 | } | 
|---|
| 452 | } | 
|---|
| 453 |  | 
|---|
| 454 | rcu_read_unlock(); | 
|---|
| 455 | return 0; | 
|---|
| 456 | error: | 
|---|
| 457 | rcu_read_unlock(); | 
|---|
| 458 | return -EINVAL; | 
|---|
| 459 | } | 
|---|
| 460 |  | 
|---|
| 461 | /* Note: caller must cfg80211_put_bss() regardless of result */ | 
|---|
| 462 | int cfg80211_mlme_assoc(struct cfg80211_registered_device *rdev, | 
|---|
| 463 | struct net_device *dev, | 
|---|
| 464 | struct cfg80211_assoc_request *req, | 
|---|
| 465 | struct netlink_ext_ack *extack) | 
|---|
| 466 | { | 
|---|
| 467 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 468 | int err; | 
|---|
| 469 |  | 
|---|
| 470 | lockdep_assert_wiphy(wdev->wiphy); | 
|---|
| 471 |  | 
|---|
| 472 | err = cfg80211_mlme_check_mlo(dev, req, extack); | 
|---|
| 473 | if (err) | 
|---|
| 474 | return err; | 
|---|
| 475 |  | 
|---|
| 476 | if (wdev->connected && | 
|---|
| 477 | (!req->prev_bssid || | 
|---|
| 478 | !ether_addr_equal(addr1: wdev->u.client.connected_addr, addr2: req->prev_bssid))) | 
|---|
| 479 | return -EALREADY; | 
|---|
| 480 |  | 
|---|
| 481 | if ((req->bss && ether_addr_equal(addr1: req->bss->bssid, addr2: dev->dev_addr)) || | 
|---|
| 482 | (req->link_id >= 0 && | 
|---|
| 483 | ether_addr_equal(addr1: req->ap_mld_addr, addr2: dev->dev_addr))) | 
|---|
| 484 | return -EINVAL; | 
|---|
| 485 |  | 
|---|
| 486 | cfg80211_oper_and_ht_capa(ht_capa: &req->ht_capa_mask, | 
|---|
| 487 | ht_capa_mask: rdev->wiphy.ht_capa_mod_mask); | 
|---|
| 488 | cfg80211_oper_and_vht_capa(vht_capa: &req->vht_capa_mask, | 
|---|
| 489 | vht_capa_mask: rdev->wiphy.vht_capa_mod_mask); | 
|---|
| 490 |  | 
|---|
| 491 | err = rdev_assoc(rdev, dev, req); | 
|---|
| 492 | if (!err) { | 
|---|
| 493 | int link_id; | 
|---|
| 494 |  | 
|---|
| 495 | if (req->bss) { | 
|---|
| 496 | cfg80211_ref_bss(wiphy: &rdev->wiphy, bss: req->bss); | 
|---|
| 497 | cfg80211_hold_bss(bss: bss_from_pub(pub: req->bss)); | 
|---|
| 498 | } | 
|---|
| 499 |  | 
|---|
| 500 | for (link_id = 0; link_id < ARRAY_SIZE(req->links); link_id++) { | 
|---|
| 501 | if (!req->links[link_id].bss) | 
|---|
| 502 | continue; | 
|---|
| 503 | cfg80211_ref_bss(wiphy: &rdev->wiphy, bss: req->links[link_id].bss); | 
|---|
| 504 | cfg80211_hold_bss(bss: bss_from_pub(pub: req->links[link_id].bss)); | 
|---|
| 505 | } | 
|---|
| 506 | } | 
|---|
| 507 | return err; | 
|---|
| 508 | } | 
|---|
| 509 |  | 
|---|
| 510 | int cfg80211_mlme_deauth(struct cfg80211_registered_device *rdev, | 
|---|
| 511 | struct net_device *dev, const u8 *bssid, | 
|---|
| 512 | const u8 *ie, int ie_len, u16 reason, | 
|---|
| 513 | bool local_state_change) | 
|---|
| 514 | { | 
|---|
| 515 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 516 | struct cfg80211_deauth_request req = { | 
|---|
| 517 | .bssid = bssid, | 
|---|
| 518 | .reason_code = reason, | 
|---|
| 519 | .ie = ie, | 
|---|
| 520 | .ie_len = ie_len, | 
|---|
| 521 | .local_state_change = local_state_change, | 
|---|
| 522 | }; | 
|---|
| 523 |  | 
|---|
| 524 | lockdep_assert_wiphy(wdev->wiphy); | 
|---|
| 525 |  | 
|---|
| 526 | if (local_state_change && | 
|---|
| 527 | (!wdev->connected || | 
|---|
| 528 | !ether_addr_equal(addr1: wdev->u.client.connected_addr, addr2: bssid))) | 
|---|
| 529 | return 0; | 
|---|
| 530 |  | 
|---|
| 531 | if (ether_addr_equal(addr1: wdev->disconnect_bssid, addr2: bssid) || | 
|---|
| 532 | (wdev->connected && | 
|---|
| 533 | ether_addr_equal(addr1: wdev->u.client.connected_addr, addr2: bssid))) | 
|---|
| 534 | wdev->conn_owner_nlportid = 0; | 
|---|
| 535 |  | 
|---|
| 536 | return rdev_deauth(rdev, dev, req: &req); | 
|---|
| 537 | } | 
|---|
| 538 |  | 
|---|
| 539 | int cfg80211_mlme_disassoc(struct cfg80211_registered_device *rdev, | 
|---|
| 540 | struct net_device *dev, const u8 *ap_addr, | 
|---|
| 541 | const u8 *ie, int ie_len, u16 reason, | 
|---|
| 542 | bool local_state_change) | 
|---|
| 543 | { | 
|---|
| 544 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 545 | struct cfg80211_disassoc_request req = { | 
|---|
| 546 | .reason_code = reason, | 
|---|
| 547 | .local_state_change = local_state_change, | 
|---|
| 548 | .ie = ie, | 
|---|
| 549 | .ie_len = ie_len, | 
|---|
| 550 | .ap_addr = ap_addr, | 
|---|
| 551 | }; | 
|---|
| 552 | int err; | 
|---|
| 553 |  | 
|---|
| 554 | lockdep_assert_wiphy(wdev->wiphy); | 
|---|
| 555 |  | 
|---|
| 556 | if (!wdev->connected) | 
|---|
| 557 | return -ENOTCONN; | 
|---|
| 558 |  | 
|---|
| 559 | if (memcmp(wdev->u.client.connected_addr, ap_addr, ETH_ALEN)) | 
|---|
| 560 | return -ENOTCONN; | 
|---|
| 561 |  | 
|---|
| 562 | err = rdev_disassoc(rdev, dev, req: &req); | 
|---|
| 563 | if (err) | 
|---|
| 564 | return err; | 
|---|
| 565 |  | 
|---|
| 566 | /* driver should have reported the disassoc */ | 
|---|
| 567 | WARN_ON(wdev->connected); | 
|---|
| 568 | return 0; | 
|---|
| 569 | } | 
|---|
| 570 |  | 
|---|
| 571 | void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, | 
|---|
| 572 | struct net_device *dev) | 
|---|
| 573 | { | 
|---|
| 574 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 575 | u8 bssid[ETH_ALEN]; | 
|---|
| 576 |  | 
|---|
| 577 | lockdep_assert_wiphy(wdev->wiphy); | 
|---|
| 578 |  | 
|---|
| 579 | if (!rdev->ops->deauth) | 
|---|
| 580 | return; | 
|---|
| 581 |  | 
|---|
| 582 | if (!wdev->connected) | 
|---|
| 583 | return; | 
|---|
| 584 |  | 
|---|
| 585 | memcpy(to: bssid, from: wdev->u.client.connected_addr, ETH_ALEN); | 
|---|
| 586 | cfg80211_mlme_deauth(rdev, dev, bssid, NULL, ie_len: 0, | 
|---|
| 587 | reason: WLAN_REASON_DEAUTH_LEAVING, local_state_change: false); | 
|---|
| 588 | } | 
|---|
| 589 |  | 
|---|
| 590 | struct cfg80211_mgmt_registration { | 
|---|
| 591 | struct list_head list; | 
|---|
| 592 | struct wireless_dev *wdev; | 
|---|
| 593 |  | 
|---|
| 594 | u32 nlportid; | 
|---|
| 595 |  | 
|---|
| 596 | int match_len; | 
|---|
| 597 |  | 
|---|
| 598 | __le16 frame_type; | 
|---|
| 599 |  | 
|---|
| 600 | bool multicast_rx; | 
|---|
| 601 |  | 
|---|
| 602 | u8 match[]; | 
|---|
| 603 | }; | 
|---|
| 604 |  | 
|---|
| 605 | static void cfg80211_mgmt_registrations_update(struct wireless_dev *wdev) | 
|---|
| 606 | { | 
|---|
| 607 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); | 
|---|
| 608 | struct wireless_dev *tmp; | 
|---|
| 609 | struct cfg80211_mgmt_registration *reg; | 
|---|
| 610 | struct mgmt_frame_regs upd = {}; | 
|---|
| 611 |  | 
|---|
| 612 | lockdep_assert_held(&rdev->wiphy.mtx); | 
|---|
| 613 |  | 
|---|
| 614 | spin_lock_bh(lock: &rdev->mgmt_registrations_lock); | 
|---|
| 615 | if (!wdev->mgmt_registrations_need_update) { | 
|---|
| 616 | spin_unlock_bh(lock: &rdev->mgmt_registrations_lock); | 
|---|
| 617 | return; | 
|---|
| 618 | } | 
|---|
| 619 |  | 
|---|
| 620 | rcu_read_lock(); | 
|---|
| 621 | list_for_each_entry_rcu(tmp, &rdev->wiphy.wdev_list, list) { | 
|---|
| 622 | list_for_each_entry(reg, &tmp->mgmt_registrations, list) { | 
|---|
| 623 | u32 mask = BIT(le16_to_cpu(reg->frame_type) >> 4); | 
|---|
| 624 | u32 mcast_mask = 0; | 
|---|
| 625 |  | 
|---|
| 626 | if (reg->multicast_rx) | 
|---|
| 627 | mcast_mask = mask; | 
|---|
| 628 |  | 
|---|
| 629 | upd.global_stypes |= mask; | 
|---|
| 630 | upd.global_mcast_stypes |= mcast_mask; | 
|---|
| 631 |  | 
|---|
| 632 | if (tmp == wdev) { | 
|---|
| 633 | upd.interface_stypes |= mask; | 
|---|
| 634 | upd.interface_mcast_stypes |= mcast_mask; | 
|---|
| 635 | } | 
|---|
| 636 | } | 
|---|
| 637 | } | 
|---|
| 638 | rcu_read_unlock(); | 
|---|
| 639 |  | 
|---|
| 640 | wdev->mgmt_registrations_need_update = 0; | 
|---|
| 641 | spin_unlock_bh(lock: &rdev->mgmt_registrations_lock); | 
|---|
| 642 |  | 
|---|
| 643 | rdev_update_mgmt_frame_registrations(rdev, wdev, upd: &upd); | 
|---|
| 644 | } | 
|---|
| 645 |  | 
|---|
| 646 | void cfg80211_mgmt_registrations_update_wk(struct work_struct *wk) | 
|---|
| 647 | { | 
|---|
| 648 | struct cfg80211_registered_device *rdev; | 
|---|
| 649 | struct wireless_dev *wdev; | 
|---|
| 650 |  | 
|---|
| 651 | rdev = container_of(wk, struct cfg80211_registered_device, | 
|---|
| 652 | mgmt_registrations_update_wk); | 
|---|
| 653 |  | 
|---|
| 654 | guard(wiphy)(T: &rdev->wiphy); | 
|---|
| 655 |  | 
|---|
| 656 | list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) | 
|---|
| 657 | cfg80211_mgmt_registrations_update(wdev); | 
|---|
| 658 | } | 
|---|
| 659 |  | 
|---|
| 660 | int cfg80211_mlme_register_mgmt(struct wireless_dev *wdev, u32 snd_portid, | 
|---|
| 661 | u16 frame_type, const u8 *match_data, | 
|---|
| 662 | int match_len, bool multicast_rx, | 
|---|
| 663 | struct netlink_ext_ack *extack) | 
|---|
| 664 | { | 
|---|
| 665 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); | 
|---|
| 666 | struct cfg80211_mgmt_registration *reg, *nreg; | 
|---|
| 667 | int err = 0; | 
|---|
| 668 | u16 mgmt_type; | 
|---|
| 669 | bool update_multicast = false; | 
|---|
| 670 |  | 
|---|
| 671 | if (!wdev->wiphy->mgmt_stypes) | 
|---|
| 672 | return -EOPNOTSUPP; | 
|---|
| 673 |  | 
|---|
| 674 | if ((frame_type & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT) { | 
|---|
| 675 | NL_SET_ERR_MSG(extack, "frame type not management"); | 
|---|
| 676 | return -EINVAL; | 
|---|
| 677 | } | 
|---|
| 678 |  | 
|---|
| 679 | if (frame_type & ~(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE)) { | 
|---|
| 680 | NL_SET_ERR_MSG(extack, "Invalid frame type"); | 
|---|
| 681 | return -EINVAL; | 
|---|
| 682 | } | 
|---|
| 683 |  | 
|---|
| 684 | mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4; | 
|---|
| 685 | if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].rx & BIT(mgmt_type))) { | 
|---|
| 686 | NL_SET_ERR_MSG(extack, | 
|---|
| 687 | "Registration to specific type not supported"); | 
|---|
| 688 | return -EINVAL; | 
|---|
| 689 | } | 
|---|
| 690 |  | 
|---|
| 691 | /* | 
|---|
| 692 | * To support Pre Association Security Negotiation (PASN), registration | 
|---|
| 693 | * for authentication frames should be supported. However, as some | 
|---|
| 694 | * versions of the user space daemons wrongly register to all types of | 
|---|
| 695 | * authentication frames (which might result in unexpected behavior) | 
|---|
| 696 | * allow such registration if the request is for a specific | 
|---|
| 697 | * authentication algorithm number. | 
|---|
| 698 | */ | 
|---|
| 699 | if (wdev->iftype == NL80211_IFTYPE_STATION && | 
|---|
| 700 | (frame_type & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_AUTH && | 
|---|
| 701 | !(match_data && match_len >= 2)) { | 
|---|
| 702 | NL_SET_ERR_MSG(extack, | 
|---|
| 703 | "Authentication algorithm number required"); | 
|---|
| 704 | return -EINVAL; | 
|---|
| 705 | } | 
|---|
| 706 |  | 
|---|
| 707 | nreg = kzalloc(sizeof(*reg) + match_len, GFP_KERNEL); | 
|---|
| 708 | if (!nreg) | 
|---|
| 709 | return -ENOMEM; | 
|---|
| 710 |  | 
|---|
| 711 | spin_lock_bh(lock: &rdev->mgmt_registrations_lock); | 
|---|
| 712 |  | 
|---|
| 713 | list_for_each_entry(reg, &wdev->mgmt_registrations, list) { | 
|---|
| 714 | int mlen = min(match_len, reg->match_len); | 
|---|
| 715 |  | 
|---|
| 716 | if (frame_type != le16_to_cpu(reg->frame_type)) | 
|---|
| 717 | continue; | 
|---|
| 718 |  | 
|---|
| 719 | if (memcmp(reg->match, match_data, mlen) == 0) { | 
|---|
| 720 | if (reg->multicast_rx != multicast_rx) { | 
|---|
| 721 | update_multicast = true; | 
|---|
| 722 | reg->multicast_rx = multicast_rx; | 
|---|
| 723 | break; | 
|---|
| 724 | } | 
|---|
| 725 | NL_SET_ERR_MSG(extack, "Match already configured"); | 
|---|
| 726 | err = -EALREADY; | 
|---|
| 727 | break; | 
|---|
| 728 | } | 
|---|
| 729 | } | 
|---|
| 730 |  | 
|---|
| 731 | if (err) | 
|---|
| 732 | goto out; | 
|---|
| 733 |  | 
|---|
| 734 | if (update_multicast) { | 
|---|
| 735 | kfree(objp: nreg); | 
|---|
| 736 | } else { | 
|---|
| 737 | memcpy(to: nreg->match, from: match_data, len: match_len); | 
|---|
| 738 | nreg->match_len = match_len; | 
|---|
| 739 | nreg->nlportid = snd_portid; | 
|---|
| 740 | nreg->frame_type = cpu_to_le16(frame_type); | 
|---|
| 741 | nreg->wdev = wdev; | 
|---|
| 742 | nreg->multicast_rx = multicast_rx; | 
|---|
| 743 | list_add(new: &nreg->list, head: &wdev->mgmt_registrations); | 
|---|
| 744 | } | 
|---|
| 745 | wdev->mgmt_registrations_need_update = 1; | 
|---|
| 746 | spin_unlock_bh(lock: &rdev->mgmt_registrations_lock); | 
|---|
| 747 |  | 
|---|
| 748 | cfg80211_mgmt_registrations_update(wdev); | 
|---|
| 749 |  | 
|---|
| 750 | return 0; | 
|---|
| 751 |  | 
|---|
| 752 | out: | 
|---|
| 753 | kfree(objp: nreg); | 
|---|
| 754 | spin_unlock_bh(lock: &rdev->mgmt_registrations_lock); | 
|---|
| 755 |  | 
|---|
| 756 | return err; | 
|---|
| 757 | } | 
|---|
| 758 |  | 
|---|
| 759 | void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlportid) | 
|---|
| 760 | { | 
|---|
| 761 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 762 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 763 | struct cfg80211_mgmt_registration *reg, *tmp; | 
|---|
| 764 |  | 
|---|
| 765 | spin_lock_bh(lock: &rdev->mgmt_registrations_lock); | 
|---|
| 766 |  | 
|---|
| 767 | list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { | 
|---|
| 768 | if (reg->nlportid != nlportid) | 
|---|
| 769 | continue; | 
|---|
| 770 |  | 
|---|
| 771 | list_del(entry: ®->list); | 
|---|
| 772 | kfree(objp: reg); | 
|---|
| 773 |  | 
|---|
| 774 | wdev->mgmt_registrations_need_update = 1; | 
|---|
| 775 | schedule_work(work: &rdev->mgmt_registrations_update_wk); | 
|---|
| 776 | } | 
|---|
| 777 |  | 
|---|
| 778 | spin_unlock_bh(lock: &rdev->mgmt_registrations_lock); | 
|---|
| 779 |  | 
|---|
| 780 | if (nlportid && rdev->crit_proto_nlportid == nlportid) { | 
|---|
| 781 | rdev->crit_proto_nlportid = 0; | 
|---|
| 782 | rdev_crit_proto_stop(rdev, wdev); | 
|---|
| 783 | } | 
|---|
| 784 |  | 
|---|
| 785 | if (nlportid == wdev->ap_unexpected_nlportid) | 
|---|
| 786 | wdev->ap_unexpected_nlportid = 0; | 
|---|
| 787 | } | 
|---|
| 788 |  | 
|---|
| 789 | void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) | 
|---|
| 790 | { | 
|---|
| 791 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); | 
|---|
| 792 | struct cfg80211_mgmt_registration *reg, *tmp; | 
|---|
| 793 |  | 
|---|
| 794 | spin_lock_bh(lock: &rdev->mgmt_registrations_lock); | 
|---|
| 795 | list_for_each_entry_safe(reg, tmp, &wdev->mgmt_registrations, list) { | 
|---|
| 796 | list_del(entry: ®->list); | 
|---|
| 797 | kfree(objp: reg); | 
|---|
| 798 | } | 
|---|
| 799 | wdev->mgmt_registrations_need_update = 1; | 
|---|
| 800 | spin_unlock_bh(lock: &rdev->mgmt_registrations_lock); | 
|---|
| 801 |  | 
|---|
| 802 | cfg80211_mgmt_registrations_update(wdev); | 
|---|
| 803 | } | 
|---|
| 804 |  | 
|---|
| 805 | static bool cfg80211_allowed_address(struct wireless_dev *wdev, const u8 *addr) | 
|---|
| 806 | { | 
|---|
| 807 | int i; | 
|---|
| 808 |  | 
|---|
| 809 | for_each_valid_link(wdev, i) { | 
|---|
| 810 | if (ether_addr_equal(addr1: addr, addr2: wdev->links[i].addr)) | 
|---|
| 811 | return true; | 
|---|
| 812 | } | 
|---|
| 813 |  | 
|---|
| 814 | return ether_addr_equal(addr1: addr, addr2: wdev_address(wdev)); | 
|---|
| 815 | } | 
|---|
| 816 |  | 
|---|
| 817 | static bool cfg80211_allowed_random_address(struct wireless_dev *wdev, | 
|---|
| 818 | const struct ieee80211_mgmt *mgmt) | 
|---|
| 819 | { | 
|---|
| 820 | if (ieee80211_is_auth(fc: mgmt->frame_control) || | 
|---|
| 821 | ieee80211_is_deauth(fc: mgmt->frame_control)) { | 
|---|
| 822 | /* Allow random TA to be used with authentication and | 
|---|
| 823 | * deauthentication frames if the driver has indicated support. | 
|---|
| 824 | */ | 
|---|
| 825 | if (wiphy_ext_feature_isset( | 
|---|
| 826 | wiphy: wdev->wiphy, | 
|---|
| 827 | ftidx: NL80211_EXT_FEATURE_AUTH_AND_DEAUTH_RANDOM_TA)) | 
|---|
| 828 | return true; | 
|---|
| 829 | } else if (ieee80211_is_action(fc: mgmt->frame_control) && | 
|---|
| 830 | mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) { | 
|---|
| 831 | /* Allow random TA to be used with Public Action frames if the | 
|---|
| 832 | * driver has indicated support. | 
|---|
| 833 | */ | 
|---|
| 834 | if (!wdev->connected && | 
|---|
| 835 | wiphy_ext_feature_isset( | 
|---|
| 836 | wiphy: wdev->wiphy, | 
|---|
| 837 | ftidx: NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA)) | 
|---|
| 838 | return true; | 
|---|
| 839 |  | 
|---|
| 840 | if (wdev->connected && | 
|---|
| 841 | wiphy_ext_feature_isset( | 
|---|
| 842 | wiphy: wdev->wiphy, | 
|---|
| 843 | ftidx: NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED)) | 
|---|
| 844 | return true; | 
|---|
| 845 | } | 
|---|
| 846 |  | 
|---|
| 847 | return false; | 
|---|
| 848 | } | 
|---|
| 849 |  | 
|---|
| 850 | int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, | 
|---|
| 851 | struct wireless_dev *wdev, | 
|---|
| 852 | struct cfg80211_mgmt_tx_params *params, u64 *cookie) | 
|---|
| 853 | { | 
|---|
| 854 | const struct ieee80211_mgmt *mgmt; | 
|---|
| 855 | u16 stype; | 
|---|
| 856 |  | 
|---|
| 857 | lockdep_assert_wiphy(&rdev->wiphy); | 
|---|
| 858 |  | 
|---|
| 859 | if (!wdev->wiphy->mgmt_stypes) | 
|---|
| 860 | return -EOPNOTSUPP; | 
|---|
| 861 |  | 
|---|
| 862 | if (!rdev->ops->mgmt_tx) | 
|---|
| 863 | return -EOPNOTSUPP; | 
|---|
| 864 |  | 
|---|
| 865 | if (params->len < 24 + 1) | 
|---|
| 866 | return -EINVAL; | 
|---|
| 867 |  | 
|---|
| 868 | mgmt = (const struct ieee80211_mgmt *)params->buf; | 
|---|
| 869 |  | 
|---|
| 870 | if (!ieee80211_is_mgmt(fc: mgmt->frame_control) || | 
|---|
| 871 | ieee80211_has_order(fc: mgmt->frame_control)) | 
|---|
| 872 | return -EINVAL; | 
|---|
| 873 |  | 
|---|
| 874 | stype = le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE; | 
|---|
| 875 | if (!(wdev->wiphy->mgmt_stypes[wdev->iftype].tx & BIT(stype >> 4))) | 
|---|
| 876 | return -EINVAL; | 
|---|
| 877 |  | 
|---|
| 878 | if (ieee80211_is_action(fc: mgmt->frame_control) && | 
|---|
| 879 | mgmt->u.action.category != WLAN_CATEGORY_PUBLIC) { | 
|---|
| 880 | int err = 0; | 
|---|
| 881 |  | 
|---|
| 882 | switch (wdev->iftype) { | 
|---|
| 883 | case NL80211_IFTYPE_ADHOC: | 
|---|
| 884 | /* | 
|---|
| 885 | * check for IBSS DA must be done by driver as | 
|---|
| 886 | * cfg80211 doesn't track the stations | 
|---|
| 887 | */ | 
|---|
| 888 | if (!wdev->u.ibss.current_bss || | 
|---|
| 889 | !ether_addr_equal(addr1: wdev->u.ibss.current_bss->pub.bssid, | 
|---|
| 890 | addr2: mgmt->bssid)) { | 
|---|
| 891 | err = -ENOTCONN; | 
|---|
| 892 | break; | 
|---|
| 893 | } | 
|---|
| 894 | break; | 
|---|
| 895 | case NL80211_IFTYPE_STATION: | 
|---|
| 896 | case NL80211_IFTYPE_P2P_CLIENT: | 
|---|
| 897 | if (!wdev->connected) { | 
|---|
| 898 | err = -ENOTCONN; | 
|---|
| 899 | break; | 
|---|
| 900 | } | 
|---|
| 901 |  | 
|---|
| 902 | /* FIXME: MLD may address this differently */ | 
|---|
| 903 |  | 
|---|
| 904 | if (!ether_addr_equal(addr1: wdev->u.client.connected_addr, | 
|---|
| 905 | addr2: mgmt->bssid)) { | 
|---|
| 906 | err = -ENOTCONN; | 
|---|
| 907 | break; | 
|---|
| 908 | } | 
|---|
| 909 |  | 
|---|
| 910 | /* for station, check that DA is the AP */ | 
|---|
| 911 | if (!ether_addr_equal(addr1: wdev->u.client.connected_addr, | 
|---|
| 912 | addr2: mgmt->da)) { | 
|---|
| 913 | err = -ENOTCONN; | 
|---|
| 914 | break; | 
|---|
| 915 | } | 
|---|
| 916 | break; | 
|---|
| 917 | case NL80211_IFTYPE_AP: | 
|---|
| 918 | case NL80211_IFTYPE_P2P_GO: | 
|---|
| 919 | case NL80211_IFTYPE_AP_VLAN: | 
|---|
| 920 | if (!ether_addr_equal(addr1: mgmt->bssid, addr2: wdev_address(wdev)) && | 
|---|
| 921 | (params->link_id < 0 || | 
|---|
| 922 | !ether_addr_equal(addr1: mgmt->bssid, | 
|---|
| 923 | addr2: wdev->links[params->link_id].addr))) | 
|---|
| 924 | err = -EINVAL; | 
|---|
| 925 | break; | 
|---|
| 926 | case NL80211_IFTYPE_MESH_POINT: | 
|---|
| 927 | if (!ether_addr_equal(addr1: mgmt->sa, addr2: mgmt->bssid)) { | 
|---|
| 928 | err = -EINVAL; | 
|---|
| 929 | break; | 
|---|
| 930 | } | 
|---|
| 931 | /* | 
|---|
| 932 | * check for mesh DA must be done by driver as | 
|---|
| 933 | * cfg80211 doesn't track the stations | 
|---|
| 934 | */ | 
|---|
| 935 | break; | 
|---|
| 936 | case NL80211_IFTYPE_P2P_DEVICE: | 
|---|
| 937 | /* | 
|---|
| 938 | * fall through, P2P device only supports | 
|---|
| 939 | * public action frames | 
|---|
| 940 | */ | 
|---|
| 941 | case NL80211_IFTYPE_NAN: | 
|---|
| 942 | default: | 
|---|
| 943 | err = -EOPNOTSUPP; | 
|---|
| 944 | break; | 
|---|
| 945 | } | 
|---|
| 946 |  | 
|---|
| 947 | if (err) | 
|---|
| 948 | return err; | 
|---|
| 949 | } | 
|---|
| 950 |  | 
|---|
| 951 | if (!cfg80211_allowed_address(wdev, addr: mgmt->sa) && | 
|---|
| 952 | !cfg80211_allowed_random_address(wdev, mgmt)) | 
|---|
| 953 | return -EINVAL; | 
|---|
| 954 |  | 
|---|
| 955 | /* Transmit the management frame as requested by user space */ | 
|---|
| 956 | return rdev_mgmt_tx(rdev, wdev, params, cookie); | 
|---|
| 957 | } | 
|---|
| 958 |  | 
|---|
| 959 | bool cfg80211_rx_mgmt_ext(struct wireless_dev *wdev, | 
|---|
| 960 | struct cfg80211_rx_info *info) | 
|---|
| 961 | { | 
|---|
| 962 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 963 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 964 | struct cfg80211_mgmt_registration *reg; | 
|---|
| 965 | const struct ieee80211_txrx_stypes *stypes = | 
|---|
| 966 | &wiphy->mgmt_stypes[wdev->iftype]; | 
|---|
| 967 | struct ieee80211_mgmt *mgmt = (void *)info->buf; | 
|---|
| 968 | const u8 *data; | 
|---|
| 969 | int data_len; | 
|---|
| 970 | bool result = false; | 
|---|
| 971 | __le16 ftype = mgmt->frame_control & | 
|---|
| 972 | cpu_to_le16(IEEE80211_FCTL_FTYPE | IEEE80211_FCTL_STYPE); | 
|---|
| 973 | u16 stype; | 
|---|
| 974 |  | 
|---|
| 975 | trace_cfg80211_rx_mgmt(wdev, info); | 
|---|
| 976 | stype = (le16_to_cpu(mgmt->frame_control) & IEEE80211_FCTL_STYPE) >> 4; | 
|---|
| 977 |  | 
|---|
| 978 | if (!(stypes->rx & BIT(stype))) { | 
|---|
| 979 | trace_cfg80211_return_bool(ret: false); | 
|---|
| 980 | return false; | 
|---|
| 981 | } | 
|---|
| 982 |  | 
|---|
| 983 | data = info->buf + ieee80211_hdrlen(fc: mgmt->frame_control); | 
|---|
| 984 | data_len = info->len - ieee80211_hdrlen(fc: mgmt->frame_control); | 
|---|
| 985 |  | 
|---|
| 986 | spin_lock_bh(lock: &rdev->mgmt_registrations_lock); | 
|---|
| 987 |  | 
|---|
| 988 | list_for_each_entry(reg, &wdev->mgmt_registrations, list) { | 
|---|
| 989 | if (reg->frame_type != ftype) | 
|---|
| 990 | continue; | 
|---|
| 991 |  | 
|---|
| 992 | if (reg->match_len > data_len) | 
|---|
| 993 | continue; | 
|---|
| 994 |  | 
|---|
| 995 | if (memcmp(reg->match, data, reg->match_len)) | 
|---|
| 996 | continue; | 
|---|
| 997 |  | 
|---|
| 998 | /* found match! */ | 
|---|
| 999 |  | 
|---|
| 1000 | /* Indicate the received Action frame to user space */ | 
|---|
| 1001 | if (nl80211_send_mgmt(rdev, wdev, nlpid: reg->nlportid, info, | 
|---|
| 1002 | GFP_ATOMIC)) | 
|---|
| 1003 | continue; | 
|---|
| 1004 |  | 
|---|
| 1005 | result = true; | 
|---|
| 1006 | break; | 
|---|
| 1007 | } | 
|---|
| 1008 |  | 
|---|
| 1009 | spin_unlock_bh(lock: &rdev->mgmt_registrations_lock); | 
|---|
| 1010 |  | 
|---|
| 1011 | trace_cfg80211_return_bool(ret: result); | 
|---|
| 1012 | return result; | 
|---|
| 1013 | } | 
|---|
| 1014 | EXPORT_SYMBOL(cfg80211_rx_mgmt_ext); | 
|---|
| 1015 |  | 
|---|
| 1016 | void cfg80211_sched_dfs_chan_update(struct cfg80211_registered_device *rdev) | 
|---|
| 1017 | { | 
|---|
| 1018 | cancel_delayed_work(dwork: &rdev->dfs_update_channels_wk); | 
|---|
| 1019 | queue_delayed_work(wq: cfg80211_wq, dwork: &rdev->dfs_update_channels_wk, delay: 0); | 
|---|
| 1020 | } | 
|---|
| 1021 |  | 
|---|
| 1022 | void cfg80211_dfs_channels_update_work(struct work_struct *work) | 
|---|
| 1023 | { | 
|---|
| 1024 | struct delayed_work *delayed_work = to_delayed_work(work); | 
|---|
| 1025 | struct cfg80211_registered_device *rdev; | 
|---|
| 1026 | struct cfg80211_chan_def chandef; | 
|---|
| 1027 | struct ieee80211_supported_band *sband; | 
|---|
| 1028 | struct ieee80211_channel *c; | 
|---|
| 1029 | struct wiphy *wiphy; | 
|---|
| 1030 | bool check_again = false; | 
|---|
| 1031 | unsigned long timeout, next_time = 0; | 
|---|
| 1032 | unsigned long time_dfs_update; | 
|---|
| 1033 | enum nl80211_radar_event radar_event; | 
|---|
| 1034 | int bandid, i; | 
|---|
| 1035 |  | 
|---|
| 1036 | rdev = container_of(delayed_work, struct cfg80211_registered_device, | 
|---|
| 1037 | dfs_update_channels_wk); | 
|---|
| 1038 | wiphy = &rdev->wiphy; | 
|---|
| 1039 |  | 
|---|
| 1040 | rtnl_lock(); | 
|---|
| 1041 | for (bandid = 0; bandid < NUM_NL80211_BANDS; bandid++) { | 
|---|
| 1042 | sband = wiphy->bands[bandid]; | 
|---|
| 1043 | if (!sband) | 
|---|
| 1044 | continue; | 
|---|
| 1045 |  | 
|---|
| 1046 | for (i = 0; i < sband->n_channels; i++) { | 
|---|
| 1047 | c = &sband->channels[i]; | 
|---|
| 1048 |  | 
|---|
| 1049 | if (!(c->flags & IEEE80211_CHAN_RADAR)) | 
|---|
| 1050 | continue; | 
|---|
| 1051 |  | 
|---|
| 1052 | if (c->dfs_state != NL80211_DFS_UNAVAILABLE && | 
|---|
| 1053 | c->dfs_state != NL80211_DFS_AVAILABLE) | 
|---|
| 1054 | continue; | 
|---|
| 1055 |  | 
|---|
| 1056 | if (c->dfs_state == NL80211_DFS_UNAVAILABLE) { | 
|---|
| 1057 | time_dfs_update = IEEE80211_DFS_MIN_NOP_TIME_MS; | 
|---|
| 1058 | radar_event = NL80211_RADAR_NOP_FINISHED; | 
|---|
| 1059 | } else { | 
|---|
| 1060 | if (regulatory_pre_cac_allowed(wiphy) || | 
|---|
| 1061 | cfg80211_any_wiphy_oper_chan(wiphy, chan: c)) | 
|---|
| 1062 | continue; | 
|---|
| 1063 |  | 
|---|
| 1064 | time_dfs_update = REG_PRE_CAC_EXPIRY_GRACE_MS; | 
|---|
| 1065 | radar_event = NL80211_RADAR_PRE_CAC_EXPIRED; | 
|---|
| 1066 | } | 
|---|
| 1067 |  | 
|---|
| 1068 | timeout = c->dfs_state_entered + | 
|---|
| 1069 | msecs_to_jiffies(m: time_dfs_update); | 
|---|
| 1070 |  | 
|---|
| 1071 | if (time_after_eq(jiffies, timeout)) { | 
|---|
| 1072 | c->dfs_state = NL80211_DFS_USABLE; | 
|---|
| 1073 | c->dfs_state_entered = jiffies; | 
|---|
| 1074 |  | 
|---|
| 1075 | cfg80211_chandef_create(chandef: &chandef, channel: c, | 
|---|
| 1076 | chantype: NL80211_CHAN_NO_HT); | 
|---|
| 1077 |  | 
|---|
| 1078 | nl80211_radar_notify(rdev, chandef: &chandef, | 
|---|
| 1079 | event: radar_event, NULL, | 
|---|
| 1080 | GFP_ATOMIC); | 
|---|
| 1081 |  | 
|---|
| 1082 | regulatory_propagate_dfs_state(wiphy, chandef: &chandef, | 
|---|
| 1083 | dfs_state: c->dfs_state, | 
|---|
| 1084 | event: radar_event); | 
|---|
| 1085 | continue; | 
|---|
| 1086 | } | 
|---|
| 1087 |  | 
|---|
| 1088 | if (!check_again) | 
|---|
| 1089 | next_time = timeout - jiffies; | 
|---|
| 1090 | else | 
|---|
| 1091 | next_time = min(next_time, timeout - jiffies); | 
|---|
| 1092 | check_again = true; | 
|---|
| 1093 | } | 
|---|
| 1094 | } | 
|---|
| 1095 | rtnl_unlock(); | 
|---|
| 1096 |  | 
|---|
| 1097 | /* reschedule if there are other channels waiting to be cleared again */ | 
|---|
| 1098 | if (check_again) | 
|---|
| 1099 | queue_delayed_work(wq: cfg80211_wq, dwork: &rdev->dfs_update_channels_wk, | 
|---|
| 1100 | delay: next_time); | 
|---|
| 1101 | } | 
|---|
| 1102 |  | 
|---|
| 1103 |  | 
|---|
| 1104 | void __cfg80211_radar_event(struct wiphy *wiphy, | 
|---|
| 1105 | struct cfg80211_chan_def *chandef, | 
|---|
| 1106 | bool offchan, gfp_t gfp) | 
|---|
| 1107 | { | 
|---|
| 1108 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 1109 |  | 
|---|
| 1110 | trace_cfg80211_radar_event(wiphy, chandef, offchan); | 
|---|
| 1111 |  | 
|---|
| 1112 | /* only set the chandef supplied channel to unavailable, in | 
|---|
| 1113 | * case the radar is detected on only one of multiple channels | 
|---|
| 1114 | * spanned by the chandef. | 
|---|
| 1115 | */ | 
|---|
| 1116 | cfg80211_set_dfs_state(wiphy, chandef, dfs_state: NL80211_DFS_UNAVAILABLE); | 
|---|
| 1117 |  | 
|---|
| 1118 | if (offchan) | 
|---|
| 1119 | queue_work(wq: cfg80211_wq, work: &rdev->background_cac_abort_wk); | 
|---|
| 1120 |  | 
|---|
| 1121 | cfg80211_sched_dfs_chan_update(rdev); | 
|---|
| 1122 |  | 
|---|
| 1123 | nl80211_radar_notify(rdev, chandef, event: NL80211_RADAR_DETECTED, NULL, gfp); | 
|---|
| 1124 |  | 
|---|
| 1125 | memcpy(to: &rdev->radar_chandef, from: chandef, len: sizeof(struct cfg80211_chan_def)); | 
|---|
| 1126 | queue_work(wq: cfg80211_wq, work: &rdev->propagate_radar_detect_wk); | 
|---|
| 1127 | } | 
|---|
| 1128 | EXPORT_SYMBOL(__cfg80211_radar_event); | 
|---|
| 1129 |  | 
|---|
| 1130 | void cfg80211_cac_event(struct net_device *netdev, | 
|---|
| 1131 | const struct cfg80211_chan_def *chandef, | 
|---|
| 1132 | enum nl80211_radar_event event, gfp_t gfp, | 
|---|
| 1133 | unsigned int link_id) | 
|---|
| 1134 | { | 
|---|
| 1135 | struct wireless_dev *wdev = netdev->ieee80211_ptr; | 
|---|
| 1136 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 1137 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 1138 | unsigned long timeout; | 
|---|
| 1139 |  | 
|---|
| 1140 | if (WARN_ON(wdev->valid_links && | 
|---|
| 1141 | !(wdev->valid_links & BIT(link_id)))) | 
|---|
| 1142 | return; | 
|---|
| 1143 |  | 
|---|
| 1144 | trace_cfg80211_cac_event(netdev, evt: event, link_id); | 
|---|
| 1145 |  | 
|---|
| 1146 | if (WARN_ON(!wdev->links[link_id].cac_started && | 
|---|
| 1147 | event != NL80211_RADAR_CAC_STARTED)) | 
|---|
| 1148 | return; | 
|---|
| 1149 |  | 
|---|
| 1150 | switch (event) { | 
|---|
| 1151 | case NL80211_RADAR_CAC_FINISHED: | 
|---|
| 1152 | timeout = wdev->links[link_id].cac_start_time + | 
|---|
| 1153 | msecs_to_jiffies(m: wdev->links[link_id].cac_time_ms); | 
|---|
| 1154 | WARN_ON(!time_after_eq(jiffies, timeout)); | 
|---|
| 1155 | cfg80211_set_dfs_state(wiphy, chandef, dfs_state: NL80211_DFS_AVAILABLE); | 
|---|
| 1156 | memcpy(to: &rdev->cac_done_chandef, from: chandef, | 
|---|
| 1157 | len: sizeof(struct cfg80211_chan_def)); | 
|---|
| 1158 | queue_work(wq: cfg80211_wq, work: &rdev->propagate_cac_done_wk); | 
|---|
| 1159 | cfg80211_sched_dfs_chan_update(rdev); | 
|---|
| 1160 | fallthrough; | 
|---|
| 1161 | case NL80211_RADAR_CAC_ABORTED: | 
|---|
| 1162 | wdev->links[link_id].cac_started = false; | 
|---|
| 1163 | break; | 
|---|
| 1164 | case NL80211_RADAR_CAC_STARTED: | 
|---|
| 1165 | wdev->links[link_id].cac_started = true; | 
|---|
| 1166 | break; | 
|---|
| 1167 | default: | 
|---|
| 1168 | WARN_ON(1); | 
|---|
| 1169 | return; | 
|---|
| 1170 | } | 
|---|
| 1171 |  | 
|---|
| 1172 | nl80211_radar_notify(rdev, chandef, event, netdev, gfp); | 
|---|
| 1173 | } | 
|---|
| 1174 | EXPORT_SYMBOL(cfg80211_cac_event); | 
|---|
| 1175 |  | 
|---|
| 1176 | static void | 
|---|
| 1177 | __cfg80211_background_cac_event(struct cfg80211_registered_device *rdev, | 
|---|
| 1178 | struct wireless_dev *wdev, | 
|---|
| 1179 | const struct cfg80211_chan_def *chandef, | 
|---|
| 1180 | enum nl80211_radar_event event) | 
|---|
| 1181 | { | 
|---|
| 1182 | struct wiphy *wiphy = &rdev->wiphy; | 
|---|
| 1183 | struct net_device *netdev; | 
|---|
| 1184 |  | 
|---|
| 1185 | lockdep_assert_wiphy(&rdev->wiphy); | 
|---|
| 1186 |  | 
|---|
| 1187 | if (!cfg80211_chandef_valid(chandef)) | 
|---|
| 1188 | return; | 
|---|
| 1189 |  | 
|---|
| 1190 | if (!rdev->background_radar_wdev) | 
|---|
| 1191 | return; | 
|---|
| 1192 |  | 
|---|
| 1193 | switch (event) { | 
|---|
| 1194 | case NL80211_RADAR_CAC_FINISHED: | 
|---|
| 1195 | cfg80211_set_dfs_state(wiphy, chandef, dfs_state: NL80211_DFS_AVAILABLE); | 
|---|
| 1196 | memcpy(to: &rdev->cac_done_chandef, from: chandef, len: sizeof(*chandef)); | 
|---|
| 1197 | queue_work(wq: cfg80211_wq, work: &rdev->propagate_cac_done_wk); | 
|---|
| 1198 | cfg80211_sched_dfs_chan_update(rdev); | 
|---|
| 1199 | wdev = rdev->background_radar_wdev; | 
|---|
| 1200 | break; | 
|---|
| 1201 | case NL80211_RADAR_CAC_ABORTED: | 
|---|
| 1202 | if (!cancel_delayed_work(dwork: &rdev->background_cac_done_wk)) | 
|---|
| 1203 | return; | 
|---|
| 1204 | wdev = rdev->background_radar_wdev; | 
|---|
| 1205 | break; | 
|---|
| 1206 | case NL80211_RADAR_CAC_STARTED: | 
|---|
| 1207 | break; | 
|---|
| 1208 | default: | 
|---|
| 1209 | return; | 
|---|
| 1210 | } | 
|---|
| 1211 |  | 
|---|
| 1212 | netdev = wdev ? wdev->netdev : NULL; | 
|---|
| 1213 | nl80211_radar_notify(rdev, chandef, event, netdev, GFP_KERNEL); | 
|---|
| 1214 | } | 
|---|
| 1215 |  | 
|---|
| 1216 | static void | 
|---|
| 1217 | cfg80211_background_cac_event(struct cfg80211_registered_device *rdev, | 
|---|
| 1218 | const struct cfg80211_chan_def *chandef, | 
|---|
| 1219 | enum nl80211_radar_event event) | 
|---|
| 1220 | { | 
|---|
| 1221 | guard(wiphy)(T: &rdev->wiphy); | 
|---|
| 1222 |  | 
|---|
| 1223 | __cfg80211_background_cac_event(rdev, wdev: rdev->background_radar_wdev, | 
|---|
| 1224 | chandef, event); | 
|---|
| 1225 | } | 
|---|
| 1226 |  | 
|---|
| 1227 | void cfg80211_background_cac_done_wk(struct work_struct *work) | 
|---|
| 1228 | { | 
|---|
| 1229 | struct delayed_work *delayed_work = to_delayed_work(work); | 
|---|
| 1230 | struct cfg80211_registered_device *rdev; | 
|---|
| 1231 |  | 
|---|
| 1232 | rdev = container_of(delayed_work, struct cfg80211_registered_device, | 
|---|
| 1233 | background_cac_done_wk); | 
|---|
| 1234 | cfg80211_background_cac_event(rdev, chandef: &rdev->background_radar_chandef, | 
|---|
| 1235 | event: NL80211_RADAR_CAC_FINISHED); | 
|---|
| 1236 | } | 
|---|
| 1237 |  | 
|---|
| 1238 | void cfg80211_background_cac_abort_wk(struct work_struct *work) | 
|---|
| 1239 | { | 
|---|
| 1240 | struct cfg80211_registered_device *rdev; | 
|---|
| 1241 |  | 
|---|
| 1242 | rdev = container_of(work, struct cfg80211_registered_device, | 
|---|
| 1243 | background_cac_abort_wk); | 
|---|
| 1244 | cfg80211_background_cac_event(rdev, chandef: &rdev->background_radar_chandef, | 
|---|
| 1245 | event: NL80211_RADAR_CAC_ABORTED); | 
|---|
| 1246 | } | 
|---|
| 1247 |  | 
|---|
| 1248 | void cfg80211_background_cac_abort(struct wiphy *wiphy) | 
|---|
| 1249 | { | 
|---|
| 1250 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 1251 |  | 
|---|
| 1252 | queue_work(wq: cfg80211_wq, work: &rdev->background_cac_abort_wk); | 
|---|
| 1253 | } | 
|---|
| 1254 | EXPORT_SYMBOL(cfg80211_background_cac_abort); | 
|---|
| 1255 |  | 
|---|
| 1256 | int | 
|---|
| 1257 | cfg80211_start_background_radar_detection(struct cfg80211_registered_device *rdev, | 
|---|
| 1258 | struct wireless_dev *wdev, | 
|---|
| 1259 | struct cfg80211_chan_def *chandef) | 
|---|
| 1260 | { | 
|---|
| 1261 | unsigned int cac_time_ms; | 
|---|
| 1262 | int err; | 
|---|
| 1263 |  | 
|---|
| 1264 | lockdep_assert_wiphy(&rdev->wiphy); | 
|---|
| 1265 |  | 
|---|
| 1266 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 1267 | ftidx: NL80211_EXT_FEATURE_RADAR_BACKGROUND)) | 
|---|
| 1268 | return -EOPNOTSUPP; | 
|---|
| 1269 |  | 
|---|
| 1270 | /* Offchannel chain already locked by another wdev */ | 
|---|
| 1271 | if (rdev->background_radar_wdev && rdev->background_radar_wdev != wdev) | 
|---|
| 1272 | return -EBUSY; | 
|---|
| 1273 |  | 
|---|
| 1274 | /* CAC already in progress on the offchannel chain */ | 
|---|
| 1275 | if (rdev->background_radar_wdev == wdev && | 
|---|
| 1276 | delayed_work_pending(&rdev->background_cac_done_wk)) | 
|---|
| 1277 | return -EBUSY; | 
|---|
| 1278 |  | 
|---|
| 1279 | err = rdev_set_radar_background(rdev, chandef); | 
|---|
| 1280 | if (err) | 
|---|
| 1281 | return err; | 
|---|
| 1282 |  | 
|---|
| 1283 | cac_time_ms = cfg80211_chandef_dfs_cac_time(wiphy: &rdev->wiphy, chandef); | 
|---|
| 1284 | if (!cac_time_ms) | 
|---|
| 1285 | cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; | 
|---|
| 1286 |  | 
|---|
| 1287 | rdev->background_radar_chandef = *chandef; | 
|---|
| 1288 | rdev->background_radar_wdev = wdev; /* Get offchain ownership */ | 
|---|
| 1289 |  | 
|---|
| 1290 | __cfg80211_background_cac_event(rdev, wdev, chandef, | 
|---|
| 1291 | event: NL80211_RADAR_CAC_STARTED); | 
|---|
| 1292 | queue_delayed_work(wq: cfg80211_wq, dwork: &rdev->background_cac_done_wk, | 
|---|
| 1293 | delay: msecs_to_jiffies(m: cac_time_ms)); | 
|---|
| 1294 |  | 
|---|
| 1295 | return 0; | 
|---|
| 1296 | } | 
|---|
| 1297 |  | 
|---|
| 1298 | void cfg80211_stop_background_radar_detection(struct wireless_dev *wdev) | 
|---|
| 1299 | { | 
|---|
| 1300 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 1301 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 1302 |  | 
|---|
| 1303 | lockdep_assert_wiphy(wiphy); | 
|---|
| 1304 |  | 
|---|
| 1305 | if (wdev != rdev->background_radar_wdev) | 
|---|
| 1306 | return; | 
|---|
| 1307 |  | 
|---|
| 1308 | rdev_set_radar_background(rdev, NULL); | 
|---|
| 1309 | rdev->background_radar_wdev = NULL; /* Release offchain ownership */ | 
|---|
| 1310 |  | 
|---|
| 1311 | __cfg80211_background_cac_event(rdev, wdev, | 
|---|
| 1312 | chandef: &rdev->background_radar_chandef, | 
|---|
| 1313 | event: NL80211_RADAR_CAC_ABORTED); | 
|---|
| 1314 | } | 
|---|
| 1315 |  | 
|---|
| 1316 | int cfg80211_assoc_ml_reconf(struct cfg80211_registered_device *rdev, | 
|---|
| 1317 | struct net_device *dev, | 
|---|
| 1318 | struct cfg80211_ml_reconf_req *req) | 
|---|
| 1319 | { | 
|---|
| 1320 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 1321 | int err; | 
|---|
| 1322 |  | 
|---|
| 1323 | lockdep_assert_wiphy(wdev->wiphy); | 
|---|
| 1324 |  | 
|---|
| 1325 | err = rdev_assoc_ml_reconf(rdev, dev, req); | 
|---|
| 1326 | if (!err) { | 
|---|
| 1327 | int link_id; | 
|---|
| 1328 |  | 
|---|
| 1329 | for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; | 
|---|
| 1330 | link_id++) { | 
|---|
| 1331 | if (!req->add_links[link_id].bss) | 
|---|
| 1332 | continue; | 
|---|
| 1333 |  | 
|---|
| 1334 | cfg80211_ref_bss(wiphy: &rdev->wiphy, bss: req->add_links[link_id].bss); | 
|---|
| 1335 | cfg80211_hold_bss(bss: bss_from_pub(pub: req->add_links[link_id].bss)); | 
|---|
| 1336 | } | 
|---|
| 1337 | } | 
|---|
| 1338 |  | 
|---|
| 1339 | return err; | 
|---|
| 1340 | } | 
|---|
| 1341 |  | 
|---|
| 1342 | void cfg80211_mlo_reconf_add_done(struct net_device *dev, | 
|---|
| 1343 | struct cfg80211_mlo_reconf_done_data *data) | 
|---|
| 1344 | { | 
|---|
| 1345 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 1346 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 1347 | int link_id; | 
|---|
| 1348 |  | 
|---|
| 1349 | lockdep_assert_wiphy(wiphy); | 
|---|
| 1350 |  | 
|---|
| 1351 | trace_cfg80211_mlo_reconf_add_done(netdev: dev, link_mask: data->added_links, | 
|---|
| 1352 | buf: data->buf, len: data->len, | 
|---|
| 1353 | driver_initiated: data->driver_initiated); | 
|---|
| 1354 |  | 
|---|
| 1355 | if (WARN_ON(!wdev->valid_links)) | 
|---|
| 1356 | return; | 
|---|
| 1357 |  | 
|---|
| 1358 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && | 
|---|
| 1359 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) | 
|---|
| 1360 | return; | 
|---|
| 1361 |  | 
|---|
| 1362 | /* validate that a BSS is given for each added link */ | 
|---|
| 1363 | for (link_id = 0; link_id < ARRAY_SIZE(data->links); link_id++) { | 
|---|
| 1364 | struct cfg80211_bss *bss = data->links[link_id].bss; | 
|---|
| 1365 |  | 
|---|
| 1366 | if (!(data->added_links & BIT(link_id))) | 
|---|
| 1367 | continue; | 
|---|
| 1368 |  | 
|---|
| 1369 | if (WARN_ON(!bss)) | 
|---|
| 1370 | return; | 
|---|
| 1371 | } | 
|---|
| 1372 |  | 
|---|
| 1373 | for (link_id = 0; link_id < ARRAY_SIZE(data->links); link_id++) { | 
|---|
| 1374 | struct cfg80211_bss *bss = data->links[link_id].bss; | 
|---|
| 1375 |  | 
|---|
| 1376 | if (!bss) | 
|---|
| 1377 | continue; | 
|---|
| 1378 |  | 
|---|
| 1379 | if (data->added_links & BIT(link_id)) { | 
|---|
| 1380 | wdev->links[link_id].client.current_bss = | 
|---|
| 1381 | bss_from_pub(pub: bss); | 
|---|
| 1382 |  | 
|---|
| 1383 | if (data->driver_initiated) | 
|---|
| 1384 | cfg80211_hold_bss(bss: bss_from_pub(pub: bss)); | 
|---|
| 1385 |  | 
|---|
| 1386 | memcpy(to: wdev->links[link_id].addr, | 
|---|
| 1387 | from: data->links[link_id].addr, | 
|---|
| 1388 | ETH_ALEN); | 
|---|
| 1389 | } else { | 
|---|
| 1390 | if (!data->driver_initiated) | 
|---|
| 1391 | cfg80211_unhold_bss(bss: bss_from_pub(pub: bss)); | 
|---|
| 1392 |  | 
|---|
| 1393 | cfg80211_put_bss(wiphy, bss); | 
|---|
| 1394 | } | 
|---|
| 1395 | } | 
|---|
| 1396 |  | 
|---|
| 1397 | wdev->valid_links |= data->added_links; | 
|---|
| 1398 | nl80211_mlo_reconf_add_done(dev, data); | 
|---|
| 1399 | } | 
|---|
| 1400 | EXPORT_SYMBOL(cfg80211_mlo_reconf_add_done); | 
|---|
| 1401 |  | 
|---|