| 1 | // SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 2 | /* | 
|---|
| 3 | * This is the new netlink-based wireless configuration interface. | 
|---|
| 4 | * | 
|---|
| 5 | * Copyright 2006-2010	Johannes Berg <johannes@sipsolutions.net> | 
|---|
| 6 | * Copyright 2013-2014  Intel Mobile Communications GmbH | 
|---|
| 7 | * Copyright 2015-2017	Intel Deutschland GmbH | 
|---|
| 8 | * Copyright (C) 2018-2025 Intel Corporation | 
|---|
| 9 | */ | 
|---|
| 10 |  | 
|---|
| 11 | #include <linux/if.h> | 
|---|
| 12 | #include <linux/module.h> | 
|---|
| 13 | #include <linux/err.h> | 
|---|
| 14 | #include <linux/slab.h> | 
|---|
| 15 | #include <linux/list.h> | 
|---|
| 16 | #include <linux/if_ether.h> | 
|---|
| 17 | #include <linux/ieee80211.h> | 
|---|
| 18 | #include <linux/nl80211.h> | 
|---|
| 19 | #include <linux/rtnetlink.h> | 
|---|
| 20 | #include <linux/netlink.h> | 
|---|
| 21 | #include <linux/nospec.h> | 
|---|
| 22 | #include <linux/etherdevice.h> | 
|---|
| 23 | #include <linux/if_vlan.h> | 
|---|
| 24 | #include <net/net_namespace.h> | 
|---|
| 25 | #include <net/genetlink.h> | 
|---|
| 26 | #include <net/cfg80211.h> | 
|---|
| 27 | #include <net/sock.h> | 
|---|
| 28 | #include <net/inet_connection_sock.h> | 
|---|
| 29 | #include "core.h" | 
|---|
| 30 | #include "nl80211.h" | 
|---|
| 31 | #include "reg.h" | 
|---|
| 32 | #include "rdev-ops.h" | 
|---|
| 33 |  | 
|---|
| 34 | static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, | 
|---|
| 35 | struct genl_info *info, | 
|---|
| 36 | struct cfg80211_crypto_settings *settings, | 
|---|
| 37 | int cipher_limit); | 
|---|
| 38 |  | 
|---|
| 39 | /* the netlink family */ | 
|---|
| 40 | static struct genl_family nl80211_fam; | 
|---|
| 41 |  | 
|---|
| 42 | /* multicast groups */ | 
|---|
| 43 | enum nl80211_multicast_groups { | 
|---|
| 44 | NL80211_MCGRP_CONFIG, | 
|---|
| 45 | NL80211_MCGRP_SCAN, | 
|---|
| 46 | NL80211_MCGRP_REGULATORY, | 
|---|
| 47 | NL80211_MCGRP_MLME, | 
|---|
| 48 | NL80211_MCGRP_VENDOR, | 
|---|
| 49 | NL80211_MCGRP_NAN, | 
|---|
| 50 | NL80211_MCGRP_TESTMODE /* keep last - ifdef! */ | 
|---|
| 51 | }; | 
|---|
| 52 |  | 
|---|
| 53 | static const struct genl_multicast_group nl80211_mcgrps[] = { | 
|---|
| 54 | [NL80211_MCGRP_CONFIG] = { .name = NL80211_MULTICAST_GROUP_CONFIG }, | 
|---|
| 55 | [NL80211_MCGRP_SCAN] = { .name = NL80211_MULTICAST_GROUP_SCAN }, | 
|---|
| 56 | [NL80211_MCGRP_REGULATORY] = { .name = NL80211_MULTICAST_GROUP_REG }, | 
|---|
| 57 | [NL80211_MCGRP_MLME] = { .name = NL80211_MULTICAST_GROUP_MLME }, | 
|---|
| 58 | [NL80211_MCGRP_VENDOR] = { .name = NL80211_MULTICAST_GROUP_VENDOR }, | 
|---|
| 59 | [NL80211_MCGRP_NAN] = { .name = NL80211_MULTICAST_GROUP_NAN }, | 
|---|
| 60 | #ifdef CONFIG_NL80211_TESTMODE | 
|---|
| 61 | [NL80211_MCGRP_TESTMODE] = { .name = NL80211_MULTICAST_GROUP_TESTMODE } | 
|---|
| 62 | #endif | 
|---|
| 63 | }; | 
|---|
| 64 |  | 
|---|
| 65 | /* returns ERR_PTR values */ | 
|---|
| 66 | static struct wireless_dev * | 
|---|
| 67 | __cfg80211_wdev_from_attrs(struct cfg80211_registered_device *rdev, | 
|---|
| 68 | struct net *netns, struct nlattr **attrs) | 
|---|
| 69 | { | 
|---|
| 70 | struct wireless_dev *result = NULL; | 
|---|
| 71 | bool have_ifidx = attrs[NL80211_ATTR_IFINDEX]; | 
|---|
| 72 | bool have_wdev_id = attrs[NL80211_ATTR_WDEV]; | 
|---|
| 73 | u64 wdev_id = 0; | 
|---|
| 74 | int wiphy_idx = -1; | 
|---|
| 75 | int ifidx = -1; | 
|---|
| 76 |  | 
|---|
| 77 | if (!have_ifidx && !have_wdev_id) | 
|---|
| 78 | return ERR_PTR(error: -EINVAL); | 
|---|
| 79 |  | 
|---|
| 80 | if (have_ifidx) | 
|---|
| 81 | ifidx = nla_get_u32(nla: attrs[NL80211_ATTR_IFINDEX]); | 
|---|
| 82 | if (have_wdev_id) { | 
|---|
| 83 | wdev_id = nla_get_u64(nla: attrs[NL80211_ATTR_WDEV]); | 
|---|
| 84 | wiphy_idx = wdev_id >> 32; | 
|---|
| 85 | } | 
|---|
| 86 |  | 
|---|
| 87 | if (rdev) { | 
|---|
| 88 | struct wireless_dev *wdev; | 
|---|
| 89 |  | 
|---|
| 90 | lockdep_assert_held(&rdev->wiphy.mtx); | 
|---|
| 91 |  | 
|---|
| 92 | list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { | 
|---|
| 93 | if (have_ifidx && wdev->netdev && | 
|---|
| 94 | wdev->netdev->ifindex == ifidx) { | 
|---|
| 95 | result = wdev; | 
|---|
| 96 | break; | 
|---|
| 97 | } | 
|---|
| 98 | if (have_wdev_id && wdev->identifier == (u32)wdev_id) { | 
|---|
| 99 | result = wdev; | 
|---|
| 100 | break; | 
|---|
| 101 | } | 
|---|
| 102 | } | 
|---|
| 103 |  | 
|---|
| 104 | return result ?: ERR_PTR(error: -ENODEV); | 
|---|
| 105 | } | 
|---|
| 106 |  | 
|---|
| 107 | ASSERT_RTNL(); | 
|---|
| 108 |  | 
|---|
| 109 | for_each_rdev(rdev) { | 
|---|
| 110 | struct wireless_dev *wdev; | 
|---|
| 111 |  | 
|---|
| 112 | if (wiphy_net(wiphy: &rdev->wiphy) != netns) | 
|---|
| 113 | continue; | 
|---|
| 114 |  | 
|---|
| 115 | if (have_wdev_id && rdev->wiphy_idx != wiphy_idx) | 
|---|
| 116 | continue; | 
|---|
| 117 |  | 
|---|
| 118 | list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { | 
|---|
| 119 | if (have_ifidx && wdev->netdev && | 
|---|
| 120 | wdev->netdev->ifindex == ifidx) { | 
|---|
| 121 | result = wdev; | 
|---|
| 122 | break; | 
|---|
| 123 | } | 
|---|
| 124 | if (have_wdev_id && wdev->identifier == (u32)wdev_id) { | 
|---|
| 125 | result = wdev; | 
|---|
| 126 | break; | 
|---|
| 127 | } | 
|---|
| 128 | } | 
|---|
| 129 |  | 
|---|
| 130 | if (result) | 
|---|
| 131 | break; | 
|---|
| 132 | } | 
|---|
| 133 |  | 
|---|
| 134 | if (result) | 
|---|
| 135 | return result; | 
|---|
| 136 | return ERR_PTR(error: -ENODEV); | 
|---|
| 137 | } | 
|---|
| 138 |  | 
|---|
| 139 | static struct cfg80211_registered_device * | 
|---|
| 140 | __cfg80211_rdev_from_attrs(struct net *netns, struct nlattr **attrs) | 
|---|
| 141 | { | 
|---|
| 142 | struct cfg80211_registered_device *rdev = NULL, *tmp; | 
|---|
| 143 | struct net_device *netdev; | 
|---|
| 144 |  | 
|---|
| 145 | ASSERT_RTNL(); | 
|---|
| 146 |  | 
|---|
| 147 | if (!attrs[NL80211_ATTR_WIPHY] && | 
|---|
| 148 | !attrs[NL80211_ATTR_IFINDEX] && | 
|---|
| 149 | !attrs[NL80211_ATTR_WDEV]) | 
|---|
| 150 | return ERR_PTR(error: -EINVAL); | 
|---|
| 151 |  | 
|---|
| 152 | if (attrs[NL80211_ATTR_WIPHY]) | 
|---|
| 153 | rdev = cfg80211_rdev_by_wiphy_idx( | 
|---|
| 154 | wiphy_idx: nla_get_u32(nla: attrs[NL80211_ATTR_WIPHY])); | 
|---|
| 155 |  | 
|---|
| 156 | if (attrs[NL80211_ATTR_WDEV]) { | 
|---|
| 157 | u64 wdev_id = nla_get_u64(nla: attrs[NL80211_ATTR_WDEV]); | 
|---|
| 158 | struct wireless_dev *wdev; | 
|---|
| 159 | bool found = false; | 
|---|
| 160 |  | 
|---|
| 161 | tmp = cfg80211_rdev_by_wiphy_idx(wiphy_idx: wdev_id >> 32); | 
|---|
| 162 | if (tmp) { | 
|---|
| 163 | /* make sure wdev exists */ | 
|---|
| 164 | list_for_each_entry(wdev, &tmp->wiphy.wdev_list, list) { | 
|---|
| 165 | if (wdev->identifier != (u32)wdev_id) | 
|---|
| 166 | continue; | 
|---|
| 167 | found = true; | 
|---|
| 168 | break; | 
|---|
| 169 | } | 
|---|
| 170 |  | 
|---|
| 171 | if (!found) | 
|---|
| 172 | tmp = NULL; | 
|---|
| 173 |  | 
|---|
| 174 | if (rdev && tmp != rdev) | 
|---|
| 175 | return ERR_PTR(error: -EINVAL); | 
|---|
| 176 | rdev = tmp; | 
|---|
| 177 | } | 
|---|
| 178 | } | 
|---|
| 179 |  | 
|---|
| 180 | if (attrs[NL80211_ATTR_IFINDEX]) { | 
|---|
| 181 | int ifindex = nla_get_u32(nla: attrs[NL80211_ATTR_IFINDEX]); | 
|---|
| 182 |  | 
|---|
| 183 | netdev = __dev_get_by_index(net: netns, ifindex); | 
|---|
| 184 | if (netdev) { | 
|---|
| 185 | if (netdev->ieee80211_ptr) | 
|---|
| 186 | tmp = wiphy_to_rdev( | 
|---|
| 187 | wiphy: netdev->ieee80211_ptr->wiphy); | 
|---|
| 188 | else | 
|---|
| 189 | tmp = NULL; | 
|---|
| 190 |  | 
|---|
| 191 | /* not wireless device -- return error */ | 
|---|
| 192 | if (!tmp) | 
|---|
| 193 | return ERR_PTR(error: -EINVAL); | 
|---|
| 194 |  | 
|---|
| 195 | /* mismatch -- return error */ | 
|---|
| 196 | if (rdev && tmp != rdev) | 
|---|
| 197 | return ERR_PTR(error: -EINVAL); | 
|---|
| 198 |  | 
|---|
| 199 | rdev = tmp; | 
|---|
| 200 | } | 
|---|
| 201 | } | 
|---|
| 202 |  | 
|---|
| 203 | if (!rdev) | 
|---|
| 204 | return ERR_PTR(error: -ENODEV); | 
|---|
| 205 |  | 
|---|
| 206 | if (netns != wiphy_net(wiphy: &rdev->wiphy)) | 
|---|
| 207 | return ERR_PTR(error: -ENODEV); | 
|---|
| 208 |  | 
|---|
| 209 | return rdev; | 
|---|
| 210 | } | 
|---|
| 211 |  | 
|---|
| 212 | /* | 
|---|
| 213 | * This function returns a pointer to the driver | 
|---|
| 214 | * that the genl_info item that is passed refers to. | 
|---|
| 215 | * | 
|---|
| 216 | * The result of this can be a PTR_ERR and hence must | 
|---|
| 217 | * be checked with IS_ERR() for errors. | 
|---|
| 218 | */ | 
|---|
| 219 | static struct cfg80211_registered_device * | 
|---|
| 220 | cfg80211_get_dev_from_info(struct net *netns, struct genl_info *info) | 
|---|
| 221 | { | 
|---|
| 222 | return __cfg80211_rdev_from_attrs(netns, attrs: info->attrs); | 
|---|
| 223 | } | 
|---|
| 224 |  | 
|---|
| 225 | static int validate_beacon_head(const struct nlattr *attr, | 
|---|
| 226 | struct netlink_ext_ack *extack) | 
|---|
| 227 | { | 
|---|
| 228 | const u8 *data = nla_data(nla: attr); | 
|---|
| 229 | unsigned int len = nla_len(nla: attr); | 
|---|
| 230 | const struct element *elem; | 
|---|
| 231 | const struct ieee80211_mgmt *mgmt = (void *)data; | 
|---|
| 232 | const struct ieee80211_ext *ext; | 
|---|
| 233 | unsigned int fixedlen, hdrlen; | 
|---|
| 234 | bool s1g_bcn; | 
|---|
| 235 |  | 
|---|
| 236 | if (len < offsetofend(typeof(*mgmt), frame_control)) | 
|---|
| 237 | goto err; | 
|---|
| 238 |  | 
|---|
| 239 | s1g_bcn = ieee80211_is_s1g_beacon(fc: mgmt->frame_control); | 
|---|
| 240 | if (s1g_bcn) { | 
|---|
| 241 | ext = (struct ieee80211_ext *)mgmt; | 
|---|
| 242 | fixedlen = | 
|---|
| 243 | offsetof(struct ieee80211_ext, u.s1g_beacon.variable) + | 
|---|
| 244 | ieee80211_s1g_optional_len(fc: ext->frame_control); | 
|---|
| 245 | hdrlen = offsetof(struct ieee80211_ext, u.s1g_beacon); | 
|---|
| 246 | } else { | 
|---|
| 247 | fixedlen = offsetof(struct ieee80211_mgmt, | 
|---|
| 248 | u.beacon.variable); | 
|---|
| 249 | hdrlen = offsetof(struct ieee80211_mgmt, u.beacon); | 
|---|
| 250 | } | 
|---|
| 251 |  | 
|---|
| 252 | if (len < fixedlen) | 
|---|
| 253 | goto err; | 
|---|
| 254 |  | 
|---|
| 255 | if (ieee80211_hdrlen(fc: mgmt->frame_control) != hdrlen) | 
|---|
| 256 | goto err; | 
|---|
| 257 |  | 
|---|
| 258 | data += fixedlen; | 
|---|
| 259 | len -= fixedlen; | 
|---|
| 260 |  | 
|---|
| 261 | for_each_element(elem, data, len) { | 
|---|
| 262 | /* nothing */ | 
|---|
| 263 | } | 
|---|
| 264 |  | 
|---|
| 265 | if (for_each_element_completed(element: elem, data, datalen: len)) | 
|---|
| 266 | return 0; | 
|---|
| 267 |  | 
|---|
| 268 | err: | 
|---|
| 269 | NL_SET_ERR_MSG_ATTR(extack, attr, "malformed beacon head"); | 
|---|
| 270 | return -EINVAL; | 
|---|
| 271 | } | 
|---|
| 272 |  | 
|---|
| 273 | static int validate_ie_attr(const struct nlattr *attr, | 
|---|
| 274 | struct netlink_ext_ack *extack) | 
|---|
| 275 | { | 
|---|
| 276 | const u8 *data = nla_data(nla: attr); | 
|---|
| 277 | unsigned int len = nla_len(nla: attr); | 
|---|
| 278 | const struct element *elem; | 
|---|
| 279 |  | 
|---|
| 280 | for_each_element(elem, data, len) { | 
|---|
| 281 | /* nothing */ | 
|---|
| 282 | } | 
|---|
| 283 |  | 
|---|
| 284 | if (for_each_element_completed(element: elem, data, datalen: len)) | 
|---|
| 285 | return 0; | 
|---|
| 286 |  | 
|---|
| 287 | NL_SET_ERR_MSG_ATTR(extack, attr, "malformed information elements"); | 
|---|
| 288 | return -EINVAL; | 
|---|
| 289 | } | 
|---|
| 290 |  | 
|---|
| 291 | static int validate_he_capa(const struct nlattr *attr, | 
|---|
| 292 | struct netlink_ext_ack *extack) | 
|---|
| 293 | { | 
|---|
| 294 | if (!ieee80211_he_capa_size_ok(data: nla_data(nla: attr), len: nla_len(nla: attr))) | 
|---|
| 295 | return -EINVAL; | 
|---|
| 296 |  | 
|---|
| 297 | return 0; | 
|---|
| 298 | } | 
|---|
| 299 |  | 
|---|
| 300 | static int validate_supported_selectors(const struct nlattr *attr, | 
|---|
| 301 | struct netlink_ext_ack *extack) | 
|---|
| 302 | { | 
|---|
| 303 | const u8 *supported_selectors = nla_data(nla: attr); | 
|---|
| 304 | u8 supported_selectors_len = nla_len(nla: attr); | 
|---|
| 305 |  | 
|---|
| 306 | /* The top bit must not be set as it is not part of the selector */ | 
|---|
| 307 | for (int i = 0; i < supported_selectors_len; i++) { | 
|---|
| 308 | if (supported_selectors[i] & 0x80) | 
|---|
| 309 | return -EINVAL; | 
|---|
| 310 | } | 
|---|
| 311 |  | 
|---|
| 312 | return 0; | 
|---|
| 313 | } | 
|---|
| 314 |  | 
|---|
| 315 | static int validate_nan_cluster_id(const struct nlattr *attr, | 
|---|
| 316 | struct netlink_ext_ack *extack) | 
|---|
| 317 | { | 
|---|
| 318 | const u8 *data = nla_data(nla: attr); | 
|---|
| 319 | unsigned int len = nla_len(nla: attr); | 
|---|
| 320 | static const u8 cluster_id_prefix[4] = {0x50, 0x6f, 0x9a, 0x1}; | 
|---|
| 321 |  | 
|---|
| 322 | if (len != ETH_ALEN) { | 
|---|
| 323 | NL_SET_ERR_MSG_ATTR(extack, attr, "bad cluster id length"); | 
|---|
| 324 | return -EINVAL; | 
|---|
| 325 | } | 
|---|
| 326 |  | 
|---|
| 327 | if (memcmp(data, cluster_id_prefix, sizeof(cluster_id_prefix))) { | 
|---|
| 328 | NL_SET_ERR_MSG_ATTR(extack, attr, "invalid cluster id prefix"); | 
|---|
| 329 | return -EINVAL; | 
|---|
| 330 | } | 
|---|
| 331 |  | 
|---|
| 332 | return 0; | 
|---|
| 333 | } | 
|---|
| 334 |  | 
|---|
| 335 | /* policy for the attributes */ | 
|---|
| 336 | static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR]; | 
|---|
| 337 |  | 
|---|
| 338 | static const struct nla_policy | 
|---|
| 339 | nl80211_ftm_responder_policy[NL80211_FTM_RESP_ATTR_MAX + 1] = { | 
|---|
| 340 | [NL80211_FTM_RESP_ATTR_ENABLED] = { .type = NLA_FLAG, }, | 
|---|
| 341 | [NL80211_FTM_RESP_ATTR_LCI] = { .type = NLA_BINARY, | 
|---|
| 342 | .len = U8_MAX }, | 
|---|
| 343 | [NL80211_FTM_RESP_ATTR_CIVICLOC] = { .type = NLA_BINARY, | 
|---|
| 344 | .len = U8_MAX }, | 
|---|
| 345 | }; | 
|---|
| 346 |  | 
|---|
| 347 | static const struct nla_policy | 
|---|
| 348 | nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = { | 
|---|
| 349 | [NL80211_PMSR_FTM_REQ_ATTR_ASAP] = { .type = NLA_FLAG }, | 
|---|
| 350 | [NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE] = { .type = NLA_U32 }, | 
|---|
| 351 | [NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP] = | 
|---|
| 352 | NLA_POLICY_MAX(NLA_U8, 15), | 
|---|
| 353 | [NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD] = { .type = NLA_U16 }, | 
|---|
| 354 | [NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION] = | 
|---|
| 355 | NLA_POLICY_MAX(NLA_U8, 15), | 
|---|
| 356 | [NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST] = { .type = NLA_U8 }, | 
|---|
| 357 | [NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES] = { .type = NLA_U8 }, | 
|---|
| 358 | [NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI] = { .type = NLA_FLAG }, | 
|---|
| 359 | [NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC] = { .type = NLA_FLAG }, | 
|---|
| 360 | [NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED] = { .type = NLA_FLAG }, | 
|---|
| 361 | [NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED] = { .type = NLA_FLAG }, | 
|---|
| 362 | [NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK] = { .type = NLA_FLAG }, | 
|---|
| 363 | [NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR] = { .type = NLA_U8 }, | 
|---|
| 364 | }; | 
|---|
| 365 |  | 
|---|
| 366 | static const struct nla_policy | 
|---|
| 367 | nl80211_pmsr_req_data_policy[NL80211_PMSR_TYPE_MAX + 1] = { | 
|---|
| 368 | [NL80211_PMSR_TYPE_FTM] = | 
|---|
| 369 | NLA_POLICY_NESTED(nl80211_pmsr_ftm_req_attr_policy), | 
|---|
| 370 | }; | 
|---|
| 371 |  | 
|---|
| 372 | static const struct nla_policy | 
|---|
| 373 | nl80211_pmsr_req_attr_policy[NL80211_PMSR_REQ_ATTR_MAX + 1] = { | 
|---|
| 374 | [NL80211_PMSR_REQ_ATTR_DATA] = | 
|---|
| 375 | NLA_POLICY_NESTED(nl80211_pmsr_req_data_policy), | 
|---|
| 376 | [NL80211_PMSR_REQ_ATTR_GET_AP_TSF] = { .type = NLA_FLAG }, | 
|---|
| 377 | }; | 
|---|
| 378 |  | 
|---|
| 379 | static const struct nla_policy | 
|---|
| 380 | nl80211_pmsr_peer_attr_policy[NL80211_PMSR_PEER_ATTR_MAX + 1] = { | 
|---|
| 381 | [NL80211_PMSR_PEER_ATTR_ADDR] = NLA_POLICY_ETH_ADDR, | 
|---|
| 382 | [NL80211_PMSR_PEER_ATTR_CHAN] = NLA_POLICY_NESTED(nl80211_policy), | 
|---|
| 383 | [NL80211_PMSR_PEER_ATTR_REQ] = | 
|---|
| 384 | NLA_POLICY_NESTED(nl80211_pmsr_req_attr_policy), | 
|---|
| 385 | [NL80211_PMSR_PEER_ATTR_RESP] = { .type = NLA_REJECT }, | 
|---|
| 386 | }; | 
|---|
| 387 |  | 
|---|
| 388 | static const struct nla_policy | 
|---|
| 389 | nl80211_pmsr_attr_policy[NL80211_PMSR_ATTR_MAX + 1] = { | 
|---|
| 390 | [NL80211_PMSR_ATTR_MAX_PEERS] = { .type = NLA_REJECT }, | 
|---|
| 391 | [NL80211_PMSR_ATTR_REPORT_AP_TSF] = { .type = NLA_REJECT }, | 
|---|
| 392 | [NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR] = { .type = NLA_REJECT }, | 
|---|
| 393 | [NL80211_PMSR_ATTR_TYPE_CAPA] = { .type = NLA_REJECT }, | 
|---|
| 394 | [NL80211_PMSR_ATTR_PEERS] = | 
|---|
| 395 | NLA_POLICY_NESTED_ARRAY(nl80211_pmsr_peer_attr_policy), | 
|---|
| 396 | }; | 
|---|
| 397 |  | 
|---|
| 398 | static const struct nla_policy | 
|---|
| 399 | he_obss_pd_policy[NL80211_HE_OBSS_PD_ATTR_MAX + 1] = { | 
|---|
| 400 | [NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET] = | 
|---|
| 401 | NLA_POLICY_RANGE(NLA_U8, 1, 20), | 
|---|
| 402 | [NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET] = | 
|---|
| 403 | NLA_POLICY_RANGE(NLA_U8, 1, 20), | 
|---|
| 404 | [NL80211_HE_OBSS_PD_ATTR_NON_SRG_MAX_OFFSET] = | 
|---|
| 405 | NLA_POLICY_RANGE(NLA_U8, 1, 20), | 
|---|
| 406 | [NL80211_HE_OBSS_PD_ATTR_BSS_COLOR_BITMAP] = | 
|---|
| 407 | NLA_POLICY_EXACT_LEN(8), | 
|---|
| 408 | [NL80211_HE_OBSS_PD_ATTR_PARTIAL_BSSID_BITMAP] = | 
|---|
| 409 | NLA_POLICY_EXACT_LEN(8), | 
|---|
| 410 | [NL80211_HE_OBSS_PD_ATTR_SR_CTRL] = { .type = NLA_U8 }, | 
|---|
| 411 | }; | 
|---|
| 412 |  | 
|---|
| 413 | static const struct nla_policy | 
|---|
| 414 | he_bss_color_policy[NL80211_HE_BSS_COLOR_ATTR_MAX + 1] = { | 
|---|
| 415 | [NL80211_HE_BSS_COLOR_ATTR_COLOR] = NLA_POLICY_RANGE(NLA_U8, 1, 63), | 
|---|
| 416 | [NL80211_HE_BSS_COLOR_ATTR_DISABLED] = { .type = NLA_FLAG }, | 
|---|
| 417 | [NL80211_HE_BSS_COLOR_ATTR_PARTIAL] = { .type = NLA_FLAG }, | 
|---|
| 418 | }; | 
|---|
| 419 |  | 
|---|
| 420 | static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { | 
|---|
| 421 | [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY, | 
|---|
| 422 | .len = NL80211_MAX_SUPP_RATES }, | 
|---|
| 423 | [NL80211_TXRATE_HT] = { .type = NLA_BINARY, | 
|---|
| 424 | .len = NL80211_MAX_SUPP_HT_RATES }, | 
|---|
| 425 | [NL80211_TXRATE_VHT] = NLA_POLICY_EXACT_LEN_WARN(sizeof(struct nl80211_txrate_vht)), | 
|---|
| 426 | [NL80211_TXRATE_GI] = { .type = NLA_U8 }, | 
|---|
| 427 | [NL80211_TXRATE_HE] = NLA_POLICY_EXACT_LEN(sizeof(struct nl80211_txrate_he)), | 
|---|
| 428 | [NL80211_TXRATE_HE_GI] =  NLA_POLICY_RANGE(NLA_U8, | 
|---|
| 429 | NL80211_RATE_INFO_HE_GI_0_8, | 
|---|
| 430 | NL80211_RATE_INFO_HE_GI_3_2), | 
|---|
| 431 | [NL80211_TXRATE_HE_LTF] = NLA_POLICY_RANGE(NLA_U8, | 
|---|
| 432 | NL80211_RATE_INFO_HE_1XLTF, | 
|---|
| 433 | NL80211_RATE_INFO_HE_4XLTF), | 
|---|
| 434 | [NL80211_TXRATE_EHT] = NLA_POLICY_EXACT_LEN(sizeof(struct nl80211_txrate_eht)), | 
|---|
| 435 | [NL80211_TXRATE_EHT_GI] =  NLA_POLICY_RANGE(NLA_U8, | 
|---|
| 436 | NL80211_RATE_INFO_EHT_GI_0_8, | 
|---|
| 437 | NL80211_RATE_INFO_EHT_GI_3_2), | 
|---|
| 438 | [NL80211_TXRATE_EHT_LTF] = NLA_POLICY_RANGE(NLA_U8, | 
|---|
| 439 | NL80211_RATE_INFO_EHT_1XLTF, | 
|---|
| 440 | NL80211_RATE_INFO_EHT_8XLTF), | 
|---|
| 441 |  | 
|---|
| 442 | }; | 
|---|
| 443 |  | 
|---|
| 444 | static const struct nla_policy | 
|---|
| 445 | nl80211_tid_config_attr_policy[NL80211_TID_CONFIG_ATTR_MAX + 1] = { | 
|---|
| 446 | [NL80211_TID_CONFIG_ATTR_VIF_SUPP] = { .type = NLA_U64 }, | 
|---|
| 447 | [NL80211_TID_CONFIG_ATTR_PEER_SUPP] = { .type = NLA_U64 }, | 
|---|
| 448 | [NL80211_TID_CONFIG_ATTR_OVERRIDE] = { .type = NLA_FLAG }, | 
|---|
| 449 | [NL80211_TID_CONFIG_ATTR_TIDS] = NLA_POLICY_RANGE(NLA_U16, 1, 0xff), | 
|---|
| 450 | [NL80211_TID_CONFIG_ATTR_NOACK] = | 
|---|
| 451 | NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE), | 
|---|
| 452 | [NL80211_TID_CONFIG_ATTR_RETRY_SHORT] = NLA_POLICY_MIN(NLA_U8, 1), | 
|---|
| 453 | [NL80211_TID_CONFIG_ATTR_RETRY_LONG] = NLA_POLICY_MIN(NLA_U8, 1), | 
|---|
| 454 | [NL80211_TID_CONFIG_ATTR_AMPDU_CTRL] = | 
|---|
| 455 | NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE), | 
|---|
| 456 | [NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL] = | 
|---|
| 457 | NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE), | 
|---|
| 458 | [NL80211_TID_CONFIG_ATTR_AMSDU_CTRL] = | 
|---|
| 459 | NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE), | 
|---|
| 460 | [NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE] = | 
|---|
| 461 | NLA_POLICY_MAX(NLA_U8, NL80211_TX_RATE_FIXED), | 
|---|
| 462 | [NL80211_TID_CONFIG_ATTR_TX_RATE] = | 
|---|
| 463 | NLA_POLICY_NESTED(nl80211_txattr_policy), | 
|---|
| 464 | }; | 
|---|
| 465 |  | 
|---|
| 466 | static const struct nla_policy | 
|---|
| 467 | nl80211_fils_discovery_policy[NL80211_FILS_DISCOVERY_ATTR_MAX + 1] = { | 
|---|
| 468 | [NL80211_FILS_DISCOVERY_ATTR_INT_MIN] = NLA_POLICY_MAX(NLA_U32, 10000), | 
|---|
| 469 | [NL80211_FILS_DISCOVERY_ATTR_INT_MAX] = NLA_POLICY_MAX(NLA_U32, 10000), | 
|---|
| 470 | [NL80211_FILS_DISCOVERY_ATTR_TMPL] = | 
|---|
| 471 | NLA_POLICY_RANGE(NLA_BINARY, | 
|---|
| 472 | NL80211_FILS_DISCOVERY_TMPL_MIN_LEN, | 
|---|
| 473 | IEEE80211_MAX_DATA_LEN), | 
|---|
| 474 | }; | 
|---|
| 475 |  | 
|---|
| 476 | static const struct nla_policy | 
|---|
| 477 | nl80211_unsol_bcast_probe_resp_policy[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_MAX + 1] = { | 
|---|
| 478 | [NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT] = NLA_POLICY_MAX(NLA_U32, 20), | 
|---|
| 479 | [NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL] = { .type = NLA_BINARY, | 
|---|
| 480 | .len = IEEE80211_MAX_DATA_LEN } | 
|---|
| 481 | }; | 
|---|
| 482 |  | 
|---|
| 483 | static const struct nla_policy | 
|---|
| 484 | sar_specs_policy[NL80211_SAR_ATTR_SPECS_MAX + 1] = { | 
|---|
| 485 | [NL80211_SAR_ATTR_SPECS_POWER] = { .type = NLA_S32 }, | 
|---|
| 486 | [NL80211_SAR_ATTR_SPECS_RANGE_INDEX] = {.type = NLA_U32 }, | 
|---|
| 487 | }; | 
|---|
| 488 |  | 
|---|
| 489 | static const struct nla_policy | 
|---|
| 490 | sar_policy[NL80211_SAR_ATTR_MAX + 1] = { | 
|---|
| 491 | [NL80211_SAR_ATTR_TYPE] = NLA_POLICY_MAX(NLA_U32, NUM_NL80211_SAR_TYPE), | 
|---|
| 492 | [NL80211_SAR_ATTR_SPECS] = NLA_POLICY_NESTED_ARRAY(sar_specs_policy), | 
|---|
| 493 | }; | 
|---|
| 494 |  | 
|---|
| 495 | static const struct nla_policy | 
|---|
| 496 | nl80211_mbssid_config_policy[NL80211_MBSSID_CONFIG_ATTR_MAX + 1] = { | 
|---|
| 497 | [NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES] = NLA_POLICY_MIN(NLA_U8, 2), | 
|---|
| 498 | [NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY] = | 
|---|
| 499 | NLA_POLICY_MIN(NLA_U8, 1), | 
|---|
| 500 | [NL80211_MBSSID_CONFIG_ATTR_INDEX] = { .type = NLA_U8 }, | 
|---|
| 501 | [NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX] = { .type = NLA_U32 }, | 
|---|
| 502 | [NL80211_MBSSID_CONFIG_ATTR_EMA] = { .type = NLA_FLAG }, | 
|---|
| 503 | [NL80211_MBSSID_CONFIG_ATTR_TX_LINK_ID] = | 
|---|
| 504 | NLA_POLICY_MAX(NLA_U8, IEEE80211_MLD_MAX_NUM_LINKS), | 
|---|
| 505 | }; | 
|---|
| 506 |  | 
|---|
| 507 | static const struct nla_policy | 
|---|
| 508 | nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] = { | 
|---|
| 509 | [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 }, | 
|---|
| 510 | [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 }, | 
|---|
| 511 | }; | 
|---|
| 512 |  | 
|---|
| 513 | static const struct nla_policy | 
|---|
| 514 | nl80211_s1g_short_beacon[NL80211_S1G_SHORT_BEACON_ATTR_MAX + 1] = { | 
|---|
| 515 | [NL80211_S1G_SHORT_BEACON_ATTR_HEAD] = | 
|---|
| 516 | NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_beacon_head, | 
|---|
| 517 | IEEE80211_MAX_DATA_LEN), | 
|---|
| 518 | [NL80211_S1G_SHORT_BEACON_ATTR_TAIL] = | 
|---|
| 519 | NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_ie_attr, | 
|---|
| 520 | IEEE80211_MAX_DATA_LEN), | 
|---|
| 521 | }; | 
|---|
| 522 |  | 
|---|
| 523 | static const struct nla_policy | 
|---|
| 524 | nl80211_nan_band_conf_policy[NL80211_NAN_BAND_CONF_ATTR_MAX + 1] = { | 
|---|
| 525 | [NL80211_NAN_BAND_CONF_BAND] = NLA_POLICY_MAX(NLA_U8, | 
|---|
| 526 | NUM_NL80211_BANDS - 1), | 
|---|
| 527 | [NL80211_NAN_BAND_CONF_FREQ] = { .type = NLA_U16 }, | 
|---|
| 528 | [NL80211_NAN_BAND_CONF_RSSI_CLOSE] = NLA_POLICY_MIN(NLA_S8, -59), | 
|---|
| 529 | [NL80211_NAN_BAND_CONF_RSSI_MIDDLE] = NLA_POLICY_MIN(NLA_S8, -74), | 
|---|
| 530 | [NL80211_NAN_BAND_CONF_WAKE_DW] = NLA_POLICY_MAX(NLA_U8, 5), | 
|---|
| 531 | [NL80211_NAN_BAND_CONF_DISABLE_SCAN] = { .type = NLA_FLAG }, | 
|---|
| 532 | }; | 
|---|
| 533 |  | 
|---|
| 534 | static const struct nla_policy | 
|---|
| 535 | nl80211_nan_conf_policy[NL80211_NAN_CONF_ATTR_MAX + 1] = { | 
|---|
| 536 | [NL80211_NAN_CONF_CLUSTER_ID] = | 
|---|
| 537 | NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_nan_cluster_id, | 
|---|
| 538 | ETH_ALEN), | 
|---|
| 539 | [NL80211_NAN_CONF_EXTRA_ATTRS] = { .type = NLA_BINARY, | 
|---|
| 540 | .len = IEEE80211_MAX_DATA_LEN}, | 
|---|
| 541 | [NL80211_NAN_CONF_VENDOR_ELEMS] = | 
|---|
| 542 | NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_ie_attr, | 
|---|
| 543 | IEEE80211_MAX_DATA_LEN), | 
|---|
| 544 | [NL80211_NAN_CONF_BAND_CONFIGS] = | 
|---|
| 545 | NLA_POLICY_NESTED_ARRAY(nl80211_nan_band_conf_policy), | 
|---|
| 546 | [NL80211_NAN_CONF_SCAN_PERIOD] = { .type = NLA_U16 }, | 
|---|
| 547 | [NL80211_NAN_CONF_SCAN_DWELL_TIME] = NLA_POLICY_RANGE(NLA_U16, 50, 512), | 
|---|
| 548 | [NL80211_NAN_CONF_DISCOVERY_BEACON_INTERVAL] = | 
|---|
| 549 | NLA_POLICY_RANGE(NLA_U8, 50, 200), | 
|---|
| 550 | [NL80211_NAN_CONF_NOTIFY_DW] = { .type = NLA_FLAG }, | 
|---|
| 551 | }; | 
|---|
| 552 |  | 
|---|
| 553 | static const struct netlink_range_validation nl80211_punct_bitmap_range = { | 
|---|
| 554 | .min = 0, | 
|---|
| 555 | .max = 0xffff, | 
|---|
| 556 | }; | 
|---|
| 557 |  | 
|---|
| 558 | static const struct netlink_range_validation q_range = { | 
|---|
| 559 | .max = INT_MAX, | 
|---|
| 560 | }; | 
|---|
| 561 |  | 
|---|
| 562 | static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { | 
|---|
| 563 | [0] = { .strict_start_type = NL80211_ATTR_HE_OBSS_PD }, | 
|---|
| 564 | [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, | 
|---|
| 565 | [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, | 
|---|
| 566 | .len = 20-1 }, | 
|---|
| 567 | [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED }, | 
|---|
| 568 |  | 
|---|
| 569 | [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 }, | 
|---|
| 570 | [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 }, | 
|---|
| 571 | [NL80211_ATTR_WIPHY_EDMG_CHANNELS] = NLA_POLICY_RANGE(NLA_U8, | 
|---|
| 572 | NL80211_EDMG_CHANNELS_MIN, | 
|---|
| 573 | NL80211_EDMG_CHANNELS_MAX), | 
|---|
| 574 | [NL80211_ATTR_WIPHY_EDMG_BW_CONFIG] = NLA_POLICY_RANGE(NLA_U8, | 
|---|
| 575 | NL80211_EDMG_BW_CONFIG_MIN, | 
|---|
| 576 | NL80211_EDMG_BW_CONFIG_MAX), | 
|---|
| 577 |  | 
|---|
| 578 | [NL80211_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 }, | 
|---|
| 579 | [NL80211_ATTR_CENTER_FREQ1] = { .type = NLA_U32 }, | 
|---|
| 580 | [NL80211_ATTR_CENTER_FREQ1_OFFSET] = NLA_POLICY_RANGE(NLA_U32, 0, 999), | 
|---|
| 581 | [NL80211_ATTR_CENTER_FREQ2] = { .type = NLA_U32 }, | 
|---|
| 582 |  | 
|---|
| 583 | [NL80211_ATTR_WIPHY_RETRY_SHORT] = NLA_POLICY_MIN(NLA_U8, 1), | 
|---|
| 584 | [NL80211_ATTR_WIPHY_RETRY_LONG] = NLA_POLICY_MIN(NLA_U8, 1), | 
|---|
| 585 | [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, | 
|---|
| 586 | [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 }, | 
|---|
| 587 | [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 }, | 
|---|
| 588 | [NL80211_ATTR_WIPHY_DYN_ACK] = { .type = NLA_FLAG }, | 
|---|
| 589 |  | 
|---|
| 590 | [NL80211_ATTR_IFTYPE] = NLA_POLICY_MAX(NLA_U32, NL80211_IFTYPE_MAX), | 
|---|
| 591 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, | 
|---|
| 592 | [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 }, | 
|---|
| 593 |  | 
|---|
| 594 | [NL80211_ATTR_MAC] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), | 
|---|
| 595 | [NL80211_ATTR_PREV_BSSID] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), | 
|---|
| 596 |  | 
|---|
| 597 | [NL80211_ATTR_KEY] = { .type = NLA_NESTED, }, | 
|---|
| 598 | [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY, | 
|---|
| 599 | .len = WLAN_MAX_KEY_LEN }, | 
|---|
| 600 | [NL80211_ATTR_KEY_IDX] = NLA_POLICY_MAX(NLA_U8, 7), | 
|---|
| 601 | [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 }, | 
|---|
| 602 | [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG }, | 
|---|
| 603 | [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 }, | 
|---|
| 604 | [NL80211_ATTR_KEY_TYPE] = | 
|---|
| 605 | NLA_POLICY_MAX(NLA_U32, NUM_NL80211_KEYTYPES), | 
|---|
| 606 |  | 
|---|
| 607 | [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 }, | 
|---|
| 608 | [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 }, | 
|---|
| 609 | [NL80211_ATTR_BEACON_HEAD] = | 
|---|
| 610 | NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_beacon_head, | 
|---|
| 611 | IEEE80211_MAX_DATA_LEN), | 
|---|
| 612 | [NL80211_ATTR_BEACON_TAIL] = | 
|---|
| 613 | NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_ie_attr, | 
|---|
| 614 | IEEE80211_MAX_DATA_LEN), | 
|---|
| 615 | [NL80211_ATTR_STA_AID] = | 
|---|
| 616 | NLA_POLICY_RANGE(NLA_U16, 1, IEEE80211_MAX_AID), | 
|---|
| 617 | [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED }, | 
|---|
| 618 | [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 }, | 
|---|
| 619 | [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY, | 
|---|
| 620 | .len = NL80211_MAX_SUPP_RATES }, | 
|---|
| 621 | [NL80211_ATTR_STA_PLINK_ACTION] = | 
|---|
| 622 | NLA_POLICY_MAX(NLA_U8, NUM_NL80211_PLINK_ACTIONS - 1), | 
|---|
| 623 | [NL80211_ATTR_STA_TX_POWER_SETTING] = | 
|---|
| 624 | NLA_POLICY_RANGE(NLA_U8, | 
|---|
| 625 | NL80211_TX_POWER_AUTOMATIC, | 
|---|
| 626 | NL80211_TX_POWER_FIXED), | 
|---|
| 627 | [NL80211_ATTR_STA_TX_POWER] = { .type = NLA_S16 }, | 
|---|
| 628 | [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 }, | 
|---|
| 629 | [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ }, | 
|---|
| 630 | [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY, | 
|---|
| 631 | .len = IEEE80211_MAX_MESH_ID_LEN }, | 
|---|
| 632 | [NL80211_ATTR_MPATH_NEXT_HOP] = NLA_POLICY_ETH_ADDR_COMPAT, | 
|---|
| 633 |  | 
|---|
| 634 | /* allow 3 for NUL-termination, we used to declare this NLA_STRING */ | 
|---|
| 635 | [NL80211_ATTR_REG_ALPHA2] = NLA_POLICY_RANGE(NLA_BINARY, 2, 3), | 
|---|
| 636 | [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED }, | 
|---|
| 637 |  | 
|---|
| 638 | [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, | 
|---|
| 639 | [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, | 
|---|
| 640 | [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, | 
|---|
| 641 | [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY, | 
|---|
| 642 | .len = NL80211_MAX_SUPP_RATES }, | 
|---|
| 643 | [NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 }, | 
|---|
| 644 |  | 
|---|
| 645 | [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED }, | 
|---|
| 646 | [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG }, | 
|---|
| 647 |  | 
|---|
| 648 | [NL80211_ATTR_HT_CAPABILITY] = NLA_POLICY_EXACT_LEN_WARN(NL80211_HT_CAPABILITY_LEN), | 
|---|
| 649 |  | 
|---|
| 650 | [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 }, | 
|---|
| 651 | [NL80211_ATTR_IE] = NLA_POLICY_VALIDATE_FN(NLA_BINARY, | 
|---|
| 652 | validate_ie_attr, | 
|---|
| 653 | IEEE80211_MAX_DATA_LEN), | 
|---|
| 654 | [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED }, | 
|---|
| 655 | [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED }, | 
|---|
| 656 |  | 
|---|
| 657 | [NL80211_ATTR_SSID] = { .type = NLA_BINARY, | 
|---|
| 658 | .len = IEEE80211_MAX_SSID_LEN }, | 
|---|
| 659 | [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 }, | 
|---|
| 660 | [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 }, | 
|---|
| 661 | [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG }, | 
|---|
| 662 | [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG }, | 
|---|
| 663 | [NL80211_ATTR_USE_MFP] = NLA_POLICY_RANGE(NLA_U32, | 
|---|
| 664 | NL80211_MFP_NO, | 
|---|
| 665 | NL80211_MFP_OPTIONAL), | 
|---|
| 666 | [NL80211_ATTR_STA_FLAGS2] = | 
|---|
| 667 | NLA_POLICY_EXACT_LEN_WARN(sizeof(struct nl80211_sta_flag_update)), | 
|---|
| 668 | [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG }, | 
|---|
| 669 | [NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 }, | 
|---|
| 670 | [NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG }, | 
|---|
| 671 | [NL80211_ATTR_CONTROL_PORT_OVER_NL80211] = { .type = NLA_FLAG }, | 
|---|
| 672 | [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG }, | 
|---|
| 673 | [NL80211_ATTR_STATUS_CODE] = { .type = NLA_U16 }, | 
|---|
| 674 | [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, | 
|---|
| 675 | [NL80211_ATTR_WPA_VERSIONS] = | 
|---|
| 676 | NLA_POLICY_RANGE(NLA_U32, 0, | 
|---|
| 677 | NL80211_WPA_VERSION_1 | | 
|---|
| 678 | NL80211_WPA_VERSION_2 | | 
|---|
| 679 | NL80211_WPA_VERSION_3), | 
|---|
| 680 | [NL80211_ATTR_PID] = { .type = NLA_U32 }, | 
|---|
| 681 | [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, | 
|---|
| 682 | [NL80211_ATTR_PMKID] = NLA_POLICY_EXACT_LEN_WARN(WLAN_PMKID_LEN), | 
|---|
| 683 | [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, | 
|---|
| 684 | [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, | 
|---|
| 685 | [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED }, | 
|---|
| 686 | [NL80211_ATTR_FRAME] = { .type = NLA_BINARY, | 
|---|
| 687 | .len = IEEE80211_MAX_DATA_LEN }, | 
|---|
| 688 | [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, | 
|---|
| 689 | [NL80211_ATTR_PS_STATE] = NLA_POLICY_RANGE(NLA_U32, | 
|---|
| 690 | NL80211_PS_DISABLED, | 
|---|
| 691 | NL80211_PS_ENABLED), | 
|---|
| 692 | [NL80211_ATTR_CQM] = { .type = NLA_NESTED, }, | 
|---|
| 693 | [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG }, | 
|---|
| 694 | [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 }, | 
|---|
| 695 | [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 }, | 
|---|
| 696 | [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 }, | 
|---|
| 697 | [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 }, | 
|---|
| 698 | [NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 }, | 
|---|
| 699 | [NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 }, | 
|---|
| 700 | [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 }, | 
|---|
| 701 | [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG }, | 
|---|
| 702 | [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, | 
|---|
| 703 | [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED }, | 
|---|
| 704 | [NL80211_ATTR_STA_PLINK_STATE] = | 
|---|
| 705 | NLA_POLICY_MAX(NLA_U8, NUM_NL80211_PLINK_STATES - 1), | 
|---|
| 706 | [NL80211_ATTR_MEASUREMENT_DURATION] = { .type = NLA_U16 }, | 
|---|
| 707 | [NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY] = { .type = NLA_FLAG }, | 
|---|
| 708 | [NL80211_ATTR_MESH_PEER_AID] = | 
|---|
| 709 | NLA_POLICY_RANGE(NLA_U16, 1, IEEE80211_MAX_AID), | 
|---|
| 710 | [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 }, | 
|---|
| 711 | [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED }, | 
|---|
| 712 | [NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED }, | 
|---|
| 713 | [NL80211_ATTR_HIDDEN_SSID] = | 
|---|
| 714 | NLA_POLICY_RANGE(NLA_U32, | 
|---|
| 715 | NL80211_HIDDEN_SSID_NOT_IN_USE, | 
|---|
| 716 | NL80211_HIDDEN_SSID_ZERO_CONTENTS), | 
|---|
| 717 | [NL80211_ATTR_IE_PROBE_RESP] = | 
|---|
| 718 | NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_ie_attr, | 
|---|
| 719 | IEEE80211_MAX_DATA_LEN), | 
|---|
| 720 | [NL80211_ATTR_IE_ASSOC_RESP] = | 
|---|
| 721 | NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_ie_attr, | 
|---|
| 722 | IEEE80211_MAX_DATA_LEN), | 
|---|
| 723 | [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG }, | 
|---|
| 724 | [NL80211_ATTR_STA_WME] = NLA_POLICY_NESTED(nl80211_sta_wme_policy), | 
|---|
| 725 | [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED }, | 
|---|
| 726 | [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG }, | 
|---|
| 727 | [NL80211_ATTR_TDLS_ACTION] = { .type = NLA_U8 }, | 
|---|
| 728 | [NL80211_ATTR_TDLS_DIALOG_TOKEN] = { .type = NLA_U8 }, | 
|---|
| 729 | [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 }, | 
|---|
| 730 | [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG }, | 
|---|
| 731 | [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG }, | 
|---|
| 732 | [NL80211_ATTR_TDLS_INITIATOR] = { .type = NLA_FLAG }, | 
|---|
| 733 | [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG }, | 
|---|
| 734 | [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY, | 
|---|
| 735 | .len = IEEE80211_MAX_DATA_LEN }, | 
|---|
| 736 | [NL80211_ATTR_DFS_REGION] = { .type = NLA_U8 }, | 
|---|
| 737 | [NL80211_ATTR_DISABLE_HT] = { .type = NLA_FLAG }, | 
|---|
| 738 | [NL80211_ATTR_HT_CAPABILITY_MASK] = { | 
|---|
| 739 | .len = NL80211_HT_CAPABILITY_LEN | 
|---|
| 740 | }, | 
|---|
| 741 | [NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 }, | 
|---|
| 742 | [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 }, | 
|---|
| 743 | [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 }, | 
|---|
| 744 | [NL80211_ATTR_WDEV] = { .type = NLA_U64 }, | 
|---|
| 745 | [NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 }, | 
|---|
| 746 |  | 
|---|
| 747 | /* need to include at least Auth Transaction and Status Code */ | 
|---|
| 748 | [NL80211_ATTR_AUTH_DATA] = NLA_POLICY_MIN_LEN(4), | 
|---|
| 749 |  | 
|---|
| 750 | [NL80211_ATTR_VHT_CAPABILITY] = NLA_POLICY_EXACT_LEN_WARN(NL80211_VHT_CAPABILITY_LEN), | 
|---|
| 751 | [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 }, | 
|---|
| 752 | [NL80211_ATTR_P2P_CTWINDOW] = NLA_POLICY_MAX(NLA_U8, 127), | 
|---|
| 753 | [NL80211_ATTR_P2P_OPPPS] = NLA_POLICY_MAX(NLA_U8, 1), | 
|---|
| 754 | [NL80211_ATTR_LOCAL_MESH_POWER_MODE] = | 
|---|
| 755 | NLA_POLICY_RANGE(NLA_U32, | 
|---|
| 756 | NL80211_MESH_POWER_UNKNOWN + 1, | 
|---|
| 757 | NL80211_MESH_POWER_MAX), | 
|---|
| 758 | [NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 }, | 
|---|
| 759 | [NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED }, | 
|---|
| 760 | [NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 }, | 
|---|
| 761 | [NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, }, | 
|---|
| 762 | [NL80211_ATTR_SPLIT_WIPHY_DUMP] = { .type = NLA_FLAG, }, | 
|---|
| 763 | [NL80211_ATTR_DISABLE_VHT] = { .type = NLA_FLAG }, | 
|---|
| 764 | [NL80211_ATTR_VHT_CAPABILITY_MASK] = { | 
|---|
| 765 | .len = NL80211_VHT_CAPABILITY_LEN, | 
|---|
| 766 | }, | 
|---|
| 767 | [NL80211_ATTR_MDID] = { .type = NLA_U16 }, | 
|---|
| 768 | [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY, | 
|---|
| 769 | .len = IEEE80211_MAX_DATA_LEN }, | 
|---|
| 770 | [NL80211_ATTR_CRIT_PROT_ID] = { .type = NLA_U16 }, | 
|---|
| 771 | [NL80211_ATTR_MAX_CRIT_PROT_DURATION] = | 
|---|
| 772 | NLA_POLICY_MAX(NLA_U16, NL80211_CRIT_PROTO_MAX_DURATION), | 
|---|
| 773 | [NL80211_ATTR_PEER_AID] = | 
|---|
| 774 | NLA_POLICY_RANGE(NLA_U16, 1, IEEE80211_MAX_AID), | 
|---|
| 775 | [NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 }, | 
|---|
| 776 | [NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG }, | 
|---|
| 777 | [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED }, | 
|---|
| 778 | [NL80211_ATTR_CNTDWN_OFFS_BEACON] = { .type = NLA_BINARY }, | 
|---|
| 779 | [NL80211_ATTR_CNTDWN_OFFS_PRESP] = { .type = NLA_BINARY }, | 
|---|
| 780 | [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = NLA_POLICY_MIN_LEN(2), | 
|---|
| 781 | /* | 
|---|
| 782 | * The value of the Length field of the Supported Operating | 
|---|
| 783 | * Classes element is between 2 and 253. | 
|---|
| 784 | */ | 
|---|
| 785 | [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = | 
|---|
| 786 | NLA_POLICY_RANGE(NLA_BINARY, 2, 253), | 
|---|
| 787 | [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG }, | 
|---|
| 788 | [NL80211_ATTR_OPMODE_NOTIF] = { .type = NLA_U8 }, | 
|---|
| 789 | [NL80211_ATTR_VENDOR_ID] = { .type = NLA_U32 }, | 
|---|
| 790 | [NL80211_ATTR_VENDOR_SUBCMD] = { .type = NLA_U32 }, | 
|---|
| 791 | [NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY }, | 
|---|
| 792 | [NL80211_ATTR_QOS_MAP] = NLA_POLICY_RANGE(NLA_BINARY, | 
|---|
| 793 | IEEE80211_QOS_MAP_LEN_MIN, | 
|---|
| 794 | IEEE80211_QOS_MAP_LEN_MAX), | 
|---|
| 795 | [NL80211_ATTR_MAC_HINT] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), | 
|---|
| 796 | [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 }, | 
|---|
| 797 | [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 }, | 
|---|
| 798 | [NL80211_ATTR_SOCKET_OWNER] = { .type = NLA_FLAG }, | 
|---|
| 799 | [NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY }, | 
|---|
| 800 | [NL80211_ATTR_USE_RRM] = { .type = NLA_FLAG }, | 
|---|
| 801 | [NL80211_ATTR_TSID] = NLA_POLICY_MAX(NLA_U8, IEEE80211_NUM_TIDS - 1), | 
|---|
| 802 | [NL80211_ATTR_USER_PRIO] = | 
|---|
| 803 | NLA_POLICY_MAX(NLA_U8, IEEE80211_NUM_UPS - 1), | 
|---|
| 804 | [NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 }, | 
|---|
| 805 | [NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 }, | 
|---|
| 806 | [NL80211_ATTR_OPER_CLASS] = { .type = NLA_U8 }, | 
|---|
| 807 | [NL80211_ATTR_MAC_MASK] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), | 
|---|
| 808 | [NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG }, | 
|---|
| 809 | [NL80211_ATTR_NETNS_FD] = { .type = NLA_U32 }, | 
|---|
| 810 | [NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 }, | 
|---|
| 811 | [NL80211_ATTR_REG_INDOOR] = { .type = NLA_FLAG }, | 
|---|
| 812 | [NL80211_ATTR_PBSS] = { .type = NLA_FLAG }, | 
|---|
| 813 | [NL80211_ATTR_BSS_SELECT] = { .type = NLA_NESTED }, | 
|---|
| 814 | [NL80211_ATTR_STA_SUPPORT_P2P_PS] = | 
|---|
| 815 | NLA_POLICY_MAX(NLA_U8, NUM_NL80211_P2P_PS_STATUS - 1), | 
|---|
| 816 | [NL80211_ATTR_MU_MIMO_GROUP_DATA] = { | 
|---|
| 817 | .len = VHT_MUMIMO_GROUPS_DATA_LEN | 
|---|
| 818 | }, | 
|---|
| 819 | [NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), | 
|---|
| 820 | [NL80211_ATTR_NAN_MASTER_PREF] = NLA_POLICY_MIN(NLA_U8, 1), | 
|---|
| 821 | [NL80211_ATTR_BANDS] = { .type = NLA_U32 }, | 
|---|
| 822 | [NL80211_ATTR_NAN_CONFIG] = NLA_POLICY_NESTED(nl80211_nan_conf_policy), | 
|---|
| 823 | [NL80211_ATTR_NAN_FUNC] = { .type = NLA_NESTED }, | 
|---|
| 824 | [NL80211_ATTR_FILS_KEK] = { .type = NLA_BINARY, | 
|---|
| 825 | .len = FILS_MAX_KEK_LEN }, | 
|---|
| 826 | [NL80211_ATTR_FILS_NONCES] = NLA_POLICY_EXACT_LEN_WARN(2 * FILS_NONCE_LEN), | 
|---|
| 827 | [NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED] = { .type = NLA_FLAG, }, | 
|---|
| 828 | [NL80211_ATTR_BSSID] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), | 
|---|
| 829 | [NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI] = { .type = NLA_S8 }, | 
|---|
| 830 | [NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST] = { | 
|---|
| 831 | .len = sizeof(struct nl80211_bss_select_rssi_adjust) | 
|---|
| 832 | }, | 
|---|
| 833 | [NL80211_ATTR_TIMEOUT_REASON] = { .type = NLA_U32 }, | 
|---|
| 834 | [NL80211_ATTR_FILS_ERP_USERNAME] = { .type = NLA_BINARY, | 
|---|
| 835 | .len = FILS_ERP_MAX_USERNAME_LEN }, | 
|---|
| 836 | [NL80211_ATTR_FILS_ERP_REALM] = { .type = NLA_BINARY, | 
|---|
| 837 | .len = FILS_ERP_MAX_REALM_LEN }, | 
|---|
| 838 | [NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] = { .type = NLA_U16 }, | 
|---|
| 839 | [NL80211_ATTR_FILS_ERP_RRK] = { .type = NLA_BINARY, | 
|---|
| 840 | .len = FILS_ERP_MAX_RRK_LEN }, | 
|---|
| 841 | [NL80211_ATTR_FILS_CACHE_ID] = NLA_POLICY_EXACT_LEN_WARN(2), | 
|---|
| 842 | [NL80211_ATTR_PMK] = { .type = NLA_BINARY, .len = PMK_MAX_LEN }, | 
|---|
| 843 | [NL80211_ATTR_PMKR0_NAME] = NLA_POLICY_EXACT_LEN(WLAN_PMK_NAME_LEN), | 
|---|
| 844 | [NL80211_ATTR_SCHED_SCAN_MULTI] = { .type = NLA_FLAG }, | 
|---|
| 845 | [NL80211_ATTR_EXTERNAL_AUTH_SUPPORT] = { .type = NLA_FLAG }, | 
|---|
| 846 |  | 
|---|
| 847 | [NL80211_ATTR_TXQ_LIMIT] = { .type = NLA_U32 }, | 
|---|
| 848 | [NL80211_ATTR_TXQ_MEMORY_LIMIT] = { .type = NLA_U32 }, | 
|---|
| 849 | [NL80211_ATTR_TXQ_QUANTUM] = NLA_POLICY_FULL_RANGE(NLA_U32, &q_range), | 
|---|
| 850 | [NL80211_ATTR_HE_CAPABILITY] = | 
|---|
| 851 | NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_he_capa, | 
|---|
| 852 | NL80211_HE_MAX_CAPABILITY_LEN), | 
|---|
| 853 | [NL80211_ATTR_FTM_RESPONDER] = | 
|---|
| 854 | NLA_POLICY_NESTED(nl80211_ftm_responder_policy), | 
|---|
| 855 | [NL80211_ATTR_TIMEOUT] = NLA_POLICY_MIN(NLA_U32, 1), | 
|---|
| 856 | [NL80211_ATTR_PEER_MEASUREMENTS] = | 
|---|
| 857 | NLA_POLICY_NESTED(nl80211_pmsr_attr_policy), | 
|---|
| 858 | [NL80211_ATTR_AIRTIME_WEIGHT] = NLA_POLICY_MIN(NLA_U16, 1), | 
|---|
| 859 | [NL80211_ATTR_SAE_PASSWORD] = { .type = NLA_BINARY, | 
|---|
| 860 | .len = SAE_PASSWORD_MAX_LEN }, | 
|---|
| 861 | [NL80211_ATTR_TWT_RESPONDER] = { .type = NLA_FLAG }, | 
|---|
| 862 | [NL80211_ATTR_HE_OBSS_PD] = NLA_POLICY_NESTED(he_obss_pd_policy), | 
|---|
| 863 | [NL80211_ATTR_VLAN_ID] = NLA_POLICY_RANGE(NLA_U16, 1, VLAN_N_VID - 2), | 
|---|
| 864 | [NL80211_ATTR_HE_BSS_COLOR] = NLA_POLICY_NESTED(he_bss_color_policy), | 
|---|
| 865 | [NL80211_ATTR_TID_CONFIG] = | 
|---|
| 866 | NLA_POLICY_NESTED_ARRAY(nl80211_tid_config_attr_policy), | 
|---|
| 867 | [NL80211_ATTR_CONTROL_PORT_NO_PREAUTH] = { .type = NLA_FLAG }, | 
|---|
| 868 | [NL80211_ATTR_PMK_LIFETIME] = NLA_POLICY_MIN(NLA_U32, 1), | 
|---|
| 869 | [NL80211_ATTR_PMK_REAUTH_THRESHOLD] = NLA_POLICY_RANGE(NLA_U8, 1, 100), | 
|---|
| 870 | [NL80211_ATTR_RECEIVE_MULTICAST] = { .type = NLA_FLAG }, | 
|---|
| 871 | [NL80211_ATTR_WIPHY_FREQ_OFFSET] = NLA_POLICY_RANGE(NLA_U32, 0, 999), | 
|---|
| 872 | [NL80211_ATTR_SCAN_FREQ_KHZ] = { .type = NLA_NESTED }, | 
|---|
| 873 | [NL80211_ATTR_HE_6GHZ_CAPABILITY] = | 
|---|
| 874 | NLA_POLICY_EXACT_LEN(sizeof(struct ieee80211_he_6ghz_capa)), | 
|---|
| 875 | [NL80211_ATTR_FILS_DISCOVERY] = | 
|---|
| 876 | NLA_POLICY_NESTED(nl80211_fils_discovery_policy), | 
|---|
| 877 | [NL80211_ATTR_UNSOL_BCAST_PROBE_RESP] = | 
|---|
| 878 | NLA_POLICY_NESTED(nl80211_unsol_bcast_probe_resp_policy), | 
|---|
| 879 | [NL80211_ATTR_S1G_CAPABILITY] = | 
|---|
| 880 | NLA_POLICY_EXACT_LEN(IEEE80211_S1G_CAPABILITY_LEN), | 
|---|
| 881 | [NL80211_ATTR_S1G_CAPABILITY_MASK] = | 
|---|
| 882 | NLA_POLICY_EXACT_LEN(IEEE80211_S1G_CAPABILITY_LEN), | 
|---|
| 883 | [NL80211_ATTR_SAE_PWE] = | 
|---|
| 884 | NLA_POLICY_RANGE(NLA_U8, NL80211_SAE_PWE_HUNT_AND_PECK, | 
|---|
| 885 | NL80211_SAE_PWE_BOTH), | 
|---|
| 886 | [NL80211_ATTR_RECONNECT_REQUESTED] = { .type = NLA_REJECT }, | 
|---|
| 887 | [NL80211_ATTR_SAR_SPEC] = NLA_POLICY_NESTED(sar_policy), | 
|---|
| 888 | [NL80211_ATTR_DISABLE_HE] = { .type = NLA_FLAG }, | 
|---|
| 889 | [NL80211_ATTR_OBSS_COLOR_BITMAP] = { .type = NLA_U64 }, | 
|---|
| 890 | [NL80211_ATTR_COLOR_CHANGE_COUNT] = { .type = NLA_U8 }, | 
|---|
| 891 | [NL80211_ATTR_COLOR_CHANGE_COLOR] = { .type = NLA_U8 }, | 
|---|
| 892 | [NL80211_ATTR_COLOR_CHANGE_ELEMS] = NLA_POLICY_NESTED(nl80211_policy), | 
|---|
| 893 | [NL80211_ATTR_MBSSID_CONFIG] = | 
|---|
| 894 | NLA_POLICY_NESTED(nl80211_mbssid_config_policy), | 
|---|
| 895 | [NL80211_ATTR_MBSSID_ELEMS] = { .type = NLA_NESTED }, | 
|---|
| 896 | [NL80211_ATTR_RADAR_BACKGROUND] = { .type = NLA_FLAG }, | 
|---|
| 897 | [NL80211_ATTR_AP_SETTINGS_FLAGS] = { .type = NLA_U32 }, | 
|---|
| 898 | [NL80211_ATTR_EHT_CAPABILITY] = | 
|---|
| 899 | NLA_POLICY_RANGE(NLA_BINARY, | 
|---|
| 900 | NL80211_EHT_MIN_CAPABILITY_LEN, | 
|---|
| 901 | NL80211_EHT_MAX_CAPABILITY_LEN), | 
|---|
| 902 | [NL80211_ATTR_DISABLE_EHT] = { .type = NLA_FLAG }, | 
|---|
| 903 | [NL80211_ATTR_MLO_LINKS] = | 
|---|
| 904 | NLA_POLICY_NESTED_ARRAY(nl80211_policy), | 
|---|
| 905 | [NL80211_ATTR_MLO_LINK_ID] = | 
|---|
| 906 | NLA_POLICY_RANGE(NLA_U8, 0, IEEE80211_MLD_MAX_NUM_LINKS - 1), | 
|---|
| 907 | [NL80211_ATTR_MLD_ADDR] = NLA_POLICY_EXACT_LEN(ETH_ALEN), | 
|---|
| 908 | [NL80211_ATTR_MLO_SUPPORT] = { .type = NLA_FLAG }, | 
|---|
| 909 | [NL80211_ATTR_MAX_NUM_AKM_SUITES] = { .type = NLA_REJECT }, | 
|---|
| 910 | [NL80211_ATTR_EML_CAPABILITY] = { .type = NLA_U16 }, | 
|---|
| 911 | [NL80211_ATTR_PUNCT_BITMAP] = | 
|---|
| 912 | NLA_POLICY_FULL_RANGE(NLA_U32, &nl80211_punct_bitmap_range), | 
|---|
| 913 |  | 
|---|
| 914 | [NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS] = { .type = NLA_U16 }, | 
|---|
| 915 | [NL80211_ATTR_HW_TIMESTAMP_ENABLED] = { .type = NLA_FLAG }, | 
|---|
| 916 | [NL80211_ATTR_EMA_RNR_ELEMS] = { .type = NLA_NESTED }, | 
|---|
| 917 | [NL80211_ATTR_MLO_LINK_DISABLED] = { .type = NLA_FLAG }, | 
|---|
| 918 | [NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA] = { .type = NLA_FLAG }, | 
|---|
| 919 | [NL80211_ATTR_MLO_TTLM_DLINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8), | 
|---|
| 920 | [NL80211_ATTR_MLO_TTLM_ULINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8), | 
|---|
| 921 | [NL80211_ATTR_ASSOC_SPP_AMSDU] = { .type = NLA_FLAG }, | 
|---|
| 922 | [NL80211_ATTR_VIF_RADIO_MASK] = { .type = NLA_U32 }, | 
|---|
| 923 | [NL80211_ATTR_SUPPORTED_SELECTORS] = | 
|---|
| 924 | NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_supported_selectors, | 
|---|
| 925 | NL80211_MAX_SUPP_SELECTORS), | 
|---|
| 926 | [NL80211_ATTR_MLO_RECONF_REM_LINKS] = { .type = NLA_U16 }, | 
|---|
| 927 | [NL80211_ATTR_EPCS] = { .type = NLA_FLAG }, | 
|---|
| 928 | [NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS] = { .type = NLA_U16 }, | 
|---|
| 929 | [NL80211_ATTR_WIPHY_RADIO_INDEX] = { .type = NLA_U8 }, | 
|---|
| 930 | [NL80211_ATTR_S1G_LONG_BEACON_PERIOD] = NLA_POLICY_MIN(NLA_U8, 2), | 
|---|
| 931 | [NL80211_ATTR_S1G_SHORT_BEACON] = | 
|---|
| 932 | NLA_POLICY_NESTED(nl80211_s1g_short_beacon), | 
|---|
| 933 | [NL80211_ATTR_BSS_PARAM] = { .type = NLA_FLAG }, | 
|---|
| 934 | [NL80211_ATTR_S1G_PRIMARY_2MHZ] = { .type = NLA_FLAG }, | 
|---|
| 935 | }; | 
|---|
| 936 |  | 
|---|
| 937 | /* policy for the key attributes */ | 
|---|
| 938 | static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = { | 
|---|
| 939 | [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, | 
|---|
| 940 | [NL80211_KEY_IDX] = { .type = NLA_U8 }, | 
|---|
| 941 | [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, | 
|---|
| 942 | [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 }, | 
|---|
| 943 | [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG }, | 
|---|
| 944 | [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG }, | 
|---|
| 945 | [NL80211_KEY_TYPE] = NLA_POLICY_MAX(NLA_U32, NUM_NL80211_KEYTYPES - 1), | 
|---|
| 946 | [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, | 
|---|
| 947 | [NL80211_KEY_MODE] = NLA_POLICY_RANGE(NLA_U8, 0, NL80211_KEY_SET_TX), | 
|---|
| 948 | }; | 
|---|
| 949 |  | 
|---|
| 950 | /* policy for the key default flags */ | 
|---|
| 951 | static const struct nla_policy | 
|---|
| 952 | nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = { | 
|---|
| 953 | [NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG }, | 
|---|
| 954 | [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG }, | 
|---|
| 955 | }; | 
|---|
| 956 |  | 
|---|
| 957 | #ifdef CONFIG_PM | 
|---|
| 958 | /* policy for WoWLAN attributes */ | 
|---|
| 959 | static const struct nla_policy | 
|---|
| 960 | nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = { | 
|---|
| 961 | [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG }, | 
|---|
| 962 | [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG }, | 
|---|
| 963 | [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG }, | 
|---|
| 964 | [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED }, | 
|---|
| 965 | [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG }, | 
|---|
| 966 | [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG }, | 
|---|
| 967 | [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG }, | 
|---|
| 968 | [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG }, | 
|---|
| 969 | [NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED }, | 
|---|
| 970 | [NL80211_WOWLAN_TRIG_NET_DETECT] = { .type = NLA_NESTED }, | 
|---|
| 971 | }; | 
|---|
| 972 |  | 
|---|
| 973 | static const struct nla_policy | 
|---|
| 974 | nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = { | 
|---|
| 975 | [NL80211_WOWLAN_TCP_SRC_IPV4] = { .type = NLA_U32 }, | 
|---|
| 976 | [NL80211_WOWLAN_TCP_DST_IPV4] = { .type = NLA_U32 }, | 
|---|
| 977 | [NL80211_WOWLAN_TCP_DST_MAC] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), | 
|---|
| 978 | [NL80211_WOWLAN_TCP_SRC_PORT] = { .type = NLA_U16 }, | 
|---|
| 979 | [NL80211_WOWLAN_TCP_DST_PORT] = { .type = NLA_U16 }, | 
|---|
| 980 | [NL80211_WOWLAN_TCP_DATA_PAYLOAD] = NLA_POLICY_MIN_LEN(1), | 
|---|
| 981 | [NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ] = { | 
|---|
| 982 | .len = sizeof(struct nl80211_wowlan_tcp_data_seq) | 
|---|
| 983 | }, | 
|---|
| 984 | [NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN] = { | 
|---|
| 985 | .len = sizeof(struct nl80211_wowlan_tcp_data_token) | 
|---|
| 986 | }, | 
|---|
| 987 | [NL80211_WOWLAN_TCP_DATA_INTERVAL] = { .type = NLA_U32 }, | 
|---|
| 988 | [NL80211_WOWLAN_TCP_WAKE_PAYLOAD] = NLA_POLICY_MIN_LEN(1), | 
|---|
| 989 | [NL80211_WOWLAN_TCP_WAKE_MASK] = NLA_POLICY_MIN_LEN(1), | 
|---|
| 990 | }; | 
|---|
| 991 | #endif /* CONFIG_PM */ | 
|---|
| 992 |  | 
|---|
| 993 | /* policy for coalesce rule attributes */ | 
|---|
| 994 | static const struct nla_policy | 
|---|
| 995 | nl80211_coalesce_policy[NUM_NL80211_ATTR_COALESCE_RULE] = { | 
|---|
| 996 | [NL80211_ATTR_COALESCE_RULE_DELAY] = { .type = NLA_U32 }, | 
|---|
| 997 | [NL80211_ATTR_COALESCE_RULE_CONDITION] = | 
|---|
| 998 | NLA_POLICY_RANGE(NLA_U32, | 
|---|
| 999 | NL80211_COALESCE_CONDITION_MATCH, | 
|---|
| 1000 | NL80211_COALESCE_CONDITION_NO_MATCH), | 
|---|
| 1001 | [NL80211_ATTR_COALESCE_RULE_PKT_PATTERN] = { .type = NLA_NESTED }, | 
|---|
| 1002 | }; | 
|---|
| 1003 |  | 
|---|
| 1004 | /* policy for GTK rekey offload attributes */ | 
|---|
| 1005 | static const struct nla_policy | 
|---|
| 1006 | nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = { | 
|---|
| 1007 | [NL80211_REKEY_DATA_KEK] = { | 
|---|
| 1008 | .type = NLA_BINARY, | 
|---|
| 1009 | .len = NL80211_KEK_EXT_LEN | 
|---|
| 1010 | }, | 
|---|
| 1011 | [NL80211_REKEY_DATA_KCK] = { | 
|---|
| 1012 | .type = NLA_BINARY, | 
|---|
| 1013 | .len = NL80211_KCK_EXT_LEN_32 | 
|---|
| 1014 | }, | 
|---|
| 1015 | [NL80211_REKEY_DATA_REPLAY_CTR] = NLA_POLICY_EXACT_LEN(NL80211_REPLAY_CTR_LEN), | 
|---|
| 1016 | [NL80211_REKEY_DATA_AKM] = { .type = NLA_U32 }, | 
|---|
| 1017 | }; | 
|---|
| 1018 |  | 
|---|
| 1019 | static const struct nla_policy | 
|---|
| 1020 | nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = { | 
|---|
| 1021 | [NL80211_SCHED_SCAN_MATCH_ATTR_SSID] = { .type = NLA_BINARY, | 
|---|
| 1022 | .len = IEEE80211_MAX_SSID_LEN }, | 
|---|
| 1023 | [NL80211_SCHED_SCAN_MATCH_ATTR_BSSID] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), | 
|---|
| 1024 | [NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 }, | 
|---|
| 1025 | }; | 
|---|
| 1026 |  | 
|---|
| 1027 | static const struct nla_policy | 
|---|
| 1028 | nl80211_plan_policy[NL80211_SCHED_SCAN_PLAN_MAX + 1] = { | 
|---|
| 1029 | [NL80211_SCHED_SCAN_PLAN_INTERVAL] = { .type = NLA_U32 }, | 
|---|
| 1030 | [NL80211_SCHED_SCAN_PLAN_ITERATIONS] = { .type = NLA_U32 }, | 
|---|
| 1031 | }; | 
|---|
| 1032 |  | 
|---|
| 1033 | static const struct nla_policy | 
|---|
| 1034 | nl80211_bss_select_policy[NL80211_BSS_SELECT_ATTR_MAX + 1] = { | 
|---|
| 1035 | [NL80211_BSS_SELECT_ATTR_RSSI] = { .type = NLA_FLAG }, | 
|---|
| 1036 | [NL80211_BSS_SELECT_ATTR_BAND_PREF] = { .type = NLA_U32 }, | 
|---|
| 1037 | [NL80211_BSS_SELECT_ATTR_RSSI_ADJUST] = { | 
|---|
| 1038 | .len = sizeof(struct nl80211_bss_select_rssi_adjust) | 
|---|
| 1039 | }, | 
|---|
| 1040 | }; | 
|---|
| 1041 |  | 
|---|
| 1042 | /* policy for NAN function attributes */ | 
|---|
| 1043 | static const struct nla_policy | 
|---|
| 1044 | nl80211_nan_func_policy[NL80211_NAN_FUNC_ATTR_MAX + 1] = { | 
|---|
| 1045 | [NL80211_NAN_FUNC_TYPE] = | 
|---|
| 1046 | NLA_POLICY_MAX(NLA_U8, NL80211_NAN_FUNC_MAX_TYPE), | 
|---|
| 1047 | [NL80211_NAN_FUNC_SERVICE_ID] = { | 
|---|
| 1048 | .len = NL80211_NAN_FUNC_SERVICE_ID_LEN }, | 
|---|
| 1049 | [NL80211_NAN_FUNC_PUBLISH_TYPE] = { .type = NLA_U8 }, | 
|---|
| 1050 | [NL80211_NAN_FUNC_PUBLISH_BCAST] = { .type = NLA_FLAG }, | 
|---|
| 1051 | [NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE] = { .type = NLA_FLAG }, | 
|---|
| 1052 | [NL80211_NAN_FUNC_FOLLOW_UP_ID] = { .type = NLA_U8 }, | 
|---|
| 1053 | [NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID] = { .type = NLA_U8 }, | 
|---|
| 1054 | [NL80211_NAN_FUNC_FOLLOW_UP_DEST] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), | 
|---|
| 1055 | [NL80211_NAN_FUNC_CLOSE_RANGE] = { .type = NLA_FLAG }, | 
|---|
| 1056 | [NL80211_NAN_FUNC_TTL] = { .type = NLA_U32 }, | 
|---|
| 1057 | [NL80211_NAN_FUNC_SERVICE_INFO] = { .type = NLA_BINARY, | 
|---|
| 1058 | .len = NL80211_NAN_FUNC_SERVICE_SPEC_INFO_MAX_LEN }, | 
|---|
| 1059 | [NL80211_NAN_FUNC_SRF] = { .type = NLA_NESTED }, | 
|---|
| 1060 | [NL80211_NAN_FUNC_RX_MATCH_FILTER] = { .type = NLA_NESTED }, | 
|---|
| 1061 | [NL80211_NAN_FUNC_TX_MATCH_FILTER] = { .type = NLA_NESTED }, | 
|---|
| 1062 | [NL80211_NAN_FUNC_INSTANCE_ID] = { .type = NLA_U8 }, | 
|---|
| 1063 | [NL80211_NAN_FUNC_TERM_REASON] = { .type = NLA_U8 }, | 
|---|
| 1064 | }; | 
|---|
| 1065 |  | 
|---|
| 1066 | /* policy for Service Response Filter attributes */ | 
|---|
| 1067 | static const struct nla_policy | 
|---|
| 1068 | nl80211_nan_srf_policy[NL80211_NAN_SRF_ATTR_MAX + 1] = { | 
|---|
| 1069 | [NL80211_NAN_SRF_INCLUDE] = { .type = NLA_FLAG }, | 
|---|
| 1070 | [NL80211_NAN_SRF_BF] = { .type = NLA_BINARY, | 
|---|
| 1071 | .len =  NL80211_NAN_FUNC_SRF_MAX_LEN }, | 
|---|
| 1072 | [NL80211_NAN_SRF_BF_IDX] = { .type = NLA_U8 }, | 
|---|
| 1073 | [NL80211_NAN_SRF_MAC_ADDRS] = { .type = NLA_NESTED }, | 
|---|
| 1074 | }; | 
|---|
| 1075 |  | 
|---|
| 1076 | /* policy for packet pattern attributes */ | 
|---|
| 1077 | static const struct nla_policy | 
|---|
| 1078 | nl80211_packet_pattern_policy[MAX_NL80211_PKTPAT + 1] = { | 
|---|
| 1079 | [NL80211_PKTPAT_MASK] = { .type = NLA_BINARY, }, | 
|---|
| 1080 | [NL80211_PKTPAT_PATTERN] = { .type = NLA_BINARY, }, | 
|---|
| 1081 | [NL80211_PKTPAT_OFFSET] = { .type = NLA_U32 }, | 
|---|
| 1082 | }; | 
|---|
| 1083 |  | 
|---|
| 1084 | static int nl80211_prepare_wdev_dump(struct netlink_callback *cb, | 
|---|
| 1085 | struct cfg80211_registered_device **rdev, | 
|---|
| 1086 | struct wireless_dev **wdev, | 
|---|
| 1087 | struct nlattr **attrbuf) | 
|---|
| 1088 | { | 
|---|
| 1089 | int err; | 
|---|
| 1090 |  | 
|---|
| 1091 | if (!cb->args[0]) { | 
|---|
| 1092 | struct nlattr **attrbuf_free = NULL; | 
|---|
| 1093 |  | 
|---|
| 1094 | if (!attrbuf) { | 
|---|
| 1095 | attrbuf = kcalloc(NUM_NL80211_ATTR, sizeof(*attrbuf), | 
|---|
| 1096 | GFP_KERNEL); | 
|---|
| 1097 | if (!attrbuf) | 
|---|
| 1098 | return -ENOMEM; | 
|---|
| 1099 | attrbuf_free = attrbuf; | 
|---|
| 1100 | } | 
|---|
| 1101 |  | 
|---|
| 1102 | err = nlmsg_parse_deprecated(nlh: cb->nlh, | 
|---|
| 1103 | GENL_HDRLEN + nl80211_fam.hdrsize, | 
|---|
| 1104 | tb: attrbuf, maxtype: nl80211_fam.maxattr, | 
|---|
| 1105 | policy: nl80211_policy, NULL); | 
|---|
| 1106 | if (err) { | 
|---|
| 1107 | kfree(objp: attrbuf_free); | 
|---|
| 1108 | return err; | 
|---|
| 1109 | } | 
|---|
| 1110 |  | 
|---|
| 1111 | rtnl_lock(); | 
|---|
| 1112 | *wdev = __cfg80211_wdev_from_attrs(NULL, netns: sock_net(sk: cb->skb->sk), | 
|---|
| 1113 | attrs: attrbuf); | 
|---|
| 1114 | kfree(objp: attrbuf_free); | 
|---|
| 1115 | if (IS_ERR(ptr: *wdev)) { | 
|---|
| 1116 | rtnl_unlock(); | 
|---|
| 1117 | return PTR_ERR(ptr: *wdev); | 
|---|
| 1118 | } | 
|---|
| 1119 | *rdev = wiphy_to_rdev(wiphy: (*wdev)->wiphy); | 
|---|
| 1120 | mutex_lock(lock: &(*rdev)->wiphy.mtx); | 
|---|
| 1121 | rtnl_unlock(); | 
|---|
| 1122 | /* 0 is the first index - add 1 to parse only once */ | 
|---|
| 1123 | cb->args[0] = (*rdev)->wiphy_idx + 1; | 
|---|
| 1124 | cb->args[1] = (*wdev)->identifier; | 
|---|
| 1125 | } else { | 
|---|
| 1126 | /* subtract the 1 again here */ | 
|---|
| 1127 | struct wiphy *wiphy; | 
|---|
| 1128 | struct wireless_dev *tmp; | 
|---|
| 1129 |  | 
|---|
| 1130 | rtnl_lock(); | 
|---|
| 1131 | wiphy = wiphy_idx_to_wiphy(wiphy_idx: cb->args[0] - 1); | 
|---|
| 1132 | if (!wiphy) { | 
|---|
| 1133 | rtnl_unlock(); | 
|---|
| 1134 | return -ENODEV; | 
|---|
| 1135 | } | 
|---|
| 1136 | *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 1137 | *wdev = NULL; | 
|---|
| 1138 |  | 
|---|
| 1139 | list_for_each_entry(tmp, &(*rdev)->wiphy.wdev_list, list) { | 
|---|
| 1140 | if (tmp->identifier == cb->args[1]) { | 
|---|
| 1141 | *wdev = tmp; | 
|---|
| 1142 | break; | 
|---|
| 1143 | } | 
|---|
| 1144 | } | 
|---|
| 1145 |  | 
|---|
| 1146 | if (!*wdev) { | 
|---|
| 1147 | rtnl_unlock(); | 
|---|
| 1148 | return -ENODEV; | 
|---|
| 1149 | } | 
|---|
| 1150 | mutex_lock(lock: &(*rdev)->wiphy.mtx); | 
|---|
| 1151 | rtnl_unlock(); | 
|---|
| 1152 | } | 
|---|
| 1153 |  | 
|---|
| 1154 | return 0; | 
|---|
| 1155 | } | 
|---|
| 1156 |  | 
|---|
| 1157 | /* message building helper */ | 
|---|
| 1158 | void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq, | 
|---|
| 1159 | int flags, u8 cmd) | 
|---|
| 1160 | { | 
|---|
| 1161 | /* since there is no private header just add the generic one */ | 
|---|
| 1162 | return genlmsg_put(skb, portid, seq, family: &nl80211_fam, flags, cmd); | 
|---|
| 1163 | } | 
|---|
| 1164 |  | 
|---|
| 1165 | static int nl80211_msg_put_wmm_rules(struct sk_buff *msg, | 
|---|
| 1166 | const struct ieee80211_reg_rule *rule) | 
|---|
| 1167 | { | 
|---|
| 1168 | int j; | 
|---|
| 1169 | struct nlattr *nl_wmm_rules = | 
|---|
| 1170 | nla_nest_start_noflag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_WMM); | 
|---|
| 1171 |  | 
|---|
| 1172 | if (!nl_wmm_rules) | 
|---|
| 1173 | goto nla_put_failure; | 
|---|
| 1174 |  | 
|---|
| 1175 | for (j = 0; j < IEEE80211_NUM_ACS; j++) { | 
|---|
| 1176 | struct nlattr *nl_wmm_rule = nla_nest_start_noflag(skb: msg, attrtype: j); | 
|---|
| 1177 |  | 
|---|
| 1178 | if (!nl_wmm_rule) | 
|---|
| 1179 | goto nla_put_failure; | 
|---|
| 1180 |  | 
|---|
| 1181 | if (nla_put_u16(skb: msg, attrtype: NL80211_WMMR_CW_MIN, | 
|---|
| 1182 | value: rule->wmm_rule.client[j].cw_min) || | 
|---|
| 1183 | nla_put_u16(skb: msg, attrtype: NL80211_WMMR_CW_MAX, | 
|---|
| 1184 | value: rule->wmm_rule.client[j].cw_max) || | 
|---|
| 1185 | nla_put_u8(skb: msg, attrtype: NL80211_WMMR_AIFSN, | 
|---|
| 1186 | value: rule->wmm_rule.client[j].aifsn) || | 
|---|
| 1187 | nla_put_u16(skb: msg, attrtype: NL80211_WMMR_TXOP, | 
|---|
| 1188 | value: rule->wmm_rule.client[j].cot)) | 
|---|
| 1189 | goto nla_put_failure; | 
|---|
| 1190 |  | 
|---|
| 1191 | nla_nest_end(skb: msg, start: nl_wmm_rule); | 
|---|
| 1192 | } | 
|---|
| 1193 | nla_nest_end(skb: msg, start: nl_wmm_rules); | 
|---|
| 1194 |  | 
|---|
| 1195 | return 0; | 
|---|
| 1196 |  | 
|---|
| 1197 | nla_put_failure: | 
|---|
| 1198 | return -ENOBUFS; | 
|---|
| 1199 | } | 
|---|
| 1200 |  | 
|---|
| 1201 | static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy, | 
|---|
| 1202 | struct ieee80211_channel *chan, | 
|---|
| 1203 | bool large) | 
|---|
| 1204 | { | 
|---|
| 1205 | /* Some channels must be completely excluded from the | 
|---|
| 1206 | * list to protect old user-space tools from breaking | 
|---|
| 1207 | */ | 
|---|
| 1208 | if (!large && chan->flags & | 
|---|
| 1209 | (IEEE80211_CHAN_NO_10MHZ | IEEE80211_CHAN_NO_20MHZ)) | 
|---|
| 1210 | return 0; | 
|---|
| 1211 | if (!large && chan->freq_offset) | 
|---|
| 1212 | return 0; | 
|---|
| 1213 |  | 
|---|
| 1214 | if (nla_put_u32(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_FREQ, | 
|---|
| 1215 | value: chan->center_freq)) | 
|---|
| 1216 | goto nla_put_failure; | 
|---|
| 1217 |  | 
|---|
| 1218 | if (nla_put_u32(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_OFFSET, value: chan->freq_offset)) | 
|---|
| 1219 | goto nla_put_failure; | 
|---|
| 1220 |  | 
|---|
| 1221 | if ((chan->flags & IEEE80211_CHAN_PSD) && | 
|---|
| 1222 | nla_put_s8(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_PSD, value: chan->psd)) | 
|---|
| 1223 | goto nla_put_failure; | 
|---|
| 1224 |  | 
|---|
| 1225 | if ((chan->flags & IEEE80211_CHAN_DISABLED) && | 
|---|
| 1226 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_DISABLED)) | 
|---|
| 1227 | goto nla_put_failure; | 
|---|
| 1228 | if (chan->flags & IEEE80211_CHAN_NO_IR) { | 
|---|
| 1229 | if (nla_put_flag(skb: msg, NL80211_FREQUENCY_ATTR_NO_IR)) | 
|---|
| 1230 | goto nla_put_failure; | 
|---|
| 1231 | if (nla_put_flag(skb: msg, attrtype: __NL80211_FREQUENCY_ATTR_NO_IBSS)) | 
|---|
| 1232 | goto nla_put_failure; | 
|---|
| 1233 | } | 
|---|
| 1234 | if (chan->flags & IEEE80211_CHAN_RADAR) { | 
|---|
| 1235 | if (nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_RADAR)) | 
|---|
| 1236 | goto nla_put_failure; | 
|---|
| 1237 | if (large) { | 
|---|
| 1238 | u32 time; | 
|---|
| 1239 |  | 
|---|
| 1240 | time = elapsed_jiffies_msecs(start: chan->dfs_state_entered); | 
|---|
| 1241 |  | 
|---|
| 1242 | if (nla_put_u32(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_DFS_STATE, | 
|---|
| 1243 | value: chan->dfs_state)) | 
|---|
| 1244 | goto nla_put_failure; | 
|---|
| 1245 | if (nla_put_u32(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_DFS_TIME, | 
|---|
| 1246 | value: time)) | 
|---|
| 1247 | goto nla_put_failure; | 
|---|
| 1248 | if (nla_put_u32(skb: msg, | 
|---|
| 1249 | attrtype: NL80211_FREQUENCY_ATTR_DFS_CAC_TIME, | 
|---|
| 1250 | value: chan->dfs_cac_ms)) | 
|---|
| 1251 | goto nla_put_failure; | 
|---|
| 1252 | } | 
|---|
| 1253 | } | 
|---|
| 1254 |  | 
|---|
| 1255 | if (large) { | 
|---|
| 1256 | if ((chan->flags & IEEE80211_CHAN_NO_HT40MINUS) && | 
|---|
| 1257 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_HT40_MINUS)) | 
|---|
| 1258 | goto nla_put_failure; | 
|---|
| 1259 | if ((chan->flags & IEEE80211_CHAN_NO_HT40PLUS) && | 
|---|
| 1260 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_HT40_PLUS)) | 
|---|
| 1261 | goto nla_put_failure; | 
|---|
| 1262 | if ((chan->flags & IEEE80211_CHAN_NO_80MHZ) && | 
|---|
| 1263 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_80MHZ)) | 
|---|
| 1264 | goto nla_put_failure; | 
|---|
| 1265 | if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) && | 
|---|
| 1266 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_160MHZ)) | 
|---|
| 1267 | goto nla_put_failure; | 
|---|
| 1268 | if ((chan->flags & IEEE80211_CHAN_INDOOR_ONLY) && | 
|---|
| 1269 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_INDOOR_ONLY)) | 
|---|
| 1270 | goto nla_put_failure; | 
|---|
| 1271 | if ((chan->flags & IEEE80211_CHAN_IR_CONCURRENT) && | 
|---|
| 1272 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_IR_CONCURRENT)) | 
|---|
| 1273 | goto nla_put_failure; | 
|---|
| 1274 | if ((chan->flags & IEEE80211_CHAN_NO_20MHZ) && | 
|---|
| 1275 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_20MHZ)) | 
|---|
| 1276 | goto nla_put_failure; | 
|---|
| 1277 | if ((chan->flags & IEEE80211_CHAN_NO_10MHZ) && | 
|---|
| 1278 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_10MHZ)) | 
|---|
| 1279 | goto nla_put_failure; | 
|---|
| 1280 | if ((chan->flags & IEEE80211_CHAN_NO_HE) && | 
|---|
| 1281 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_HE)) | 
|---|
| 1282 | goto nla_put_failure; | 
|---|
| 1283 | if ((chan->flags & IEEE80211_CHAN_NO_320MHZ) && | 
|---|
| 1284 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_320MHZ)) | 
|---|
| 1285 | goto nla_put_failure; | 
|---|
| 1286 | if ((chan->flags & IEEE80211_CHAN_NO_EHT) && | 
|---|
| 1287 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_EHT)) | 
|---|
| 1288 | goto nla_put_failure; | 
|---|
| 1289 | if ((chan->flags & IEEE80211_CHAN_DFS_CONCURRENT) && | 
|---|
| 1290 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_DFS_CONCURRENT)) | 
|---|
| 1291 | goto nla_put_failure; | 
|---|
| 1292 | if ((chan->flags & IEEE80211_CHAN_NO_6GHZ_VLP_CLIENT) && | 
|---|
| 1293 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT)) | 
|---|
| 1294 | goto nla_put_failure; | 
|---|
| 1295 | if ((chan->flags & IEEE80211_CHAN_NO_6GHZ_AFC_CLIENT) && | 
|---|
| 1296 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT)) | 
|---|
| 1297 | goto nla_put_failure; | 
|---|
| 1298 | if ((chan->flags & IEEE80211_CHAN_CAN_MONITOR) && | 
|---|
| 1299 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_CAN_MONITOR)) | 
|---|
| 1300 | goto nla_put_failure; | 
|---|
| 1301 | if ((chan->flags & IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP) && | 
|---|
| 1302 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_ALLOW_6GHZ_VLP_AP)) | 
|---|
| 1303 | goto nla_put_failure; | 
|---|
| 1304 | if ((chan->flags & IEEE80211_CHAN_ALLOW_20MHZ_ACTIVITY) && | 
|---|
| 1305 | nla_put_flag(skb: msg, | 
|---|
| 1306 | attrtype: NL80211_FREQUENCY_ATTR_ALLOW_20MHZ_ACTIVITY)) | 
|---|
| 1307 | goto nla_put_failure; | 
|---|
| 1308 | if ((chan->flags & IEEE80211_CHAN_NO_4MHZ) && | 
|---|
| 1309 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_4MHZ)) | 
|---|
| 1310 | goto nla_put_failure; | 
|---|
| 1311 | if ((chan->flags & IEEE80211_CHAN_NO_8MHZ) && | 
|---|
| 1312 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_8MHZ)) | 
|---|
| 1313 | goto nla_put_failure; | 
|---|
| 1314 | if ((chan->flags & IEEE80211_CHAN_NO_16MHZ) && | 
|---|
| 1315 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_16MHZ)) | 
|---|
| 1316 | goto nla_put_failure; | 
|---|
| 1317 | } | 
|---|
| 1318 |  | 
|---|
| 1319 | if (nla_put_u32(skb: msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, | 
|---|
| 1320 | DBM_TO_MBM(chan->max_power))) | 
|---|
| 1321 | goto nla_put_failure; | 
|---|
| 1322 |  | 
|---|
| 1323 | if (large) { | 
|---|
| 1324 | const struct ieee80211_reg_rule *rule = | 
|---|
| 1325 | freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq)); | 
|---|
| 1326 |  | 
|---|
| 1327 | if (!IS_ERR_OR_NULL(ptr: rule) && rule->has_wmm) { | 
|---|
| 1328 | if (nl80211_msg_put_wmm_rules(msg, rule)) | 
|---|
| 1329 | goto nla_put_failure; | 
|---|
| 1330 | } | 
|---|
| 1331 | } | 
|---|
| 1332 |  | 
|---|
| 1333 | return 0; | 
|---|
| 1334 |  | 
|---|
| 1335 | nla_put_failure: | 
|---|
| 1336 | return -ENOBUFS; | 
|---|
| 1337 | } | 
|---|
| 1338 |  | 
|---|
| 1339 | static bool nl80211_put_txq_stats(struct sk_buff *msg, | 
|---|
| 1340 | struct cfg80211_txq_stats *txqstats, | 
|---|
| 1341 | int attrtype) | 
|---|
| 1342 | { | 
|---|
| 1343 | struct nlattr *txqattr; | 
|---|
| 1344 |  | 
|---|
| 1345 | #define PUT_TXQVAL_U32(attr, memb) do {					  \ | 
|---|
| 1346 | if (txqstats->filled & BIT(NL80211_TXQ_STATS_ ## attr) &&	  \ | 
|---|
| 1347 | nla_put_u32(msg, NL80211_TXQ_STATS_ ## attr, txqstats->memb)) \ | 
|---|
| 1348 | return false;						  \ | 
|---|
| 1349 | } while (0) | 
|---|
| 1350 |  | 
|---|
| 1351 | txqattr = nla_nest_start_noflag(skb: msg, attrtype); | 
|---|
| 1352 | if (!txqattr) | 
|---|
| 1353 | return false; | 
|---|
| 1354 |  | 
|---|
| 1355 | PUT_TXQVAL_U32(BACKLOG_BYTES, backlog_bytes); | 
|---|
| 1356 | PUT_TXQVAL_U32(BACKLOG_PACKETS, backlog_packets); | 
|---|
| 1357 | PUT_TXQVAL_U32(FLOWS, flows); | 
|---|
| 1358 | PUT_TXQVAL_U32(DROPS, drops); | 
|---|
| 1359 | PUT_TXQVAL_U32(ECN_MARKS, ecn_marks); | 
|---|
| 1360 | PUT_TXQVAL_U32(OVERLIMIT, overlimit); | 
|---|
| 1361 | PUT_TXQVAL_U32(OVERMEMORY, overmemory); | 
|---|
| 1362 | PUT_TXQVAL_U32(COLLISIONS, collisions); | 
|---|
| 1363 | PUT_TXQVAL_U32(TX_BYTES, tx_bytes); | 
|---|
| 1364 | PUT_TXQVAL_U32(TX_PACKETS, tx_packets); | 
|---|
| 1365 | PUT_TXQVAL_U32(MAX_FLOWS, max_flows); | 
|---|
| 1366 | nla_nest_end(skb: msg, start: txqattr); | 
|---|
| 1367 |  | 
|---|
| 1368 | #undef PUT_TXQVAL_U32 | 
|---|
| 1369 | return true; | 
|---|
| 1370 | } | 
|---|
| 1371 |  | 
|---|
| 1372 | /* netlink command implementations */ | 
|---|
| 1373 |  | 
|---|
| 1374 | /** | 
|---|
| 1375 | * nl80211_link_id - return link ID | 
|---|
| 1376 | * @attrs: attributes to look at | 
|---|
| 1377 | * | 
|---|
| 1378 | * Returns: the link ID or 0 if not given | 
|---|
| 1379 | * | 
|---|
| 1380 | * Note this function doesn't do any validation of the link | 
|---|
| 1381 | * ID validity wrt. links that were actually added, so it must | 
|---|
| 1382 | * be called only from ops with %NL80211_FLAG_MLO_VALID_LINK_ID | 
|---|
| 1383 | * or if additional validation is done. | 
|---|
| 1384 | */ | 
|---|
| 1385 | static unsigned int nl80211_link_id(struct nlattr **attrs) | 
|---|
| 1386 | { | 
|---|
| 1387 | struct nlattr *linkid = attrs[NL80211_ATTR_MLO_LINK_ID]; | 
|---|
| 1388 |  | 
|---|
| 1389 | return nla_get_u8_default(nla: linkid, defvalue: 0); | 
|---|
| 1390 | } | 
|---|
| 1391 |  | 
|---|
| 1392 | static int nl80211_link_id_or_invalid(struct nlattr **attrs) | 
|---|
| 1393 | { | 
|---|
| 1394 | struct nlattr *linkid = attrs[NL80211_ATTR_MLO_LINK_ID]; | 
|---|
| 1395 |  | 
|---|
| 1396 | if (!linkid) | 
|---|
| 1397 | return -1; | 
|---|
| 1398 |  | 
|---|
| 1399 | return nla_get_u8(nla: linkid); | 
|---|
| 1400 | } | 
|---|
| 1401 |  | 
|---|
| 1402 | struct key_parse { | 
|---|
| 1403 | struct key_params p; | 
|---|
| 1404 | int idx; | 
|---|
| 1405 | int type; | 
|---|
| 1406 | bool def, defmgmt, defbeacon; | 
|---|
| 1407 | bool def_uni, def_multi; | 
|---|
| 1408 | }; | 
|---|
| 1409 |  | 
|---|
| 1410 | static int nl80211_parse_key_new(struct genl_info *info, struct nlattr *key, | 
|---|
| 1411 | struct key_parse *k) | 
|---|
| 1412 | { | 
|---|
| 1413 | struct nlattr *tb[NL80211_KEY_MAX + 1]; | 
|---|
| 1414 | int err = nla_parse_nested_deprecated(tb, maxtype: NL80211_KEY_MAX, nla: key, | 
|---|
| 1415 | policy: nl80211_key_policy, | 
|---|
| 1416 | extack: info->extack); | 
|---|
| 1417 | if (err) | 
|---|
| 1418 | return err; | 
|---|
| 1419 |  | 
|---|
| 1420 | k->def = !!tb[NL80211_KEY_DEFAULT]; | 
|---|
| 1421 | k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT]; | 
|---|
| 1422 | k->defbeacon = !!tb[NL80211_KEY_DEFAULT_BEACON]; | 
|---|
| 1423 |  | 
|---|
| 1424 | if (k->def) { | 
|---|
| 1425 | k->def_uni = true; | 
|---|
| 1426 | k->def_multi = true; | 
|---|
| 1427 | } | 
|---|
| 1428 | if (k->defmgmt || k->defbeacon) | 
|---|
| 1429 | k->def_multi = true; | 
|---|
| 1430 |  | 
|---|
| 1431 | if (tb[NL80211_KEY_IDX]) | 
|---|
| 1432 | k->idx = nla_get_u8(nla: tb[NL80211_KEY_IDX]); | 
|---|
| 1433 |  | 
|---|
| 1434 | if (tb[NL80211_KEY_DATA]) { | 
|---|
| 1435 | k->p.key = nla_data(nla: tb[NL80211_KEY_DATA]); | 
|---|
| 1436 | k->p.key_len = nla_len(nla: tb[NL80211_KEY_DATA]); | 
|---|
| 1437 | } | 
|---|
| 1438 |  | 
|---|
| 1439 | if (tb[NL80211_KEY_SEQ]) { | 
|---|
| 1440 | k->p.seq = nla_data(nla: tb[NL80211_KEY_SEQ]); | 
|---|
| 1441 | k->p.seq_len = nla_len(nla: tb[NL80211_KEY_SEQ]); | 
|---|
| 1442 | } | 
|---|
| 1443 |  | 
|---|
| 1444 | if (tb[NL80211_KEY_CIPHER]) | 
|---|
| 1445 | k->p.cipher = nla_get_u32(nla: tb[NL80211_KEY_CIPHER]); | 
|---|
| 1446 |  | 
|---|
| 1447 | if (tb[NL80211_KEY_TYPE]) | 
|---|
| 1448 | k->type = nla_get_u32(nla: tb[NL80211_KEY_TYPE]); | 
|---|
| 1449 |  | 
|---|
| 1450 | if (tb[NL80211_KEY_DEFAULT_TYPES]) { | 
|---|
| 1451 | struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES]; | 
|---|
| 1452 |  | 
|---|
| 1453 | err = nla_parse_nested_deprecated(tb: kdt, | 
|---|
| 1454 | maxtype: NUM_NL80211_KEY_DEFAULT_TYPES - 1, | 
|---|
| 1455 | nla: tb[NL80211_KEY_DEFAULT_TYPES], | 
|---|
| 1456 | policy: nl80211_key_default_policy, | 
|---|
| 1457 | extack: info->extack); | 
|---|
| 1458 | if (err) | 
|---|
| 1459 | return err; | 
|---|
| 1460 |  | 
|---|
| 1461 | k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST]; | 
|---|
| 1462 | k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST]; | 
|---|
| 1463 | } | 
|---|
| 1464 |  | 
|---|
| 1465 | if (tb[NL80211_KEY_MODE]) | 
|---|
| 1466 | k->p.mode = nla_get_u8(nla: tb[NL80211_KEY_MODE]); | 
|---|
| 1467 |  | 
|---|
| 1468 | return 0; | 
|---|
| 1469 | } | 
|---|
| 1470 |  | 
|---|
| 1471 | static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k) | 
|---|
| 1472 | { | 
|---|
| 1473 | if (info->attrs[NL80211_ATTR_KEY_DATA]) { | 
|---|
| 1474 | k->p.key = nla_data(nla: info->attrs[NL80211_ATTR_KEY_DATA]); | 
|---|
| 1475 | k->p.key_len = nla_len(nla: info->attrs[NL80211_ATTR_KEY_DATA]); | 
|---|
| 1476 | } | 
|---|
| 1477 |  | 
|---|
| 1478 | if (info->attrs[NL80211_ATTR_KEY_SEQ]) { | 
|---|
| 1479 | k->p.seq = nla_data(nla: info->attrs[NL80211_ATTR_KEY_SEQ]); | 
|---|
| 1480 | k->p.seq_len = nla_len(nla: info->attrs[NL80211_ATTR_KEY_SEQ]); | 
|---|
| 1481 | } | 
|---|
| 1482 |  | 
|---|
| 1483 | if (info->attrs[NL80211_ATTR_KEY_IDX]) | 
|---|
| 1484 | k->idx = nla_get_u8(nla: info->attrs[NL80211_ATTR_KEY_IDX]); | 
|---|
| 1485 |  | 
|---|
| 1486 | if (info->attrs[NL80211_ATTR_KEY_CIPHER]) | 
|---|
| 1487 | k->p.cipher = nla_get_u32(nla: info->attrs[NL80211_ATTR_KEY_CIPHER]); | 
|---|
| 1488 |  | 
|---|
| 1489 | k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT]; | 
|---|
| 1490 | k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]; | 
|---|
| 1491 |  | 
|---|
| 1492 | if (k->def) { | 
|---|
| 1493 | k->def_uni = true; | 
|---|
| 1494 | k->def_multi = true; | 
|---|
| 1495 | } | 
|---|
| 1496 | if (k->defmgmt) | 
|---|
| 1497 | k->def_multi = true; | 
|---|
| 1498 |  | 
|---|
| 1499 | if (info->attrs[NL80211_ATTR_KEY_TYPE]) | 
|---|
| 1500 | k->type = nla_get_u32(nla: info->attrs[NL80211_ATTR_KEY_TYPE]); | 
|---|
| 1501 |  | 
|---|
| 1502 | if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) { | 
|---|
| 1503 | struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES]; | 
|---|
| 1504 | int err = nla_parse_nested_deprecated(tb: kdt, | 
|---|
| 1505 | maxtype: NUM_NL80211_KEY_DEFAULT_TYPES - 1, | 
|---|
| 1506 | nla: info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES], | 
|---|
| 1507 | policy: nl80211_key_default_policy, | 
|---|
| 1508 | extack: info->extack); | 
|---|
| 1509 | if (err) | 
|---|
| 1510 | return err; | 
|---|
| 1511 |  | 
|---|
| 1512 | k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST]; | 
|---|
| 1513 | k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST]; | 
|---|
| 1514 | } | 
|---|
| 1515 |  | 
|---|
| 1516 | return 0; | 
|---|
| 1517 | } | 
|---|
| 1518 |  | 
|---|
| 1519 | static int nl80211_parse_key(struct genl_info *info, struct key_parse *k) | 
|---|
| 1520 | { | 
|---|
| 1521 | int err; | 
|---|
| 1522 |  | 
|---|
| 1523 | memset(s: k, c: 0, n: sizeof(*k)); | 
|---|
| 1524 | k->idx = -1; | 
|---|
| 1525 | k->type = -1; | 
|---|
| 1526 |  | 
|---|
| 1527 | if (info->attrs[NL80211_ATTR_KEY]) | 
|---|
| 1528 | err = nl80211_parse_key_new(info, key: info->attrs[NL80211_ATTR_KEY], k); | 
|---|
| 1529 | else | 
|---|
| 1530 | err = nl80211_parse_key_old(info, k); | 
|---|
| 1531 |  | 
|---|
| 1532 | if (err) | 
|---|
| 1533 | return err; | 
|---|
| 1534 |  | 
|---|
| 1535 | if ((k->def ? 1 : 0) + (k->defmgmt ? 1 : 0) + | 
|---|
| 1536 | (k->defbeacon ? 1 : 0) > 1) { | 
|---|
| 1537 | GENL_SET_ERR_MSG(info, | 
|---|
| 1538 | "key with multiple default flags is invalid"); | 
|---|
| 1539 | return -EINVAL; | 
|---|
| 1540 | } | 
|---|
| 1541 |  | 
|---|
| 1542 | if (k->defmgmt || k->defbeacon) { | 
|---|
| 1543 | if (k->def_uni || !k->def_multi) { | 
|---|
| 1544 | GENL_SET_ERR_MSG(info, | 
|---|
| 1545 | "defmgmt/defbeacon key must be mcast"); | 
|---|
| 1546 | return -EINVAL; | 
|---|
| 1547 | } | 
|---|
| 1548 | } | 
|---|
| 1549 |  | 
|---|
| 1550 | if (k->idx != -1) { | 
|---|
| 1551 | if (k->defmgmt) { | 
|---|
| 1552 | if (k->idx < 4 || k->idx > 5) { | 
|---|
| 1553 | GENL_SET_ERR_MSG(info, | 
|---|
| 1554 | "defmgmt key idx not 4 or 5"); | 
|---|
| 1555 | return -EINVAL; | 
|---|
| 1556 | } | 
|---|
| 1557 | } else if (k->defbeacon) { | 
|---|
| 1558 | if (k->idx < 6 || k->idx > 7) { | 
|---|
| 1559 | GENL_SET_ERR_MSG(info, | 
|---|
| 1560 | "defbeacon key idx not 6 or 7"); | 
|---|
| 1561 | return -EINVAL; | 
|---|
| 1562 | } | 
|---|
| 1563 | } else if (k->def) { | 
|---|
| 1564 | if (k->idx < 0 || k->idx > 3) { | 
|---|
| 1565 | GENL_SET_ERR_MSG(info, "def key idx not 0-3"); | 
|---|
| 1566 | return -EINVAL; | 
|---|
| 1567 | } | 
|---|
| 1568 | } else { | 
|---|
| 1569 | if (k->idx < 0 || k->idx > 7) { | 
|---|
| 1570 | GENL_SET_ERR_MSG(info, "key idx not 0-7"); | 
|---|
| 1571 | return -EINVAL; | 
|---|
| 1572 | } | 
|---|
| 1573 | } | 
|---|
| 1574 | } | 
|---|
| 1575 |  | 
|---|
| 1576 | return 0; | 
|---|
| 1577 | } | 
|---|
| 1578 |  | 
|---|
| 1579 | static struct cfg80211_cached_keys * | 
|---|
| 1580 | nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, | 
|---|
| 1581 | struct genl_info *info, bool *no_ht) | 
|---|
| 1582 | { | 
|---|
| 1583 | struct nlattr *keys = info->attrs[NL80211_ATTR_KEYS]; | 
|---|
| 1584 | struct key_parse parse; | 
|---|
| 1585 | struct nlattr *key; | 
|---|
| 1586 | struct cfg80211_cached_keys *result; | 
|---|
| 1587 | int rem, err, def = 0; | 
|---|
| 1588 | bool have_key = false; | 
|---|
| 1589 |  | 
|---|
| 1590 | nla_for_each_nested(key, keys, rem) { | 
|---|
| 1591 | have_key = true; | 
|---|
| 1592 | break; | 
|---|
| 1593 | } | 
|---|
| 1594 |  | 
|---|
| 1595 | if (!have_key) | 
|---|
| 1596 | return NULL; | 
|---|
| 1597 |  | 
|---|
| 1598 | result = kzalloc(sizeof(*result), GFP_KERNEL); | 
|---|
| 1599 | if (!result) | 
|---|
| 1600 | return ERR_PTR(error: -ENOMEM); | 
|---|
| 1601 |  | 
|---|
| 1602 | result->def = -1; | 
|---|
| 1603 |  | 
|---|
| 1604 | nla_for_each_nested(key, keys, rem) { | 
|---|
| 1605 | memset(s: &parse, c: 0, n: sizeof(parse)); | 
|---|
| 1606 | parse.idx = -1; | 
|---|
| 1607 |  | 
|---|
| 1608 | err = nl80211_parse_key_new(info, key, k: &parse); | 
|---|
| 1609 | if (err) | 
|---|
| 1610 | goto error; | 
|---|
| 1611 | err = -EINVAL; | 
|---|
| 1612 | if (!parse.p.key) | 
|---|
| 1613 | goto error; | 
|---|
| 1614 | if (parse.idx < 0 || parse.idx > 3) { | 
|---|
| 1615 | GENL_SET_ERR_MSG(info, "key index out of range [0-3]"); | 
|---|
| 1616 | goto error; | 
|---|
| 1617 | } | 
|---|
| 1618 | if (parse.def) { | 
|---|
| 1619 | if (def) { | 
|---|
| 1620 | GENL_SET_ERR_MSG(info, | 
|---|
| 1621 | "only one key can be default"); | 
|---|
| 1622 | goto error; | 
|---|
| 1623 | } | 
|---|
| 1624 | def = 1; | 
|---|
| 1625 | result->def = parse.idx; | 
|---|
| 1626 | if (!parse.def_uni || !parse.def_multi) | 
|---|
| 1627 | goto error; | 
|---|
| 1628 | } else if (parse.defmgmt) | 
|---|
| 1629 | goto error; | 
|---|
| 1630 | err = cfg80211_validate_key_settings(rdev, params: &parse.p, | 
|---|
| 1631 | key_idx: parse.idx, pairwise: false, NULL); | 
|---|
| 1632 | if (err) | 
|---|
| 1633 | goto error; | 
|---|
| 1634 | if (parse.p.cipher != WLAN_CIPHER_SUITE_WEP40 && | 
|---|
| 1635 | parse.p.cipher != WLAN_CIPHER_SUITE_WEP104) { | 
|---|
| 1636 | GENL_SET_ERR_MSG(info, "connect key must be WEP"); | 
|---|
| 1637 | err = -EINVAL; | 
|---|
| 1638 | goto error; | 
|---|
| 1639 | } | 
|---|
| 1640 | result->params[parse.idx].cipher = parse.p.cipher; | 
|---|
| 1641 | result->params[parse.idx].key_len = parse.p.key_len; | 
|---|
| 1642 | result->params[parse.idx].key = result->data[parse.idx]; | 
|---|
| 1643 | memcpy(to: result->data[parse.idx], from: parse.p.key, len: parse.p.key_len); | 
|---|
| 1644 |  | 
|---|
| 1645 | /* must be WEP key if we got here */ | 
|---|
| 1646 | if (no_ht) | 
|---|
| 1647 | *no_ht = true; | 
|---|
| 1648 | } | 
|---|
| 1649 |  | 
|---|
| 1650 | if (result->def < 0) { | 
|---|
| 1651 | err = -EINVAL; | 
|---|
| 1652 | GENL_SET_ERR_MSG(info, "need a default/TX key"); | 
|---|
| 1653 | goto error; | 
|---|
| 1654 | } | 
|---|
| 1655 |  | 
|---|
| 1656 | return result; | 
|---|
| 1657 | error: | 
|---|
| 1658 | kfree_sensitive(objp: result); | 
|---|
| 1659 | return ERR_PTR(error: err); | 
|---|
| 1660 | } | 
|---|
| 1661 |  | 
|---|
| 1662 | static int nl80211_key_allowed(struct wireless_dev *wdev) | 
|---|
| 1663 | { | 
|---|
| 1664 | lockdep_assert_wiphy(wdev->wiphy); | 
|---|
| 1665 |  | 
|---|
| 1666 | switch (wdev->iftype) { | 
|---|
| 1667 | case NL80211_IFTYPE_AP: | 
|---|
| 1668 | case NL80211_IFTYPE_AP_VLAN: | 
|---|
| 1669 | case NL80211_IFTYPE_P2P_GO: | 
|---|
| 1670 | case NL80211_IFTYPE_MESH_POINT: | 
|---|
| 1671 | break; | 
|---|
| 1672 | case NL80211_IFTYPE_ADHOC: | 
|---|
| 1673 | if (wdev->u.ibss.current_bss) | 
|---|
| 1674 | return 0; | 
|---|
| 1675 | return -ENOLINK; | 
|---|
| 1676 | case NL80211_IFTYPE_STATION: | 
|---|
| 1677 | case NL80211_IFTYPE_P2P_CLIENT: | 
|---|
| 1678 | if (wdev->connected) | 
|---|
| 1679 | return 0; | 
|---|
| 1680 | return -ENOLINK; | 
|---|
| 1681 | case NL80211_IFTYPE_NAN: | 
|---|
| 1682 | if (wiphy_ext_feature_isset(wiphy: wdev->wiphy, | 
|---|
| 1683 | ftidx: NL80211_EXT_FEATURE_SECURE_NAN)) | 
|---|
| 1684 | return 0; | 
|---|
| 1685 | return -EINVAL; | 
|---|
| 1686 | case NL80211_IFTYPE_UNSPECIFIED: | 
|---|
| 1687 | case NL80211_IFTYPE_OCB: | 
|---|
| 1688 | case NL80211_IFTYPE_MONITOR: | 
|---|
| 1689 | case NL80211_IFTYPE_P2P_DEVICE: | 
|---|
| 1690 | case NL80211_IFTYPE_WDS: | 
|---|
| 1691 | case NUM_NL80211_IFTYPES: | 
|---|
| 1692 | return -EINVAL; | 
|---|
| 1693 | } | 
|---|
| 1694 |  | 
|---|
| 1695 | return 0; | 
|---|
| 1696 | } | 
|---|
| 1697 |  | 
|---|
| 1698 | static struct ieee80211_channel *nl80211_get_valid_chan(struct wiphy *wiphy, | 
|---|
| 1699 | u32 freq) | 
|---|
| 1700 | { | 
|---|
| 1701 | struct ieee80211_channel *chan; | 
|---|
| 1702 |  | 
|---|
| 1703 | chan = ieee80211_get_channel_khz(wiphy, freq); | 
|---|
| 1704 | if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) | 
|---|
| 1705 | return NULL; | 
|---|
| 1706 | return chan; | 
|---|
| 1707 | } | 
|---|
| 1708 |  | 
|---|
| 1709 | static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes) | 
|---|
| 1710 | { | 
|---|
| 1711 | struct nlattr *nl_modes = nla_nest_start_noflag(skb: msg, attrtype: attr); | 
|---|
| 1712 | int i; | 
|---|
| 1713 |  | 
|---|
| 1714 | if (!nl_modes) | 
|---|
| 1715 | goto nla_put_failure; | 
|---|
| 1716 |  | 
|---|
| 1717 | i = 0; | 
|---|
| 1718 | while (ifmodes) { | 
|---|
| 1719 | if ((ifmodes & 1) && nla_put_flag(skb: msg, attrtype: i)) | 
|---|
| 1720 | goto nla_put_failure; | 
|---|
| 1721 | ifmodes >>= 1; | 
|---|
| 1722 | i++; | 
|---|
| 1723 | } | 
|---|
| 1724 |  | 
|---|
| 1725 | nla_nest_end(skb: msg, start: nl_modes); | 
|---|
| 1726 | return 0; | 
|---|
| 1727 |  | 
|---|
| 1728 | nla_put_failure: | 
|---|
| 1729 | return -ENOBUFS; | 
|---|
| 1730 | } | 
|---|
| 1731 |  | 
|---|
| 1732 | static int nl80211_put_ifcomb_data(struct sk_buff *msg, bool large, int idx, | 
|---|
| 1733 | const struct ieee80211_iface_combination *c, | 
|---|
| 1734 | u16 nested) | 
|---|
| 1735 | { | 
|---|
| 1736 | struct nlattr *nl_combi, *nl_limits; | 
|---|
| 1737 | int i; | 
|---|
| 1738 |  | 
|---|
| 1739 | nl_combi = nla_nest_start_noflag(skb: msg, attrtype: idx | nested); | 
|---|
| 1740 | if (!nl_combi) | 
|---|
| 1741 | goto nla_put_failure; | 
|---|
| 1742 |  | 
|---|
| 1743 | nl_limits = nla_nest_start_noflag(skb: msg, attrtype: NL80211_IFACE_COMB_LIMITS | | 
|---|
| 1744 | nested); | 
|---|
| 1745 | if (!nl_limits) | 
|---|
| 1746 | goto nla_put_failure; | 
|---|
| 1747 |  | 
|---|
| 1748 | for (i = 0; i < c->n_limits; i++) { | 
|---|
| 1749 | struct nlattr *nl_limit; | 
|---|
| 1750 |  | 
|---|
| 1751 | nl_limit = nla_nest_start_noflag(skb: msg, attrtype: i + 1); | 
|---|
| 1752 | if (!nl_limit) | 
|---|
| 1753 | goto nla_put_failure; | 
|---|
| 1754 | if (nla_put_u32(skb: msg, attrtype: NL80211_IFACE_LIMIT_MAX, value: c->limits[i].max)) | 
|---|
| 1755 | goto nla_put_failure; | 
|---|
| 1756 | if (nl80211_put_iftypes(msg, attr: NL80211_IFACE_LIMIT_TYPES, | 
|---|
| 1757 | ifmodes: c->limits[i].types)) | 
|---|
| 1758 | goto nla_put_failure; | 
|---|
| 1759 | nla_nest_end(skb: msg, start: nl_limit); | 
|---|
| 1760 | } | 
|---|
| 1761 |  | 
|---|
| 1762 | nla_nest_end(skb: msg, start: nl_limits); | 
|---|
| 1763 |  | 
|---|
| 1764 | if (c->beacon_int_infra_match && | 
|---|
| 1765 | nla_put_flag(skb: msg, attrtype: NL80211_IFACE_COMB_STA_AP_BI_MATCH)) | 
|---|
| 1766 | goto nla_put_failure; | 
|---|
| 1767 | if (nla_put_u32(skb: msg, attrtype: NL80211_IFACE_COMB_NUM_CHANNELS, | 
|---|
| 1768 | value: c->num_different_channels) || | 
|---|
| 1769 | nla_put_u32(skb: msg, attrtype: NL80211_IFACE_COMB_MAXNUM, | 
|---|
| 1770 | value: c->max_interfaces)) | 
|---|
| 1771 | goto nla_put_failure; | 
|---|
| 1772 | if (large && | 
|---|
| 1773 | (nla_put_u32(skb: msg, attrtype: NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, | 
|---|
| 1774 | value: c->radar_detect_widths) || | 
|---|
| 1775 | nla_put_u32(skb: msg, attrtype: NL80211_IFACE_COMB_RADAR_DETECT_REGIONS, | 
|---|
| 1776 | value: c->radar_detect_regions))) | 
|---|
| 1777 | goto nla_put_failure; | 
|---|
| 1778 | if (c->beacon_int_min_gcd && | 
|---|
| 1779 | nla_put_u32(skb: msg, attrtype: NL80211_IFACE_COMB_BI_MIN_GCD, | 
|---|
| 1780 | value: c->beacon_int_min_gcd)) | 
|---|
| 1781 | goto nla_put_failure; | 
|---|
| 1782 |  | 
|---|
| 1783 | nla_nest_end(skb: msg, start: nl_combi); | 
|---|
| 1784 |  | 
|---|
| 1785 | return 0; | 
|---|
| 1786 | nla_put_failure: | 
|---|
| 1787 | return -ENOBUFS; | 
|---|
| 1788 | } | 
|---|
| 1789 |  | 
|---|
| 1790 | static int nl80211_put_iface_combinations(struct wiphy *wiphy, | 
|---|
| 1791 | struct sk_buff *msg, | 
|---|
| 1792 | int attr, int radio, | 
|---|
| 1793 | bool large, u16 nested) | 
|---|
| 1794 | { | 
|---|
| 1795 | const struct ieee80211_iface_combination *c; | 
|---|
| 1796 | struct nlattr *nl_combis; | 
|---|
| 1797 | int i, n; | 
|---|
| 1798 |  | 
|---|
| 1799 | nl_combis = nla_nest_start_noflag(skb: msg, attrtype: attr | nested); | 
|---|
| 1800 | if (!nl_combis) | 
|---|
| 1801 | goto nla_put_failure; | 
|---|
| 1802 |  | 
|---|
| 1803 | if (radio >= 0) { | 
|---|
| 1804 | c = wiphy->radio[0].iface_combinations; | 
|---|
| 1805 | n = wiphy->radio[0].n_iface_combinations; | 
|---|
| 1806 | } else { | 
|---|
| 1807 | c = wiphy->iface_combinations; | 
|---|
| 1808 | n = wiphy->n_iface_combinations; | 
|---|
| 1809 | } | 
|---|
| 1810 | for (i = 0; i < n; i++) | 
|---|
| 1811 | if (nl80211_put_ifcomb_data(msg, large, idx: i + 1, c: &c[i], nested)) | 
|---|
| 1812 | goto nla_put_failure; | 
|---|
| 1813 |  | 
|---|
| 1814 | nla_nest_end(skb: msg, start: nl_combis); | 
|---|
| 1815 |  | 
|---|
| 1816 | return 0; | 
|---|
| 1817 | nla_put_failure: | 
|---|
| 1818 | return -ENOBUFS; | 
|---|
| 1819 | } | 
|---|
| 1820 |  | 
|---|
| 1821 | #ifdef CONFIG_PM | 
|---|
| 1822 | static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev, | 
|---|
| 1823 | struct sk_buff *msg) | 
|---|
| 1824 | { | 
|---|
| 1825 | const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan->tcp; | 
|---|
| 1826 | struct nlattr *nl_tcp; | 
|---|
| 1827 |  | 
|---|
| 1828 | if (!tcp) | 
|---|
| 1829 | return 0; | 
|---|
| 1830 |  | 
|---|
| 1831 | nl_tcp = nla_nest_start_noflag(skb: msg, | 
|---|
| 1832 | attrtype: NL80211_WOWLAN_TRIG_TCP_CONNECTION); | 
|---|
| 1833 | if (!nl_tcp) | 
|---|
| 1834 | return -ENOBUFS; | 
|---|
| 1835 |  | 
|---|
| 1836 | if (nla_put_u32(skb: msg, attrtype: NL80211_WOWLAN_TCP_DATA_PAYLOAD, | 
|---|
| 1837 | value: tcp->data_payload_max)) | 
|---|
| 1838 | return -ENOBUFS; | 
|---|
| 1839 |  | 
|---|
| 1840 | if (nla_put_u32(skb: msg, attrtype: NL80211_WOWLAN_TCP_DATA_PAYLOAD, | 
|---|
| 1841 | value: tcp->data_payload_max)) | 
|---|
| 1842 | return -ENOBUFS; | 
|---|
| 1843 |  | 
|---|
| 1844 | if (tcp->seq && nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ)) | 
|---|
| 1845 | return -ENOBUFS; | 
|---|
| 1846 |  | 
|---|
| 1847 | if (tcp->tok && nla_put(skb: msg, attrtype: NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, | 
|---|
| 1848 | attrlen: sizeof(*tcp->tok), data: tcp->tok)) | 
|---|
| 1849 | return -ENOBUFS; | 
|---|
| 1850 |  | 
|---|
| 1851 | if (nla_put_u32(skb: msg, attrtype: NL80211_WOWLAN_TCP_DATA_INTERVAL, | 
|---|
| 1852 | value: tcp->data_interval_max)) | 
|---|
| 1853 | return -ENOBUFS; | 
|---|
| 1854 |  | 
|---|
| 1855 | if (nla_put_u32(skb: msg, attrtype: NL80211_WOWLAN_TCP_WAKE_PAYLOAD, | 
|---|
| 1856 | value: tcp->wake_payload_max)) | 
|---|
| 1857 | return -ENOBUFS; | 
|---|
| 1858 |  | 
|---|
| 1859 | nla_nest_end(skb: msg, start: nl_tcp); | 
|---|
| 1860 | return 0; | 
|---|
| 1861 | } | 
|---|
| 1862 |  | 
|---|
| 1863 | static int nl80211_send_wowlan(struct sk_buff *msg, | 
|---|
| 1864 | struct cfg80211_registered_device *rdev, | 
|---|
| 1865 | bool large) | 
|---|
| 1866 | { | 
|---|
| 1867 | struct nlattr *nl_wowlan; | 
|---|
| 1868 |  | 
|---|
| 1869 | if (!rdev->wiphy.wowlan) | 
|---|
| 1870 | return 0; | 
|---|
| 1871 |  | 
|---|
| 1872 | nl_wowlan = nla_nest_start_noflag(skb: msg, | 
|---|
| 1873 | attrtype: NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED); | 
|---|
| 1874 | if (!nl_wowlan) | 
|---|
| 1875 | return -ENOBUFS; | 
|---|
| 1876 |  | 
|---|
| 1877 | if (((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_ANY) && | 
|---|
| 1878 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_ANY)) || | 
|---|
| 1879 | ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_DISCONNECT) && | 
|---|
| 1880 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_DISCONNECT)) || | 
|---|
| 1881 | ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT) && | 
|---|
| 1882 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_MAGIC_PKT)) || | 
|---|
| 1883 | ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) && | 
|---|
| 1884 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) || | 
|---|
| 1885 | ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && | 
|---|
| 1886 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) || | 
|---|
| 1887 | ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) && | 
|---|
| 1888 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) || | 
|---|
| 1889 | ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) && | 
|---|
| 1890 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) || | 
|---|
| 1891 | ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE) && | 
|---|
| 1892 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) | 
|---|
| 1893 | return -ENOBUFS; | 
|---|
| 1894 |  | 
|---|
| 1895 | if (rdev->wiphy.wowlan->n_patterns) { | 
|---|
| 1896 | struct nl80211_pattern_support pat = { | 
|---|
| 1897 | .max_patterns = rdev->wiphy.wowlan->n_patterns, | 
|---|
| 1898 | .min_pattern_len = rdev->wiphy.wowlan->pattern_min_len, | 
|---|
| 1899 | .max_pattern_len = rdev->wiphy.wowlan->pattern_max_len, | 
|---|
| 1900 | .max_pkt_offset = rdev->wiphy.wowlan->max_pkt_offset, | 
|---|
| 1901 | }; | 
|---|
| 1902 |  | 
|---|
| 1903 | if (nla_put(skb: msg, attrtype: NL80211_WOWLAN_TRIG_PKT_PATTERN, | 
|---|
| 1904 | attrlen: sizeof(pat), data: &pat)) | 
|---|
| 1905 | return -ENOBUFS; | 
|---|
| 1906 | } | 
|---|
| 1907 |  | 
|---|
| 1908 | if ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_NET_DETECT) && | 
|---|
| 1909 | nla_put_u32(skb: msg, attrtype: NL80211_WOWLAN_TRIG_NET_DETECT, | 
|---|
| 1910 | value: rdev->wiphy.wowlan->max_nd_match_sets)) | 
|---|
| 1911 | return -ENOBUFS; | 
|---|
| 1912 |  | 
|---|
| 1913 | if (large && nl80211_send_wowlan_tcp_caps(rdev, msg)) | 
|---|
| 1914 | return -ENOBUFS; | 
|---|
| 1915 |  | 
|---|
| 1916 | nla_nest_end(skb: msg, start: nl_wowlan); | 
|---|
| 1917 |  | 
|---|
| 1918 | return 0; | 
|---|
| 1919 | } | 
|---|
| 1920 | #endif | 
|---|
| 1921 |  | 
|---|
| 1922 | static int nl80211_send_coalesce(struct sk_buff *msg, | 
|---|
| 1923 | struct cfg80211_registered_device *rdev) | 
|---|
| 1924 | { | 
|---|
| 1925 | struct nl80211_coalesce_rule_support rule; | 
|---|
| 1926 |  | 
|---|
| 1927 | if (!rdev->wiphy.coalesce) | 
|---|
| 1928 | return 0; | 
|---|
| 1929 |  | 
|---|
| 1930 | rule.max_rules = rdev->wiphy.coalesce->n_rules; | 
|---|
| 1931 | rule.max_delay = rdev->wiphy.coalesce->max_delay; | 
|---|
| 1932 | rule.pat.max_patterns = rdev->wiphy.coalesce->n_patterns; | 
|---|
| 1933 | rule.pat.min_pattern_len = rdev->wiphy.coalesce->pattern_min_len; | 
|---|
| 1934 | rule.pat.max_pattern_len = rdev->wiphy.coalesce->pattern_max_len; | 
|---|
| 1935 | rule.pat.max_pkt_offset = rdev->wiphy.coalesce->max_pkt_offset; | 
|---|
| 1936 |  | 
|---|
| 1937 | if (nla_put(skb: msg, attrtype: NL80211_ATTR_COALESCE_RULE, attrlen: sizeof(rule), data: &rule)) | 
|---|
| 1938 | return -ENOBUFS; | 
|---|
| 1939 |  | 
|---|
| 1940 | return 0; | 
|---|
| 1941 | } | 
|---|
| 1942 |  | 
|---|
| 1943 | static int | 
|---|
| 1944 | nl80211_send_iftype_data(struct sk_buff *msg, | 
|---|
| 1945 | const struct ieee80211_supported_band *sband, | 
|---|
| 1946 | const struct ieee80211_sband_iftype_data *iftdata) | 
|---|
| 1947 | { | 
|---|
| 1948 | const struct ieee80211_sta_he_cap *he_cap = &iftdata->he_cap; | 
|---|
| 1949 | const struct ieee80211_sta_eht_cap *eht_cap = &iftdata->eht_cap; | 
|---|
| 1950 |  | 
|---|
| 1951 | if (nl80211_put_iftypes(msg, attr: NL80211_BAND_IFTYPE_ATTR_IFTYPES, | 
|---|
| 1952 | ifmodes: iftdata->types_mask)) | 
|---|
| 1953 | return -ENOBUFS; | 
|---|
| 1954 |  | 
|---|
| 1955 | if (he_cap->has_he) { | 
|---|
| 1956 | if (nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC, | 
|---|
| 1957 | attrlen: sizeof(he_cap->he_cap_elem.mac_cap_info), | 
|---|
| 1958 | data: he_cap->he_cap_elem.mac_cap_info) || | 
|---|
| 1959 | nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY, | 
|---|
| 1960 | attrlen: sizeof(he_cap->he_cap_elem.phy_cap_info), | 
|---|
| 1961 | data: he_cap->he_cap_elem.phy_cap_info) || | 
|---|
| 1962 | nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET, | 
|---|
| 1963 | attrlen: sizeof(he_cap->he_mcs_nss_supp), | 
|---|
| 1964 | data: &he_cap->he_mcs_nss_supp) || | 
|---|
| 1965 | nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE, | 
|---|
| 1966 | attrlen: sizeof(he_cap->ppe_thres), data: he_cap->ppe_thres)) | 
|---|
| 1967 | return -ENOBUFS; | 
|---|
| 1968 | } | 
|---|
| 1969 |  | 
|---|
| 1970 | if (eht_cap->has_eht && he_cap->has_he) { | 
|---|
| 1971 | u8 mcs_nss_size, ppe_thresh_size; | 
|---|
| 1972 | u16 ppe_thres_hdr; | 
|---|
| 1973 | bool is_ap; | 
|---|
| 1974 |  | 
|---|
| 1975 | is_ap = iftdata->types_mask & BIT(NL80211_IFTYPE_AP) || | 
|---|
| 1976 | iftdata->types_mask & BIT(NL80211_IFTYPE_P2P_GO); | 
|---|
| 1977 |  | 
|---|
| 1978 | mcs_nss_size = | 
|---|
| 1979 | ieee80211_eht_mcs_nss_size(he_cap: &he_cap->he_cap_elem, | 
|---|
| 1980 | eht_cap: &eht_cap->eht_cap_elem, | 
|---|
| 1981 | from_ap: is_ap); | 
|---|
| 1982 |  | 
|---|
| 1983 | ppe_thres_hdr = get_unaligned_le16(p: &eht_cap->eht_ppe_thres[0]); | 
|---|
| 1984 | ppe_thresh_size = | 
|---|
| 1985 | ieee80211_eht_ppe_size(ppe_thres_hdr, | 
|---|
| 1986 | phy_cap_info: eht_cap->eht_cap_elem.phy_cap_info); | 
|---|
| 1987 |  | 
|---|
| 1988 | if (nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC, | 
|---|
| 1989 | attrlen: sizeof(eht_cap->eht_cap_elem.mac_cap_info), | 
|---|
| 1990 | data: eht_cap->eht_cap_elem.mac_cap_info) || | 
|---|
| 1991 | nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY, | 
|---|
| 1992 | attrlen: sizeof(eht_cap->eht_cap_elem.phy_cap_info), | 
|---|
| 1993 | data: eht_cap->eht_cap_elem.phy_cap_info) || | 
|---|
| 1994 | nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET, | 
|---|
| 1995 | attrlen: mcs_nss_size, data: &eht_cap->eht_mcs_nss_supp) || | 
|---|
| 1996 | nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE, | 
|---|
| 1997 | attrlen: ppe_thresh_size, data: eht_cap->eht_ppe_thres)) | 
|---|
| 1998 | return -ENOBUFS; | 
|---|
| 1999 | } | 
|---|
| 2000 |  | 
|---|
| 2001 | if (sband->band == NL80211_BAND_6GHZ && | 
|---|
| 2002 | nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA, | 
|---|
| 2003 | attrlen: sizeof(iftdata->he_6ghz_capa), | 
|---|
| 2004 | data: &iftdata->he_6ghz_capa)) | 
|---|
| 2005 | return -ENOBUFS; | 
|---|
| 2006 |  | 
|---|
| 2007 | if (iftdata->vendor_elems.data && iftdata->vendor_elems.len && | 
|---|
| 2008 | nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS, | 
|---|
| 2009 | attrlen: iftdata->vendor_elems.len, data: iftdata->vendor_elems.data)) | 
|---|
| 2010 | return -ENOBUFS; | 
|---|
| 2011 |  | 
|---|
| 2012 | return 0; | 
|---|
| 2013 | } | 
|---|
| 2014 |  | 
|---|
| 2015 | static int nl80211_send_band_rateinfo(struct sk_buff *msg, | 
|---|
| 2016 | struct ieee80211_supported_band *sband, | 
|---|
| 2017 | bool large) | 
|---|
| 2018 | { | 
|---|
| 2019 | struct nlattr *nl_rates, *nl_rate; | 
|---|
| 2020 | struct ieee80211_rate *rate; | 
|---|
| 2021 | int i; | 
|---|
| 2022 |  | 
|---|
| 2023 | /* add HT info */ | 
|---|
| 2024 | if (sband->ht_cap.ht_supported && | 
|---|
| 2025 | (nla_put(skb: msg, attrtype: NL80211_BAND_ATTR_HT_MCS_SET, | 
|---|
| 2026 | attrlen: sizeof(sband->ht_cap.mcs), | 
|---|
| 2027 | data: &sband->ht_cap.mcs) || | 
|---|
| 2028 | nla_put_u16(skb: msg, NL80211_BAND_ATTR_HT_CAPA, | 
|---|
| 2029 | value: sband->ht_cap.cap) || | 
|---|
| 2030 | nla_put_u8(skb: msg, attrtype: NL80211_BAND_ATTR_HT_AMPDU_FACTOR, | 
|---|
| 2031 | value: sband->ht_cap.ampdu_factor) || | 
|---|
| 2032 | nla_put_u8(skb: msg, attrtype: NL80211_BAND_ATTR_HT_AMPDU_DENSITY, | 
|---|
| 2033 | value: sband->ht_cap.ampdu_density))) | 
|---|
| 2034 | return -ENOBUFS; | 
|---|
| 2035 |  | 
|---|
| 2036 | /* add VHT info */ | 
|---|
| 2037 | if (sband->vht_cap.vht_supported && | 
|---|
| 2038 | (nla_put(skb: msg, attrtype: NL80211_BAND_ATTR_VHT_MCS_SET, | 
|---|
| 2039 | attrlen: sizeof(sband->vht_cap.vht_mcs), | 
|---|
| 2040 | data: &sband->vht_cap.vht_mcs) || | 
|---|
| 2041 | nla_put_u32(skb: msg, attrtype: NL80211_BAND_ATTR_VHT_CAPA, | 
|---|
| 2042 | value: sband->vht_cap.cap))) | 
|---|
| 2043 | return -ENOBUFS; | 
|---|
| 2044 |  | 
|---|
| 2045 | if (large && sband->n_iftype_data) { | 
|---|
| 2046 | struct nlattr *nl_iftype_data = | 
|---|
| 2047 | nla_nest_start_noflag(skb: msg, | 
|---|
| 2048 | attrtype: NL80211_BAND_ATTR_IFTYPE_DATA); | 
|---|
| 2049 | const struct ieee80211_sband_iftype_data *iftd; | 
|---|
| 2050 | int err; | 
|---|
| 2051 |  | 
|---|
| 2052 | if (!nl_iftype_data) | 
|---|
| 2053 | return -ENOBUFS; | 
|---|
| 2054 |  | 
|---|
| 2055 | for_each_sband_iftype_data(sband, i, iftd) { | 
|---|
| 2056 | struct nlattr *iftdata; | 
|---|
| 2057 |  | 
|---|
| 2058 | iftdata = nla_nest_start_noflag(skb: msg, attrtype: i + 1); | 
|---|
| 2059 | if (!iftdata) | 
|---|
| 2060 | return -ENOBUFS; | 
|---|
| 2061 |  | 
|---|
| 2062 | err = nl80211_send_iftype_data(msg, sband, iftdata: iftd); | 
|---|
| 2063 | if (err) | 
|---|
| 2064 | return err; | 
|---|
| 2065 |  | 
|---|
| 2066 | nla_nest_end(skb: msg, start: iftdata); | 
|---|
| 2067 | } | 
|---|
| 2068 |  | 
|---|
| 2069 | nla_nest_end(skb: msg, start: nl_iftype_data); | 
|---|
| 2070 | } | 
|---|
| 2071 |  | 
|---|
| 2072 | /* add EDMG info */ | 
|---|
| 2073 | if (large && sband->edmg_cap.channels && | 
|---|
| 2074 | (nla_put_u8(skb: msg, attrtype: NL80211_BAND_ATTR_EDMG_CHANNELS, | 
|---|
| 2075 | value: sband->edmg_cap.channels) || | 
|---|
| 2076 | nla_put_u8(skb: msg, attrtype: NL80211_BAND_ATTR_EDMG_BW_CONFIG, | 
|---|
| 2077 | value: sband->edmg_cap.bw_config))) | 
|---|
| 2078 |  | 
|---|
| 2079 | return -ENOBUFS; | 
|---|
| 2080 |  | 
|---|
| 2081 | /* add bitrates */ | 
|---|
| 2082 | nl_rates = nla_nest_start_noflag(skb: msg, attrtype: NL80211_BAND_ATTR_RATES); | 
|---|
| 2083 | if (!nl_rates) | 
|---|
| 2084 | return -ENOBUFS; | 
|---|
| 2085 |  | 
|---|
| 2086 | for (i = 0; i < sband->n_bitrates; i++) { | 
|---|
| 2087 | nl_rate = nla_nest_start_noflag(skb: msg, attrtype: i); | 
|---|
| 2088 | if (!nl_rate) | 
|---|
| 2089 | return -ENOBUFS; | 
|---|
| 2090 |  | 
|---|
| 2091 | rate = &sband->bitrates[i]; | 
|---|
| 2092 | if (nla_put_u32(skb: msg, attrtype: NL80211_BITRATE_ATTR_RATE, | 
|---|
| 2093 | value: rate->bitrate)) | 
|---|
| 2094 | return -ENOBUFS; | 
|---|
| 2095 | if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) && | 
|---|
| 2096 | nla_put_flag(skb: msg, | 
|---|
| 2097 | attrtype: NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE)) | 
|---|
| 2098 | return -ENOBUFS; | 
|---|
| 2099 |  | 
|---|
| 2100 | nla_nest_end(skb: msg, start: nl_rate); | 
|---|
| 2101 | } | 
|---|
| 2102 |  | 
|---|
| 2103 | nla_nest_end(skb: msg, start: nl_rates); | 
|---|
| 2104 |  | 
|---|
| 2105 | /* S1G capabilities */ | 
|---|
| 2106 | if (sband->band == NL80211_BAND_S1GHZ && sband->s1g_cap.s1g && | 
|---|
| 2107 | (nla_put(skb: msg, attrtype: NL80211_BAND_ATTR_S1G_CAPA, | 
|---|
| 2108 | attrlen: sizeof(sband->s1g_cap.cap), | 
|---|
| 2109 | data: sband->s1g_cap.cap) || | 
|---|
| 2110 | nla_put(skb: msg, attrtype: NL80211_BAND_ATTR_S1G_MCS_NSS_SET, | 
|---|
| 2111 | attrlen: sizeof(sband->s1g_cap.nss_mcs), | 
|---|
| 2112 | data: sband->s1g_cap.nss_mcs))) | 
|---|
| 2113 | return -ENOBUFS; | 
|---|
| 2114 |  | 
|---|
| 2115 | return 0; | 
|---|
| 2116 | } | 
|---|
| 2117 |  | 
|---|
| 2118 | static int | 
|---|
| 2119 | nl80211_send_mgmt_stypes(struct sk_buff *msg, | 
|---|
| 2120 | const struct ieee80211_txrx_stypes *mgmt_stypes) | 
|---|
| 2121 | { | 
|---|
| 2122 | u16 stypes; | 
|---|
| 2123 | struct nlattr *nl_ftypes, *nl_ifs; | 
|---|
| 2124 | enum nl80211_iftype ift; | 
|---|
| 2125 | int i; | 
|---|
| 2126 |  | 
|---|
| 2127 | if (!mgmt_stypes) | 
|---|
| 2128 | return 0; | 
|---|
| 2129 |  | 
|---|
| 2130 | nl_ifs = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_TX_FRAME_TYPES); | 
|---|
| 2131 | if (!nl_ifs) | 
|---|
| 2132 | return -ENOBUFS; | 
|---|
| 2133 |  | 
|---|
| 2134 | for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) { | 
|---|
| 2135 | nl_ftypes = nla_nest_start_noflag(skb: msg, attrtype: ift); | 
|---|
| 2136 | if (!nl_ftypes) | 
|---|
| 2137 | return -ENOBUFS; | 
|---|
| 2138 | i = 0; | 
|---|
| 2139 | stypes = mgmt_stypes[ift].tx; | 
|---|
| 2140 | while (stypes) { | 
|---|
| 2141 | if ((stypes & 1) && | 
|---|
| 2142 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_FRAME_TYPE, | 
|---|
| 2143 | value: (i << 4) | IEEE80211_FTYPE_MGMT)) | 
|---|
| 2144 | return -ENOBUFS; | 
|---|
| 2145 | stypes >>= 1; | 
|---|
| 2146 | i++; | 
|---|
| 2147 | } | 
|---|
| 2148 | nla_nest_end(skb: msg, start: nl_ftypes); | 
|---|
| 2149 | } | 
|---|
| 2150 |  | 
|---|
| 2151 | nla_nest_end(skb: msg, start: nl_ifs); | 
|---|
| 2152 |  | 
|---|
| 2153 | nl_ifs = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_RX_FRAME_TYPES); | 
|---|
| 2154 | if (!nl_ifs) | 
|---|
| 2155 | return -ENOBUFS; | 
|---|
| 2156 |  | 
|---|
| 2157 | for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) { | 
|---|
| 2158 | nl_ftypes = nla_nest_start_noflag(skb: msg, attrtype: ift); | 
|---|
| 2159 | if (!nl_ftypes) | 
|---|
| 2160 | return -ENOBUFS; | 
|---|
| 2161 | i = 0; | 
|---|
| 2162 | stypes = mgmt_stypes[ift].rx; | 
|---|
| 2163 | while (stypes) { | 
|---|
| 2164 | if ((stypes & 1) && | 
|---|
| 2165 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_FRAME_TYPE, | 
|---|
| 2166 | value: (i << 4) | IEEE80211_FTYPE_MGMT)) | 
|---|
| 2167 | return -ENOBUFS; | 
|---|
| 2168 | stypes >>= 1; | 
|---|
| 2169 | i++; | 
|---|
| 2170 | } | 
|---|
| 2171 | nla_nest_end(skb: msg, start: nl_ftypes); | 
|---|
| 2172 | } | 
|---|
| 2173 | nla_nest_end(skb: msg, start: nl_ifs); | 
|---|
| 2174 |  | 
|---|
| 2175 | return 0; | 
|---|
| 2176 | } | 
|---|
| 2177 |  | 
|---|
| 2178 | #define CMD(op, n)							\ | 
|---|
| 2179 | do {								\ | 
|---|
| 2180 | if (rdev->ops->op) {					\ | 
|---|
| 2181 | i++;						\ | 
|---|
| 2182 | if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) 	\ | 
|---|
| 2183 | goto nla_put_failure;			\ | 
|---|
| 2184 | }							\ | 
|---|
| 2185 | } while (0) | 
|---|
| 2186 |  | 
|---|
| 2187 | static int nl80211_add_commands_unsplit(struct cfg80211_registered_device *rdev, | 
|---|
| 2188 | struct sk_buff *msg) | 
|---|
| 2189 | { | 
|---|
| 2190 | int i = 0; | 
|---|
| 2191 |  | 
|---|
| 2192 | /* | 
|---|
| 2193 | * do *NOT* add anything into this function, new things need to be | 
|---|
| 2194 | * advertised only to new versions of userspace that can deal with | 
|---|
| 2195 | * the split (and they can't possibly care about new features... | 
|---|
| 2196 | */ | 
|---|
| 2197 | CMD(add_virtual_intf, NEW_INTERFACE); | 
|---|
| 2198 | CMD(change_virtual_intf, SET_INTERFACE); | 
|---|
| 2199 | CMD(add_key, NEW_KEY); | 
|---|
| 2200 | CMD(start_ap, START_AP); | 
|---|
| 2201 | CMD(add_station, NEW_STATION); | 
|---|
| 2202 | CMD(add_mpath, NEW_MPATH); | 
|---|
| 2203 | CMD(update_mesh_config, SET_MESH_CONFIG); | 
|---|
| 2204 | CMD(change_bss, SET_BSS); | 
|---|
| 2205 | CMD(auth, AUTHENTICATE); | 
|---|
| 2206 | CMD(assoc, ASSOCIATE); | 
|---|
| 2207 | CMD(deauth, DEAUTHENTICATE); | 
|---|
| 2208 | CMD(disassoc, DISASSOCIATE); | 
|---|
| 2209 | CMD(join_ibss, JOIN_IBSS); | 
|---|
| 2210 | CMD(join_mesh, JOIN_MESH); | 
|---|
| 2211 | CMD(set_pmksa, SET_PMKSA); | 
|---|
| 2212 | CMD(del_pmksa, DEL_PMKSA); | 
|---|
| 2213 | CMD(flush_pmksa, FLUSH_PMKSA); | 
|---|
| 2214 | if (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) | 
|---|
| 2215 | CMD(remain_on_channel, REMAIN_ON_CHANNEL); | 
|---|
| 2216 | CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); | 
|---|
| 2217 | CMD(mgmt_tx, FRAME); | 
|---|
| 2218 | CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL); | 
|---|
| 2219 | if (rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { | 
|---|
| 2220 | i++; | 
|---|
| 2221 | if (nla_put_u32(skb: msg, attrtype: i, value: NL80211_CMD_SET_WIPHY_NETNS)) | 
|---|
| 2222 | goto nla_put_failure; | 
|---|
| 2223 | } | 
|---|
| 2224 | if (rdev->ops->set_monitor_channel || rdev->ops->start_ap || | 
|---|
| 2225 | rdev->ops->join_mesh) { | 
|---|
| 2226 | i++; | 
|---|
| 2227 | if (nla_put_u32(skb: msg, attrtype: i, value: NL80211_CMD_SET_CHANNEL)) | 
|---|
| 2228 | goto nla_put_failure; | 
|---|
| 2229 | } | 
|---|
| 2230 | if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) { | 
|---|
| 2231 | CMD(tdls_mgmt, TDLS_MGMT); | 
|---|
| 2232 | CMD(tdls_oper, TDLS_OPER); | 
|---|
| 2233 | } | 
|---|
| 2234 | if (rdev->wiphy.max_sched_scan_reqs) | 
|---|
| 2235 | CMD(sched_scan_start, START_SCHED_SCAN); | 
|---|
| 2236 | CMD(probe_client, PROBE_CLIENT); | 
|---|
| 2237 | CMD(set_noack_map, SET_NOACK_MAP); | 
|---|
| 2238 | if (rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) { | 
|---|
| 2239 | i++; | 
|---|
| 2240 | if (nla_put_u32(skb: msg, attrtype: i, value: NL80211_CMD_REGISTER_BEACONS)) | 
|---|
| 2241 | goto nla_put_failure; | 
|---|
| 2242 | } | 
|---|
| 2243 | CMD(start_p2p_device, START_P2P_DEVICE); | 
|---|
| 2244 | CMD(set_mcast_rate, SET_MCAST_RATE); | 
|---|
| 2245 | #ifdef CONFIG_NL80211_TESTMODE | 
|---|
| 2246 | CMD(testmode_cmd, TESTMODE); | 
|---|
| 2247 | #endif | 
|---|
| 2248 |  | 
|---|
| 2249 | if (rdev->ops->connect || rdev->ops->auth) { | 
|---|
| 2250 | i++; | 
|---|
| 2251 | if (nla_put_u32(skb: msg, attrtype: i, NL80211_CMD_CONNECT)) | 
|---|
| 2252 | goto nla_put_failure; | 
|---|
| 2253 | } | 
|---|
| 2254 |  | 
|---|
| 2255 | if (rdev->ops->disconnect || rdev->ops->deauth) { | 
|---|
| 2256 | i++; | 
|---|
| 2257 | if (nla_put_u32(skb: msg, attrtype: i, value: NL80211_CMD_DISCONNECT)) | 
|---|
| 2258 | goto nla_put_failure; | 
|---|
| 2259 | } | 
|---|
| 2260 |  | 
|---|
| 2261 | return i; | 
|---|
| 2262 | nla_put_failure: | 
|---|
| 2263 | return -ENOBUFS; | 
|---|
| 2264 | } | 
|---|
| 2265 |  | 
|---|
| 2266 | static int | 
|---|
| 2267 | nl80211_send_pmsr_ftm_capa(const struct cfg80211_pmsr_capabilities *cap, | 
|---|
| 2268 | struct sk_buff *msg) | 
|---|
| 2269 | { | 
|---|
| 2270 | struct nlattr *ftm; | 
|---|
| 2271 |  | 
|---|
| 2272 | if (!cap->ftm.supported) | 
|---|
| 2273 | return 0; | 
|---|
| 2274 |  | 
|---|
| 2275 | ftm = nla_nest_start_noflag(skb: msg, attrtype: NL80211_PMSR_TYPE_FTM); | 
|---|
| 2276 | if (!ftm) | 
|---|
| 2277 | return -ENOBUFS; | 
|---|
| 2278 |  | 
|---|
| 2279 | if (cap->ftm.asap && nla_put_flag(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_ASAP)) | 
|---|
| 2280 | return -ENOBUFS; | 
|---|
| 2281 | if (cap->ftm.non_asap && | 
|---|
| 2282 | nla_put_flag(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP)) | 
|---|
| 2283 | return -ENOBUFS; | 
|---|
| 2284 | if (cap->ftm.request_lci && | 
|---|
| 2285 | nla_put_flag(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI)) | 
|---|
| 2286 | return -ENOBUFS; | 
|---|
| 2287 | if (cap->ftm.request_civicloc && | 
|---|
| 2288 | nla_put_flag(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC)) | 
|---|
| 2289 | return -ENOBUFS; | 
|---|
| 2290 | if (nla_put_u32(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES, | 
|---|
| 2291 | value: cap->ftm.preambles)) | 
|---|
| 2292 | return -ENOBUFS; | 
|---|
| 2293 | if (nla_put_u32(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS, | 
|---|
| 2294 | value: cap->ftm.bandwidths)) | 
|---|
| 2295 | return -ENOBUFS; | 
|---|
| 2296 | if (cap->ftm.max_bursts_exponent >= 0 && | 
|---|
| 2297 | nla_put_u32(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT, | 
|---|
| 2298 | value: cap->ftm.max_bursts_exponent)) | 
|---|
| 2299 | return -ENOBUFS; | 
|---|
| 2300 | if (cap->ftm.max_ftms_per_burst && | 
|---|
| 2301 | nla_put_u32(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST, | 
|---|
| 2302 | value: cap->ftm.max_ftms_per_burst)) | 
|---|
| 2303 | return -ENOBUFS; | 
|---|
| 2304 | if (cap->ftm.trigger_based && | 
|---|
| 2305 | nla_put_flag(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED)) | 
|---|
| 2306 | return -ENOBUFS; | 
|---|
| 2307 | if (cap->ftm.non_trigger_based && | 
|---|
| 2308 | nla_put_flag(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED)) | 
|---|
| 2309 | return -ENOBUFS; | 
|---|
| 2310 |  | 
|---|
| 2311 | nla_nest_end(skb: msg, start: ftm); | 
|---|
| 2312 | return 0; | 
|---|
| 2313 | } | 
|---|
| 2314 |  | 
|---|
| 2315 | static int nl80211_send_pmsr_capa(struct cfg80211_registered_device *rdev, | 
|---|
| 2316 | struct sk_buff *msg) | 
|---|
| 2317 | { | 
|---|
| 2318 | const struct cfg80211_pmsr_capabilities *cap = rdev->wiphy.pmsr_capa; | 
|---|
| 2319 | struct nlattr *pmsr, *caps; | 
|---|
| 2320 |  | 
|---|
| 2321 | if (!cap) | 
|---|
| 2322 | return 0; | 
|---|
| 2323 |  | 
|---|
| 2324 | /* | 
|---|
| 2325 | * we don't need to clean up anything here since the caller | 
|---|
| 2326 | * will genlmsg_cancel() if we fail | 
|---|
| 2327 | */ | 
|---|
| 2328 |  | 
|---|
| 2329 | pmsr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_PEER_MEASUREMENTS); | 
|---|
| 2330 | if (!pmsr) | 
|---|
| 2331 | return -ENOBUFS; | 
|---|
| 2332 |  | 
|---|
| 2333 | if (nla_put_u32(skb: msg, attrtype: NL80211_PMSR_ATTR_MAX_PEERS, value: cap->max_peers)) | 
|---|
| 2334 | return -ENOBUFS; | 
|---|
| 2335 |  | 
|---|
| 2336 | if (cap->report_ap_tsf && | 
|---|
| 2337 | nla_put_flag(skb: msg, attrtype: NL80211_PMSR_ATTR_REPORT_AP_TSF)) | 
|---|
| 2338 | return -ENOBUFS; | 
|---|
| 2339 |  | 
|---|
| 2340 | if (cap->randomize_mac_addr && | 
|---|
| 2341 | nla_put_flag(skb: msg, attrtype: NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR)) | 
|---|
| 2342 | return -ENOBUFS; | 
|---|
| 2343 |  | 
|---|
| 2344 | caps = nla_nest_start_noflag(skb: msg, attrtype: NL80211_PMSR_ATTR_TYPE_CAPA); | 
|---|
| 2345 | if (!caps) | 
|---|
| 2346 | return -ENOBUFS; | 
|---|
| 2347 |  | 
|---|
| 2348 | if (nl80211_send_pmsr_ftm_capa(cap, msg)) | 
|---|
| 2349 | return -ENOBUFS; | 
|---|
| 2350 |  | 
|---|
| 2351 | nla_nest_end(skb: msg, start: caps); | 
|---|
| 2352 | nla_nest_end(skb: msg, start: pmsr); | 
|---|
| 2353 |  | 
|---|
| 2354 | return 0; | 
|---|
| 2355 | } | 
|---|
| 2356 |  | 
|---|
| 2357 | static int | 
|---|
| 2358 | nl80211_put_iftype_akm_suites(struct cfg80211_registered_device *rdev, | 
|---|
| 2359 | struct sk_buff *msg) | 
|---|
| 2360 | { | 
|---|
| 2361 | int i; | 
|---|
| 2362 | struct nlattr *nested, *nested_akms; | 
|---|
| 2363 | const struct wiphy_iftype_akm_suites *iftype_akms; | 
|---|
| 2364 |  | 
|---|
| 2365 | if (!rdev->wiphy.num_iftype_akm_suites || | 
|---|
| 2366 | !rdev->wiphy.iftype_akm_suites) | 
|---|
| 2367 | return 0; | 
|---|
| 2368 |  | 
|---|
| 2369 | nested = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_IFTYPE_AKM_SUITES); | 
|---|
| 2370 | if (!nested) | 
|---|
| 2371 | return -ENOBUFS; | 
|---|
| 2372 |  | 
|---|
| 2373 | for (i = 0; i < rdev->wiphy.num_iftype_akm_suites; i++) { | 
|---|
| 2374 | nested_akms = nla_nest_start(skb: msg, attrtype: i + 1); | 
|---|
| 2375 | if (!nested_akms) | 
|---|
| 2376 | return -ENOBUFS; | 
|---|
| 2377 |  | 
|---|
| 2378 | iftype_akms = &rdev->wiphy.iftype_akm_suites[i]; | 
|---|
| 2379 |  | 
|---|
| 2380 | if (nl80211_put_iftypes(msg, attr: NL80211_IFTYPE_AKM_ATTR_IFTYPES, | 
|---|
| 2381 | ifmodes: iftype_akms->iftypes_mask)) | 
|---|
| 2382 | return -ENOBUFS; | 
|---|
| 2383 |  | 
|---|
| 2384 | if (nla_put(skb: msg, attrtype: NL80211_IFTYPE_AKM_ATTR_SUITES, | 
|---|
| 2385 | attrlen: sizeof(u32) * iftype_akms->n_akm_suites, | 
|---|
| 2386 | data: iftype_akms->akm_suites)) { | 
|---|
| 2387 | return -ENOBUFS; | 
|---|
| 2388 | } | 
|---|
| 2389 | nla_nest_end(skb: msg, start: nested_akms); | 
|---|
| 2390 | } | 
|---|
| 2391 |  | 
|---|
| 2392 | nla_nest_end(skb: msg, start: nested); | 
|---|
| 2393 |  | 
|---|
| 2394 | return 0; | 
|---|
| 2395 | } | 
|---|
| 2396 |  | 
|---|
| 2397 | static int | 
|---|
| 2398 | nl80211_put_tid_config_support(struct cfg80211_registered_device *rdev, | 
|---|
| 2399 | struct sk_buff *msg) | 
|---|
| 2400 | { | 
|---|
| 2401 | struct nlattr *supp; | 
|---|
| 2402 |  | 
|---|
| 2403 | if (!rdev->wiphy.tid_config_support.vif && | 
|---|
| 2404 | !rdev->wiphy.tid_config_support.peer) | 
|---|
| 2405 | return 0; | 
|---|
| 2406 |  | 
|---|
| 2407 | supp = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_TID_CONFIG); | 
|---|
| 2408 | if (!supp) | 
|---|
| 2409 | return -ENOSPC; | 
|---|
| 2410 |  | 
|---|
| 2411 | if (rdev->wiphy.tid_config_support.vif && | 
|---|
| 2412 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_TID_CONFIG_ATTR_VIF_SUPP, | 
|---|
| 2413 | value: rdev->wiphy.tid_config_support.vif, | 
|---|
| 2414 | padattr: NL80211_TID_CONFIG_ATTR_PAD)) | 
|---|
| 2415 | goto fail; | 
|---|
| 2416 |  | 
|---|
| 2417 | if (rdev->wiphy.tid_config_support.peer && | 
|---|
| 2418 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_TID_CONFIG_ATTR_PEER_SUPP, | 
|---|
| 2419 | value: rdev->wiphy.tid_config_support.peer, | 
|---|
| 2420 | padattr: NL80211_TID_CONFIG_ATTR_PAD)) | 
|---|
| 2421 | goto fail; | 
|---|
| 2422 |  | 
|---|
| 2423 | /* for now we just use the same value ... makes more sense */ | 
|---|
| 2424 | if (nla_put_u8(skb: msg, attrtype: NL80211_TID_CONFIG_ATTR_RETRY_SHORT, | 
|---|
| 2425 | value: rdev->wiphy.tid_config_support.max_retry)) | 
|---|
| 2426 | goto fail; | 
|---|
| 2427 | if (nla_put_u8(skb: msg, attrtype: NL80211_TID_CONFIG_ATTR_RETRY_LONG, | 
|---|
| 2428 | value: rdev->wiphy.tid_config_support.max_retry)) | 
|---|
| 2429 | goto fail; | 
|---|
| 2430 |  | 
|---|
| 2431 | nla_nest_end(skb: msg, start: supp); | 
|---|
| 2432 |  | 
|---|
| 2433 | return 0; | 
|---|
| 2434 | fail: | 
|---|
| 2435 | nla_nest_cancel(skb: msg, start: supp); | 
|---|
| 2436 | return -ENOBUFS; | 
|---|
| 2437 | } | 
|---|
| 2438 |  | 
|---|
| 2439 | static int | 
|---|
| 2440 | nl80211_put_sar_specs(struct cfg80211_registered_device *rdev, | 
|---|
| 2441 | struct sk_buff *msg) | 
|---|
| 2442 | { | 
|---|
| 2443 | struct nlattr *sar_capa, *specs, *sub_freq_range; | 
|---|
| 2444 | u8 num_freq_ranges; | 
|---|
| 2445 | int i; | 
|---|
| 2446 |  | 
|---|
| 2447 | if (!rdev->wiphy.sar_capa) | 
|---|
| 2448 | return 0; | 
|---|
| 2449 |  | 
|---|
| 2450 | num_freq_ranges = rdev->wiphy.sar_capa->num_freq_ranges; | 
|---|
| 2451 |  | 
|---|
| 2452 | sar_capa = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_SAR_SPEC); | 
|---|
| 2453 | if (!sar_capa) | 
|---|
| 2454 | return -ENOSPC; | 
|---|
| 2455 |  | 
|---|
| 2456 | if (nla_put_u32(skb: msg, attrtype: NL80211_SAR_ATTR_TYPE, value: rdev->wiphy.sar_capa->type)) | 
|---|
| 2457 | goto fail; | 
|---|
| 2458 |  | 
|---|
| 2459 | specs = nla_nest_start(skb: msg, attrtype: NL80211_SAR_ATTR_SPECS); | 
|---|
| 2460 | if (!specs) | 
|---|
| 2461 | goto fail; | 
|---|
| 2462 |  | 
|---|
| 2463 | /* report supported freq_ranges */ | 
|---|
| 2464 | for (i = 0; i < num_freq_ranges; i++) { | 
|---|
| 2465 | sub_freq_range = nla_nest_start(skb: msg, attrtype: i + 1); | 
|---|
| 2466 | if (!sub_freq_range) | 
|---|
| 2467 | goto fail; | 
|---|
| 2468 |  | 
|---|
| 2469 | if (nla_put_u32(skb: msg, attrtype: NL80211_SAR_ATTR_SPECS_START_FREQ, | 
|---|
| 2470 | value: rdev->wiphy.sar_capa->freq_ranges[i].start_freq)) | 
|---|
| 2471 | goto fail; | 
|---|
| 2472 |  | 
|---|
| 2473 | if (nla_put_u32(skb: msg, attrtype: NL80211_SAR_ATTR_SPECS_END_FREQ, | 
|---|
| 2474 | value: rdev->wiphy.sar_capa->freq_ranges[i].end_freq)) | 
|---|
| 2475 | goto fail; | 
|---|
| 2476 |  | 
|---|
| 2477 | nla_nest_end(skb: msg, start: sub_freq_range); | 
|---|
| 2478 | } | 
|---|
| 2479 |  | 
|---|
| 2480 | nla_nest_end(skb: msg, start: specs); | 
|---|
| 2481 | nla_nest_end(skb: msg, start: sar_capa); | 
|---|
| 2482 |  | 
|---|
| 2483 | return 0; | 
|---|
| 2484 | fail: | 
|---|
| 2485 | nla_nest_cancel(skb: msg, start: sar_capa); | 
|---|
| 2486 | return -ENOBUFS; | 
|---|
| 2487 | } | 
|---|
| 2488 |  | 
|---|
| 2489 | static int nl80211_put_mbssid_support(struct wiphy *wiphy, struct sk_buff *msg) | 
|---|
| 2490 | { | 
|---|
| 2491 | struct nlattr *config; | 
|---|
| 2492 |  | 
|---|
| 2493 | if (!wiphy->mbssid_max_interfaces) | 
|---|
| 2494 | return 0; | 
|---|
| 2495 |  | 
|---|
| 2496 | config = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_MBSSID_CONFIG); | 
|---|
| 2497 | if (!config) | 
|---|
| 2498 | return -ENOBUFS; | 
|---|
| 2499 |  | 
|---|
| 2500 | if (nla_put_u8(skb: msg, attrtype: NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES, | 
|---|
| 2501 | value: wiphy->mbssid_max_interfaces)) | 
|---|
| 2502 | goto fail; | 
|---|
| 2503 |  | 
|---|
| 2504 | if (wiphy->ema_max_profile_periodicity && | 
|---|
| 2505 | nla_put_u8(skb: msg, | 
|---|
| 2506 | attrtype: NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY, | 
|---|
| 2507 | value: wiphy->ema_max_profile_periodicity)) | 
|---|
| 2508 | goto fail; | 
|---|
| 2509 |  | 
|---|
| 2510 | nla_nest_end(skb: msg, start: config); | 
|---|
| 2511 | return 0; | 
|---|
| 2512 |  | 
|---|
| 2513 | fail: | 
|---|
| 2514 | nla_nest_cancel(skb: msg, start: config); | 
|---|
| 2515 | return -ENOBUFS; | 
|---|
| 2516 | } | 
|---|
| 2517 |  | 
|---|
| 2518 | static int nl80211_put_radio(struct wiphy *wiphy, struct sk_buff *msg, int idx) | 
|---|
| 2519 | { | 
|---|
| 2520 | const struct wiphy_radio *r = &wiphy->radio[idx]; | 
|---|
| 2521 | const struct wiphy_radio_cfg *rcfg = &wiphy->radio_cfg[idx]; | 
|---|
| 2522 | struct nlattr *radio, *freq; | 
|---|
| 2523 | int i; | 
|---|
| 2524 |  | 
|---|
| 2525 | radio = nla_nest_start(skb: msg, attrtype: idx); | 
|---|
| 2526 | if (!radio) | 
|---|
| 2527 | return -ENOBUFS; | 
|---|
| 2528 |  | 
|---|
| 2529 | if (nla_put_u32(skb: msg, attrtype: NL80211_WIPHY_RADIO_ATTR_INDEX, value: idx)) | 
|---|
| 2530 | goto nla_put_failure; | 
|---|
| 2531 |  | 
|---|
| 2532 | if (rcfg->rts_threshold && | 
|---|
| 2533 | nla_put_u32(skb: msg, attrtype: NL80211_WIPHY_RADIO_ATTR_RTS_THRESHOLD, | 
|---|
| 2534 | value: rcfg->rts_threshold)) | 
|---|
| 2535 | goto nla_put_failure; | 
|---|
| 2536 |  | 
|---|
| 2537 | if (r->antenna_mask && | 
|---|
| 2538 | nla_put_u32(skb: msg, attrtype: NL80211_WIPHY_RADIO_ATTR_ANTENNA_MASK, | 
|---|
| 2539 | value: r->antenna_mask)) | 
|---|
| 2540 | goto nla_put_failure; | 
|---|
| 2541 |  | 
|---|
| 2542 | for (i = 0; i < r->n_freq_range; i++) { | 
|---|
| 2543 | const struct wiphy_radio_freq_range *range = &r->freq_range[i]; | 
|---|
| 2544 |  | 
|---|
| 2545 | freq = nla_nest_start(skb: msg, attrtype: NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE); | 
|---|
| 2546 | if (!freq) | 
|---|
| 2547 | goto nla_put_failure; | 
|---|
| 2548 |  | 
|---|
| 2549 | if (nla_put_u32(skb: msg, attrtype: NL80211_WIPHY_RADIO_FREQ_ATTR_START, | 
|---|
| 2550 | value: range->start_freq) || | 
|---|
| 2551 | nla_put_u32(skb: msg, attrtype: NL80211_WIPHY_RADIO_FREQ_ATTR_END, | 
|---|
| 2552 | value: range->end_freq)) | 
|---|
| 2553 | goto nla_put_failure; | 
|---|
| 2554 |  | 
|---|
| 2555 | nla_nest_end(skb: msg, start: freq); | 
|---|
| 2556 | } | 
|---|
| 2557 |  | 
|---|
| 2558 | for (i = 0; i < r->n_iface_combinations; i++) | 
|---|
| 2559 | if (nl80211_put_ifcomb_data(msg, large: true, | 
|---|
| 2560 | idx: NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION, | 
|---|
| 2561 | c: &r->iface_combinations[i], | 
|---|
| 2562 | NLA_F_NESTED)) | 
|---|
| 2563 | goto nla_put_failure; | 
|---|
| 2564 |  | 
|---|
| 2565 | nla_nest_end(skb: msg, start: radio); | 
|---|
| 2566 |  | 
|---|
| 2567 | return 0; | 
|---|
| 2568 |  | 
|---|
| 2569 | nla_put_failure: | 
|---|
| 2570 | return -ENOBUFS; | 
|---|
| 2571 | } | 
|---|
| 2572 |  | 
|---|
| 2573 | static int nl80211_put_radios(struct wiphy *wiphy, struct sk_buff *msg) | 
|---|
| 2574 | { | 
|---|
| 2575 | struct nlattr *radios; | 
|---|
| 2576 | int i; | 
|---|
| 2577 |  | 
|---|
| 2578 | if (!wiphy->n_radio) | 
|---|
| 2579 | return 0; | 
|---|
| 2580 |  | 
|---|
| 2581 | radios = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_WIPHY_RADIOS); | 
|---|
| 2582 | if (!radios) | 
|---|
| 2583 | return -ENOBUFS; | 
|---|
| 2584 |  | 
|---|
| 2585 | for (i = 0; i < wiphy->n_radio; i++) | 
|---|
| 2586 | if (nl80211_put_radio(wiphy, msg, idx: i)) | 
|---|
| 2587 | goto fail; | 
|---|
| 2588 |  | 
|---|
| 2589 | nla_nest_end(skb: msg, start: radios); | 
|---|
| 2590 |  | 
|---|
| 2591 | if (nl80211_put_iface_combinations(wiphy, msg, | 
|---|
| 2592 | attr: NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS, | 
|---|
| 2593 | radio: -1, large: true, NLA_F_NESTED)) | 
|---|
| 2594 | return -ENOBUFS; | 
|---|
| 2595 |  | 
|---|
| 2596 | return 0; | 
|---|
| 2597 |  | 
|---|
| 2598 | fail: | 
|---|
| 2599 | nla_nest_cancel(skb: msg, start: radios); | 
|---|
| 2600 | return -ENOBUFS; | 
|---|
| 2601 | } | 
|---|
| 2602 |  | 
|---|
| 2603 | static int nl80211_put_nan_capa(struct wiphy *wiphy, struct sk_buff *msg) | 
|---|
| 2604 | { | 
|---|
| 2605 | struct nlattr *nan_caps; | 
|---|
| 2606 |  | 
|---|
| 2607 | nan_caps = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_NAN_CAPABILITIES); | 
|---|
| 2608 | if (!nan_caps) | 
|---|
| 2609 | return -ENOBUFS; | 
|---|
| 2610 |  | 
|---|
| 2611 | if (wiphy->nan_capa.flags & WIPHY_NAN_FLAGS_CONFIGURABLE_SYNC && | 
|---|
| 2612 | nla_put_flag(skb: msg, attrtype: NL80211_NAN_CAPA_CONFIGURABLE_SYNC)) | 
|---|
| 2613 | goto fail; | 
|---|
| 2614 |  | 
|---|
| 2615 | if ((wiphy->nan_capa.flags & WIPHY_NAN_FLAGS_USERSPACE_DE) && | 
|---|
| 2616 | nla_put_flag(skb: msg, attrtype: NL80211_NAN_CAPA_USERSPACE_DE)) | 
|---|
| 2617 | goto fail; | 
|---|
| 2618 |  | 
|---|
| 2619 | if (nla_put_u8(skb: msg, attrtype: NL80211_NAN_CAPA_OP_MODE, | 
|---|
| 2620 | value: wiphy->nan_capa.op_mode) || | 
|---|
| 2621 | nla_put_u8(skb: msg, attrtype: NL80211_NAN_CAPA_NUM_ANTENNAS, | 
|---|
| 2622 | value: wiphy->nan_capa.n_antennas) || | 
|---|
| 2623 | nla_put_u16(skb: msg, attrtype: NL80211_NAN_CAPA_MAX_CHANNEL_SWITCH_TIME, | 
|---|
| 2624 | value: wiphy->nan_capa.max_channel_switch_time) || | 
|---|
| 2625 | nla_put_u8(skb: msg, attrtype: NL80211_NAN_CAPA_CAPABILITIES, | 
|---|
| 2626 | value: wiphy->nan_capa.dev_capabilities)) | 
|---|
| 2627 | goto fail; | 
|---|
| 2628 |  | 
|---|
| 2629 | nla_nest_end(skb: msg, start: nan_caps); | 
|---|
| 2630 |  | 
|---|
| 2631 | return 0; | 
|---|
| 2632 |  | 
|---|
| 2633 | fail: | 
|---|
| 2634 | nla_nest_cancel(skb: msg, start: nan_caps); | 
|---|
| 2635 | return -ENOBUFS; | 
|---|
| 2636 | } | 
|---|
| 2637 |  | 
|---|
| 2638 | struct nl80211_dump_wiphy_state { | 
|---|
| 2639 | s64 filter_wiphy; | 
|---|
| 2640 | long start; | 
|---|
| 2641 | long split_start, band_start, chan_start, capa_start; | 
|---|
| 2642 | bool split; | 
|---|
| 2643 | }; | 
|---|
| 2644 |  | 
|---|
| 2645 | static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, | 
|---|
| 2646 | enum nl80211_commands cmd, | 
|---|
| 2647 | struct sk_buff *msg, u32 portid, u32 seq, | 
|---|
| 2648 | int flags, struct nl80211_dump_wiphy_state *state) | 
|---|
| 2649 | { | 
|---|
| 2650 | void *hdr; | 
|---|
| 2651 | struct nlattr *nl_bands, *nl_band; | 
|---|
| 2652 | struct nlattr *nl_freqs, *nl_freq; | 
|---|
| 2653 | struct nlattr *nl_cmds; | 
|---|
| 2654 | enum nl80211_band band; | 
|---|
| 2655 | struct ieee80211_channel *chan; | 
|---|
| 2656 | int i; | 
|---|
| 2657 | const struct ieee80211_txrx_stypes *mgmt_stypes = | 
|---|
| 2658 | rdev->wiphy.mgmt_stypes; | 
|---|
| 2659 | u32 features; | 
|---|
| 2660 |  | 
|---|
| 2661 | hdr = nl80211hdr_put(skb: msg, portid, seq, flags, cmd); | 
|---|
| 2662 | if (!hdr) | 
|---|
| 2663 | return -ENOBUFS; | 
|---|
| 2664 |  | 
|---|
| 2665 | if (WARN_ON(!state)) | 
|---|
| 2666 | return -EINVAL; | 
|---|
| 2667 |  | 
|---|
| 2668 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 2669 | nla_put_string(skb: msg, attrtype: NL80211_ATTR_WIPHY_NAME, | 
|---|
| 2670 | str: wiphy_name(wiphy: &rdev->wiphy)) || | 
|---|
| 2671 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_GENERATION, | 
|---|
| 2672 | value: cfg80211_rdev_list_generation)) | 
|---|
| 2673 | goto nla_put_failure; | 
|---|
| 2674 |  | 
|---|
| 2675 | if (cmd != NL80211_CMD_NEW_WIPHY) | 
|---|
| 2676 | goto finish; | 
|---|
| 2677 |  | 
|---|
| 2678 | switch (state->split_start) { | 
|---|
| 2679 | case 0: | 
|---|
| 2680 | if (nla_put_u8(skb: msg, attrtype: NL80211_ATTR_WIPHY_RETRY_SHORT, | 
|---|
| 2681 | value: rdev->wiphy.retry_short) || | 
|---|
| 2682 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_WIPHY_RETRY_LONG, | 
|---|
| 2683 | value: rdev->wiphy.retry_long) || | 
|---|
| 2684 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY_FRAG_THRESHOLD, | 
|---|
| 2685 | value: rdev->wiphy.frag_threshold) || | 
|---|
| 2686 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY_RTS_THRESHOLD, | 
|---|
| 2687 | value: rdev->wiphy.rts_threshold) || | 
|---|
| 2688 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_WIPHY_COVERAGE_CLASS, | 
|---|
| 2689 | value: rdev->wiphy.coverage_class) || | 
|---|
| 2690 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MAX_NUM_SCAN_SSIDS, | 
|---|
| 2691 | value: rdev->wiphy.max_scan_ssids) || | 
|---|
| 2692 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, | 
|---|
| 2693 | value: rdev->wiphy.max_sched_scan_ssids) || | 
|---|
| 2694 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_MAX_SCAN_IE_LEN, | 
|---|
| 2695 | value: rdev->wiphy.max_scan_ie_len) || | 
|---|
| 2696 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, | 
|---|
| 2697 | value: rdev->wiphy.max_sched_scan_ie_len) || | 
|---|
| 2698 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MAX_MATCH_SETS, | 
|---|
| 2699 | value: rdev->wiphy.max_match_sets)) | 
|---|
| 2700 | goto nla_put_failure; | 
|---|
| 2701 |  | 
|---|
| 2702 | if ((rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) && | 
|---|
| 2703 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_SUPPORT_IBSS_RSN)) | 
|---|
| 2704 | goto nla_put_failure; | 
|---|
| 2705 | if ((rdev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) && | 
|---|
| 2706 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_SUPPORT_MESH_AUTH)) | 
|---|
| 2707 | goto nla_put_failure; | 
|---|
| 2708 | if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) && | 
|---|
| 2709 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_SUPPORT_AP_UAPSD)) | 
|---|
| 2710 | goto nla_put_failure; | 
|---|
| 2711 | if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) && | 
|---|
| 2712 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_ROAM_SUPPORT)) | 
|---|
| 2713 | goto nla_put_failure; | 
|---|
| 2714 | if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) && | 
|---|
| 2715 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_TDLS_SUPPORT)) | 
|---|
| 2716 | goto nla_put_failure; | 
|---|
| 2717 | if ((rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) && | 
|---|
| 2718 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_TDLS_EXTERNAL_SETUP)) | 
|---|
| 2719 | goto nla_put_failure; | 
|---|
| 2720 | state->split_start++; | 
|---|
| 2721 | if (state->split) | 
|---|
| 2722 | break; | 
|---|
| 2723 | fallthrough; | 
|---|
| 2724 | case 1: | 
|---|
| 2725 | if (nla_put(skb: msg, attrtype: NL80211_ATTR_CIPHER_SUITES, | 
|---|
| 2726 | attrlen: sizeof(u32) * rdev->wiphy.n_cipher_suites, | 
|---|
| 2727 | data: rdev->wiphy.cipher_suites)) | 
|---|
| 2728 | goto nla_put_failure; | 
|---|
| 2729 |  | 
|---|
| 2730 | if (nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MAX_NUM_PMKIDS, | 
|---|
| 2731 | value: rdev->wiphy.max_num_pmkids)) | 
|---|
| 2732 | goto nla_put_failure; | 
|---|
| 2733 |  | 
|---|
| 2734 | if ((rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) && | 
|---|
| 2735 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_CONTROL_PORT_ETHERTYPE)) | 
|---|
| 2736 | goto nla_put_failure; | 
|---|
| 2737 |  | 
|---|
| 2738 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, | 
|---|
| 2739 | value: rdev->wiphy.available_antennas_tx) || | 
|---|
| 2740 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, | 
|---|
| 2741 | value: rdev->wiphy.available_antennas_rx)) | 
|---|
| 2742 | goto nla_put_failure; | 
|---|
| 2743 |  | 
|---|
| 2744 | if ((rdev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) && | 
|---|
| 2745 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_PROBE_RESP_OFFLOAD, | 
|---|
| 2746 | value: rdev->wiphy.probe_resp_offload)) | 
|---|
| 2747 | goto nla_put_failure; | 
|---|
| 2748 |  | 
|---|
| 2749 | if ((rdev->wiphy.available_antennas_tx || | 
|---|
| 2750 | rdev->wiphy.available_antennas_rx) && | 
|---|
| 2751 | rdev->ops->get_antenna) { | 
|---|
| 2752 | u32 tx_ant = 0, rx_ant = 0; | 
|---|
| 2753 | int res; | 
|---|
| 2754 |  | 
|---|
| 2755 | res = rdev_get_antenna(rdev, radio_idx: -1, tx_ant: &tx_ant, rx_ant: &rx_ant); | 
|---|
| 2756 | if (!res) { | 
|---|
| 2757 | if (nla_put_u32(skb: msg, | 
|---|
| 2758 | attrtype: NL80211_ATTR_WIPHY_ANTENNA_TX, | 
|---|
| 2759 | value: tx_ant) || | 
|---|
| 2760 | nla_put_u32(skb: msg, | 
|---|
| 2761 | attrtype: NL80211_ATTR_WIPHY_ANTENNA_RX, | 
|---|
| 2762 | value: rx_ant)) | 
|---|
| 2763 | goto nla_put_failure; | 
|---|
| 2764 | } | 
|---|
| 2765 | } | 
|---|
| 2766 |  | 
|---|
| 2767 | state->split_start++; | 
|---|
| 2768 | if (state->split) | 
|---|
| 2769 | break; | 
|---|
| 2770 | fallthrough; | 
|---|
| 2771 | case 2: | 
|---|
| 2772 | if (nl80211_put_iftypes(msg, attr: NL80211_ATTR_SUPPORTED_IFTYPES, | 
|---|
| 2773 | ifmodes: rdev->wiphy.interface_modes)) | 
|---|
| 2774 | goto nla_put_failure; | 
|---|
| 2775 | state->split_start++; | 
|---|
| 2776 | if (state->split) | 
|---|
| 2777 | break; | 
|---|
| 2778 | fallthrough; | 
|---|
| 2779 | case 3: | 
|---|
| 2780 | nl_bands = nla_nest_start_noflag(skb: msg, | 
|---|
| 2781 | attrtype: NL80211_ATTR_WIPHY_BANDS); | 
|---|
| 2782 | if (!nl_bands) | 
|---|
| 2783 | goto nla_put_failure; | 
|---|
| 2784 |  | 
|---|
| 2785 | for (band = state->band_start; | 
|---|
| 2786 | band < (state->split ? | 
|---|
| 2787 | NUM_NL80211_BANDS : | 
|---|
| 2788 | NL80211_BAND_60GHZ + 1); | 
|---|
| 2789 | band++) { | 
|---|
| 2790 | struct ieee80211_supported_band *sband; | 
|---|
| 2791 |  | 
|---|
| 2792 | /* omit higher bands for ancient software */ | 
|---|
| 2793 | if (band > NL80211_BAND_5GHZ && !state->split) | 
|---|
| 2794 | break; | 
|---|
| 2795 |  | 
|---|
| 2796 | sband = rdev->wiphy.bands[band]; | 
|---|
| 2797 |  | 
|---|
| 2798 | if (!sband) | 
|---|
| 2799 | continue; | 
|---|
| 2800 |  | 
|---|
| 2801 | nl_band = nla_nest_start_noflag(skb: msg, attrtype: band); | 
|---|
| 2802 | if (!nl_band) | 
|---|
| 2803 | goto nla_put_failure; | 
|---|
| 2804 |  | 
|---|
| 2805 | switch (state->chan_start) { | 
|---|
| 2806 | case 0: | 
|---|
| 2807 | if (nl80211_send_band_rateinfo(msg, sband, | 
|---|
| 2808 | large: state->split)) | 
|---|
| 2809 | goto nla_put_failure; | 
|---|
| 2810 | state->chan_start++; | 
|---|
| 2811 | if (state->split) | 
|---|
| 2812 | break; | 
|---|
| 2813 | fallthrough; | 
|---|
| 2814 | default: | 
|---|
| 2815 | /* add frequencies */ | 
|---|
| 2816 | nl_freqs = nla_nest_start_noflag(skb: msg, | 
|---|
| 2817 | attrtype: NL80211_BAND_ATTR_FREQS); | 
|---|
| 2818 | if (!nl_freqs) | 
|---|
| 2819 | goto nla_put_failure; | 
|---|
| 2820 |  | 
|---|
| 2821 | for (i = state->chan_start - 1; | 
|---|
| 2822 | i < sband->n_channels; | 
|---|
| 2823 | i++) { | 
|---|
| 2824 | nl_freq = nla_nest_start_noflag(skb: msg, | 
|---|
| 2825 | attrtype: i); | 
|---|
| 2826 | if (!nl_freq) | 
|---|
| 2827 | goto nla_put_failure; | 
|---|
| 2828 |  | 
|---|
| 2829 | chan = &sband->channels[i]; | 
|---|
| 2830 |  | 
|---|
| 2831 | if (nl80211_msg_put_channel( | 
|---|
| 2832 | msg, wiphy: &rdev->wiphy, chan, | 
|---|
| 2833 | large: state->split)) | 
|---|
| 2834 | goto nla_put_failure; | 
|---|
| 2835 |  | 
|---|
| 2836 | nla_nest_end(skb: msg, start: nl_freq); | 
|---|
| 2837 | if (state->split) | 
|---|
| 2838 | break; | 
|---|
| 2839 | } | 
|---|
| 2840 | if (i < sband->n_channels) | 
|---|
| 2841 | state->chan_start = i + 2; | 
|---|
| 2842 | else | 
|---|
| 2843 | state->chan_start = 0; | 
|---|
| 2844 | nla_nest_end(skb: msg, start: nl_freqs); | 
|---|
| 2845 | } | 
|---|
| 2846 |  | 
|---|
| 2847 | nla_nest_end(skb: msg, start: nl_band); | 
|---|
| 2848 |  | 
|---|
| 2849 | if (state->split) { | 
|---|
| 2850 | /* start again here */ | 
|---|
| 2851 | if (state->chan_start) | 
|---|
| 2852 | band--; | 
|---|
| 2853 | break; | 
|---|
| 2854 | } | 
|---|
| 2855 | } | 
|---|
| 2856 | nla_nest_end(skb: msg, start: nl_bands); | 
|---|
| 2857 |  | 
|---|
| 2858 | if (band < NUM_NL80211_BANDS) | 
|---|
| 2859 | state->band_start = band + 1; | 
|---|
| 2860 | else | 
|---|
| 2861 | state->band_start = 0; | 
|---|
| 2862 |  | 
|---|
| 2863 | /* if bands & channels are done, continue outside */ | 
|---|
| 2864 | if (state->band_start == 0 && state->chan_start == 0) | 
|---|
| 2865 | state->split_start++; | 
|---|
| 2866 | if (state->split) | 
|---|
| 2867 | break; | 
|---|
| 2868 | fallthrough; | 
|---|
| 2869 | case 4: | 
|---|
| 2870 | nl_cmds = nla_nest_start_noflag(skb: msg, | 
|---|
| 2871 | attrtype: NL80211_ATTR_SUPPORTED_COMMANDS); | 
|---|
| 2872 | if (!nl_cmds) | 
|---|
| 2873 | goto nla_put_failure; | 
|---|
| 2874 |  | 
|---|
| 2875 | i = nl80211_add_commands_unsplit(rdev, msg); | 
|---|
| 2876 | if (i < 0) | 
|---|
| 2877 | goto nla_put_failure; | 
|---|
| 2878 | if (state->split) { | 
|---|
| 2879 | CMD(crit_proto_start, CRIT_PROTOCOL_START); | 
|---|
| 2880 | CMD(crit_proto_stop, CRIT_PROTOCOL_STOP); | 
|---|
| 2881 | if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH) | 
|---|
| 2882 | CMD(channel_switch, CHANNEL_SWITCH); | 
|---|
| 2883 | CMD(set_qos_map, SET_QOS_MAP); | 
|---|
| 2884 | if (rdev->wiphy.features & | 
|---|
| 2885 | NL80211_FEATURE_SUPPORTS_WMM_ADMISSION) | 
|---|
| 2886 | CMD(add_tx_ts, ADD_TX_TS); | 
|---|
| 2887 | CMD(set_multicast_to_unicast, SET_MULTICAST_TO_UNICAST); | 
|---|
| 2888 | CMD(update_connect_params, UPDATE_CONNECT_PARAMS); | 
|---|
| 2889 | CMD(update_ft_ies, UPDATE_FT_IES); | 
|---|
| 2890 | if (rdev->wiphy.sar_capa) | 
|---|
| 2891 | CMD(set_sar_specs, SET_SAR_SPECS); | 
|---|
| 2892 | CMD(assoc_ml_reconf, ASSOC_MLO_RECONF); | 
|---|
| 2893 | } | 
|---|
| 2894 | #undef CMD | 
|---|
| 2895 |  | 
|---|
| 2896 | nla_nest_end(skb: msg, start: nl_cmds); | 
|---|
| 2897 | state->split_start++; | 
|---|
| 2898 | if (state->split) | 
|---|
| 2899 | break; | 
|---|
| 2900 | fallthrough; | 
|---|
| 2901 | case 5: | 
|---|
| 2902 | if (rdev->ops->remain_on_channel && | 
|---|
| 2903 | (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) && | 
|---|
| 2904 | nla_put_u32(skb: msg, | 
|---|
| 2905 | attrtype: NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION, | 
|---|
| 2906 | value: rdev->wiphy.max_remain_on_channel_duration)) | 
|---|
| 2907 | goto nla_put_failure; | 
|---|
| 2908 |  | 
|---|
| 2909 | if ((rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) && | 
|---|
| 2910 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_OFFCHANNEL_TX_OK)) | 
|---|
| 2911 | goto nla_put_failure; | 
|---|
| 2912 |  | 
|---|
| 2913 | state->split_start++; | 
|---|
| 2914 | if (state->split) | 
|---|
| 2915 | break; | 
|---|
| 2916 | fallthrough; | 
|---|
| 2917 | case 6: | 
|---|
| 2918 | #ifdef CONFIG_PM | 
|---|
| 2919 | if (nl80211_send_wowlan(msg, rdev, large: state->split)) | 
|---|
| 2920 | goto nla_put_failure; | 
|---|
| 2921 | state->split_start++; | 
|---|
| 2922 | if (state->split) | 
|---|
| 2923 | break; | 
|---|
| 2924 | #else | 
|---|
| 2925 | state->split_start++; | 
|---|
| 2926 | #endif | 
|---|
| 2927 | fallthrough; | 
|---|
| 2928 | case 7: | 
|---|
| 2929 | if (nl80211_put_iftypes(msg, attr: NL80211_ATTR_SOFTWARE_IFTYPES, | 
|---|
| 2930 | ifmodes: rdev->wiphy.software_iftypes)) | 
|---|
| 2931 | goto nla_put_failure; | 
|---|
| 2932 |  | 
|---|
| 2933 | if (nl80211_put_iface_combinations(wiphy: &rdev->wiphy, msg, | 
|---|
| 2934 | attr: NL80211_ATTR_INTERFACE_COMBINATIONS, | 
|---|
| 2935 | radio: rdev->wiphy.n_radio ? 0 : -1, | 
|---|
| 2936 | large: state->split, nested: 0)) | 
|---|
| 2937 | goto nla_put_failure; | 
|---|
| 2938 |  | 
|---|
| 2939 | state->split_start++; | 
|---|
| 2940 | if (state->split) | 
|---|
| 2941 | break; | 
|---|
| 2942 | fallthrough; | 
|---|
| 2943 | case 8: | 
|---|
| 2944 | if ((rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) && | 
|---|
| 2945 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_DEVICE_AP_SME, | 
|---|
| 2946 | value: rdev->wiphy.ap_sme_capa)) | 
|---|
| 2947 | goto nla_put_failure; | 
|---|
| 2948 |  | 
|---|
| 2949 | features = rdev->wiphy.features; | 
|---|
| 2950 | /* | 
|---|
| 2951 | * We can only add the per-channel limit information if the | 
|---|
| 2952 | * dump is split, otherwise it makes it too big. Therefore | 
|---|
| 2953 | * only advertise it in that case. | 
|---|
| 2954 | */ | 
|---|
| 2955 | if (state->split) | 
|---|
| 2956 | features |= NL80211_FEATURE_ADVERTISE_CHAN_LIMITS; | 
|---|
| 2957 | if (nla_put_u32(skb: msg, NL80211_ATTR_FEATURE_FLAGS, value: features)) | 
|---|
| 2958 | goto nla_put_failure; | 
|---|
| 2959 |  | 
|---|
| 2960 | if (rdev->wiphy.ht_capa_mod_mask && | 
|---|
| 2961 | nla_put(skb: msg, attrtype: NL80211_ATTR_HT_CAPABILITY_MASK, | 
|---|
| 2962 | attrlen: sizeof(*rdev->wiphy.ht_capa_mod_mask), | 
|---|
| 2963 | data: rdev->wiphy.ht_capa_mod_mask)) | 
|---|
| 2964 | goto nla_put_failure; | 
|---|
| 2965 |  | 
|---|
| 2966 | if (rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME && | 
|---|
| 2967 | rdev->wiphy.max_acl_mac_addrs && | 
|---|
| 2968 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_MAC_ACL_MAX, | 
|---|
| 2969 | value: rdev->wiphy.max_acl_mac_addrs)) | 
|---|
| 2970 | goto nla_put_failure; | 
|---|
| 2971 |  | 
|---|
| 2972 | /* | 
|---|
| 2973 | * Any information below this point is only available to | 
|---|
| 2974 | * applications that can deal with it being split. This | 
|---|
| 2975 | * helps ensure that newly added capabilities don't break | 
|---|
| 2976 | * older tools by overrunning their buffers. | 
|---|
| 2977 | * | 
|---|
| 2978 | * We still increment split_start so that in the split | 
|---|
| 2979 | * case we'll continue with more data in the next round, | 
|---|
| 2980 | * but break unconditionally so unsplit data stops here. | 
|---|
| 2981 | */ | 
|---|
| 2982 | if (state->split) | 
|---|
| 2983 | state->split_start++; | 
|---|
| 2984 | else | 
|---|
| 2985 | state->split_start = 0; | 
|---|
| 2986 | break; | 
|---|
| 2987 | case 9: | 
|---|
| 2988 | if (nl80211_send_mgmt_stypes(msg, mgmt_stypes)) | 
|---|
| 2989 | goto nla_put_failure; | 
|---|
| 2990 |  | 
|---|
| 2991 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS, | 
|---|
| 2992 | value: rdev->wiphy.max_sched_scan_plans) || | 
|---|
| 2993 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL, | 
|---|
| 2994 | value: rdev->wiphy.max_sched_scan_plan_interval) || | 
|---|
| 2995 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS, | 
|---|
| 2996 | value: rdev->wiphy.max_sched_scan_plan_iterations)) | 
|---|
| 2997 | goto nla_put_failure; | 
|---|
| 2998 |  | 
|---|
| 2999 | if (rdev->wiphy.extended_capabilities && | 
|---|
| 3000 | (nla_put(skb: msg, attrtype: NL80211_ATTR_EXT_CAPA, | 
|---|
| 3001 | attrlen: rdev->wiphy.extended_capabilities_len, | 
|---|
| 3002 | data: rdev->wiphy.extended_capabilities) || | 
|---|
| 3003 | nla_put(skb: msg, attrtype: NL80211_ATTR_EXT_CAPA_MASK, | 
|---|
| 3004 | attrlen: rdev->wiphy.extended_capabilities_len, | 
|---|
| 3005 | data: rdev->wiphy.extended_capabilities_mask))) | 
|---|
| 3006 | goto nla_put_failure; | 
|---|
| 3007 |  | 
|---|
| 3008 | if (rdev->wiphy.vht_capa_mod_mask && | 
|---|
| 3009 | nla_put(skb: msg, attrtype: NL80211_ATTR_VHT_CAPABILITY_MASK, | 
|---|
| 3010 | attrlen: sizeof(*rdev->wiphy.vht_capa_mod_mask), | 
|---|
| 3011 | data: rdev->wiphy.vht_capa_mod_mask)) | 
|---|
| 3012 | goto nla_put_failure; | 
|---|
| 3013 |  | 
|---|
| 3014 | if (nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, | 
|---|
| 3015 | data: rdev->wiphy.perm_addr)) | 
|---|
| 3016 | goto nla_put_failure; | 
|---|
| 3017 |  | 
|---|
| 3018 | if (!is_zero_ether_addr(addr: rdev->wiphy.addr_mask) && | 
|---|
| 3019 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC_MASK, ETH_ALEN, | 
|---|
| 3020 | data: rdev->wiphy.addr_mask)) | 
|---|
| 3021 | goto nla_put_failure; | 
|---|
| 3022 |  | 
|---|
| 3023 | if (rdev->wiphy.n_addresses > 1) { | 
|---|
| 3024 | void *attr; | 
|---|
| 3025 |  | 
|---|
| 3026 | attr = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_MAC_ADDRS); | 
|---|
| 3027 | if (!attr) | 
|---|
| 3028 | goto nla_put_failure; | 
|---|
| 3029 |  | 
|---|
| 3030 | for (i = 0; i < rdev->wiphy.n_addresses; i++) | 
|---|
| 3031 | if (nla_put(skb: msg, attrtype: i + 1, ETH_ALEN, | 
|---|
| 3032 | data: rdev->wiphy.addresses[i].addr)) | 
|---|
| 3033 | goto nla_put_failure; | 
|---|
| 3034 |  | 
|---|
| 3035 | nla_nest_end(skb: msg, start: attr); | 
|---|
| 3036 | } | 
|---|
| 3037 |  | 
|---|
| 3038 | state->split_start++; | 
|---|
| 3039 | break; | 
|---|
| 3040 | case 10: | 
|---|
| 3041 | if (nl80211_send_coalesce(msg, rdev)) | 
|---|
| 3042 | goto nla_put_failure; | 
|---|
| 3043 |  | 
|---|
| 3044 | if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) && | 
|---|
| 3045 | (nla_put_flag(skb: msg, attrtype: NL80211_ATTR_SUPPORT_5_MHZ) || | 
|---|
| 3046 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_SUPPORT_10_MHZ))) | 
|---|
| 3047 | goto nla_put_failure; | 
|---|
| 3048 |  | 
|---|
| 3049 | if (rdev->wiphy.max_ap_assoc_sta && | 
|---|
| 3050 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_MAX_AP_ASSOC_STA, | 
|---|
| 3051 | value: rdev->wiphy.max_ap_assoc_sta)) | 
|---|
| 3052 | goto nla_put_failure; | 
|---|
| 3053 |  | 
|---|
| 3054 | state->split_start++; | 
|---|
| 3055 | break; | 
|---|
| 3056 | case 11: | 
|---|
| 3057 | if (rdev->wiphy.n_vendor_commands) { | 
|---|
| 3058 | const struct nl80211_vendor_cmd_info *info; | 
|---|
| 3059 | struct nlattr *nested; | 
|---|
| 3060 |  | 
|---|
| 3061 | nested = nla_nest_start_noflag(skb: msg, | 
|---|
| 3062 | attrtype: NL80211_ATTR_VENDOR_DATA); | 
|---|
| 3063 | if (!nested) | 
|---|
| 3064 | goto nla_put_failure; | 
|---|
| 3065 |  | 
|---|
| 3066 | for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) { | 
|---|
| 3067 | info = &rdev->wiphy.vendor_commands[i].info; | 
|---|
| 3068 | if (nla_put(skb: msg, attrtype: i + 1, attrlen: sizeof(*info), data: info)) | 
|---|
| 3069 | goto nla_put_failure; | 
|---|
| 3070 | } | 
|---|
| 3071 | nla_nest_end(skb: msg, start: nested); | 
|---|
| 3072 | } | 
|---|
| 3073 |  | 
|---|
| 3074 | if (rdev->wiphy.n_vendor_events) { | 
|---|
| 3075 | const struct nl80211_vendor_cmd_info *info; | 
|---|
| 3076 | struct nlattr *nested; | 
|---|
| 3077 |  | 
|---|
| 3078 | nested = nla_nest_start_noflag(skb: msg, | 
|---|
| 3079 | attrtype: NL80211_ATTR_VENDOR_EVENTS); | 
|---|
| 3080 | if (!nested) | 
|---|
| 3081 | goto nla_put_failure; | 
|---|
| 3082 |  | 
|---|
| 3083 | for (i = 0; i < rdev->wiphy.n_vendor_events; i++) { | 
|---|
| 3084 | info = &rdev->wiphy.vendor_events[i]; | 
|---|
| 3085 | if (nla_put(skb: msg, attrtype: i + 1, attrlen: sizeof(*info), data: info)) | 
|---|
| 3086 | goto nla_put_failure; | 
|---|
| 3087 | } | 
|---|
| 3088 | nla_nest_end(skb: msg, start: nested); | 
|---|
| 3089 | } | 
|---|
| 3090 | state->split_start++; | 
|---|
| 3091 | break; | 
|---|
| 3092 | case 12: | 
|---|
| 3093 | if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH && | 
|---|
| 3094 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MAX_CSA_COUNTERS, | 
|---|
| 3095 | value: rdev->wiphy.max_num_csa_counters)) | 
|---|
| 3096 | goto nla_put_failure; | 
|---|
| 3097 |  | 
|---|
| 3098 | if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED && | 
|---|
| 3099 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_WIPHY_SELF_MANAGED_REG)) | 
|---|
| 3100 | goto nla_put_failure; | 
|---|
| 3101 |  | 
|---|
| 3102 | if (rdev->wiphy.max_sched_scan_reqs && | 
|---|
| 3103 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_SCHED_SCAN_MAX_REQS, | 
|---|
| 3104 | value: rdev->wiphy.max_sched_scan_reqs)) | 
|---|
| 3105 | goto nla_put_failure; | 
|---|
| 3106 |  | 
|---|
| 3107 | if (nla_put(skb: msg, attrtype: NL80211_ATTR_EXT_FEATURES, | 
|---|
| 3108 | attrlen: sizeof(rdev->wiphy.ext_features), | 
|---|
| 3109 | data: rdev->wiphy.ext_features)) | 
|---|
| 3110 | goto nla_put_failure; | 
|---|
| 3111 |  | 
|---|
| 3112 | if (rdev->wiphy.bss_param_support) { | 
|---|
| 3113 | struct nlattr *nested; | 
|---|
| 3114 | u32 parsup = rdev->wiphy.bss_param_support; | 
|---|
| 3115 |  | 
|---|
| 3116 | nested = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_BSS_PARAM); | 
|---|
| 3117 | if (!nested) | 
|---|
| 3118 | goto nla_put_failure; | 
|---|
| 3119 |  | 
|---|
| 3120 | if ((parsup & WIPHY_BSS_PARAM_CTS_PROT) && | 
|---|
| 3121 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_BSS_CTS_PROT)) | 
|---|
| 3122 | goto nla_put_failure; | 
|---|
| 3123 | if ((parsup & WIPHY_BSS_PARAM_SHORT_PREAMBLE) && | 
|---|
| 3124 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_BSS_SHORT_PREAMBLE)) | 
|---|
| 3125 | goto nla_put_failure; | 
|---|
| 3126 | if ((parsup & WIPHY_BSS_PARAM_SHORT_SLOT_TIME) && | 
|---|
| 3127 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_BSS_SHORT_SLOT_TIME)) | 
|---|
| 3128 | goto nla_put_failure; | 
|---|
| 3129 | if ((parsup & WIPHY_BSS_PARAM_BASIC_RATES) && | 
|---|
| 3130 | nla_put_flag(skb: msg, NL80211_ATTR_BSS_BASIC_RATES)) | 
|---|
| 3131 | goto nla_put_failure; | 
|---|
| 3132 | if ((parsup & WIPHY_BSS_PARAM_AP_ISOLATE) && | 
|---|
| 3133 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_AP_ISOLATE)) | 
|---|
| 3134 | goto nla_put_failure; | 
|---|
| 3135 | if ((parsup & WIPHY_BSS_PARAM_HT_OPMODE) && | 
|---|
| 3136 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_BSS_HT_OPMODE)) | 
|---|
| 3137 | goto nla_put_failure; | 
|---|
| 3138 | if ((parsup & WIPHY_BSS_PARAM_P2P_CTWINDOW) && | 
|---|
| 3139 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_P2P_CTWINDOW)) | 
|---|
| 3140 | goto nla_put_failure; | 
|---|
| 3141 | if ((parsup & WIPHY_BSS_PARAM_P2P_OPPPS) && | 
|---|
| 3142 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_P2P_OPPPS)) | 
|---|
| 3143 | goto nla_put_failure; | 
|---|
| 3144 | nla_nest_end(skb: msg, start: nested); | 
|---|
| 3145 | } | 
|---|
| 3146 | if (rdev->wiphy.bss_select_support) { | 
|---|
| 3147 | struct nlattr *nested; | 
|---|
| 3148 | u32 bss_select_support = rdev->wiphy.bss_select_support; | 
|---|
| 3149 |  | 
|---|
| 3150 | nested = nla_nest_start_noflag(skb: msg, | 
|---|
| 3151 | attrtype: NL80211_ATTR_BSS_SELECT); | 
|---|
| 3152 | if (!nested) | 
|---|
| 3153 | goto nla_put_failure; | 
|---|
| 3154 |  | 
|---|
| 3155 | i = 0; | 
|---|
| 3156 | while (bss_select_support) { | 
|---|
| 3157 | if ((bss_select_support & 1) && | 
|---|
| 3158 | nla_put_flag(skb: msg, attrtype: i)) | 
|---|
| 3159 | goto nla_put_failure; | 
|---|
| 3160 | i++; | 
|---|
| 3161 | bss_select_support >>= 1; | 
|---|
| 3162 | } | 
|---|
| 3163 | nla_nest_end(skb: msg, start: nested); | 
|---|
| 3164 | } | 
|---|
| 3165 |  | 
|---|
| 3166 | state->split_start++; | 
|---|
| 3167 | break; | 
|---|
| 3168 | case 13: | 
|---|
| 3169 | if (rdev->wiphy.num_iftype_ext_capab && | 
|---|
| 3170 | rdev->wiphy.iftype_ext_capab) { | 
|---|
| 3171 | struct nlattr *nested_ext_capab, *nested; | 
|---|
| 3172 |  | 
|---|
| 3173 | nested = nla_nest_start_noflag(skb: msg, | 
|---|
| 3174 | attrtype: NL80211_ATTR_IFTYPE_EXT_CAPA); | 
|---|
| 3175 | if (!nested) | 
|---|
| 3176 | goto nla_put_failure; | 
|---|
| 3177 |  | 
|---|
| 3178 | for (i = state->capa_start; | 
|---|
| 3179 | i < rdev->wiphy.num_iftype_ext_capab; i++) { | 
|---|
| 3180 | const struct wiphy_iftype_ext_capab *capab; | 
|---|
| 3181 |  | 
|---|
| 3182 | capab = &rdev->wiphy.iftype_ext_capab[i]; | 
|---|
| 3183 |  | 
|---|
| 3184 | nested_ext_capab = nla_nest_start_noflag(skb: msg, | 
|---|
| 3185 | attrtype: i); | 
|---|
| 3186 | if (!nested_ext_capab || | 
|---|
| 3187 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFTYPE, | 
|---|
| 3188 | value: capab->iftype) || | 
|---|
| 3189 | nla_put(skb: msg, attrtype: NL80211_ATTR_EXT_CAPA, | 
|---|
| 3190 | attrlen: capab->extended_capabilities_len, | 
|---|
| 3191 | data: capab->extended_capabilities) || | 
|---|
| 3192 | nla_put(skb: msg, attrtype: NL80211_ATTR_EXT_CAPA_MASK, | 
|---|
| 3193 | attrlen: capab->extended_capabilities_len, | 
|---|
| 3194 | data: capab->extended_capabilities_mask)) | 
|---|
| 3195 | goto nla_put_failure; | 
|---|
| 3196 |  | 
|---|
| 3197 | if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_MLO && | 
|---|
| 3198 | (nla_put_u16(skb: msg, | 
|---|
| 3199 | attrtype: NL80211_ATTR_EML_CAPABILITY, | 
|---|
| 3200 | value: capab->eml_capabilities) || | 
|---|
| 3201 | nla_put_u16(skb: msg, | 
|---|
| 3202 | attrtype: NL80211_ATTR_MLD_CAPA_AND_OPS, | 
|---|
| 3203 | value: capab->mld_capa_and_ops))) | 
|---|
| 3204 | goto nla_put_failure; | 
|---|
| 3205 |  | 
|---|
| 3206 | nla_nest_end(skb: msg, start: nested_ext_capab); | 
|---|
| 3207 | if (state->split) | 
|---|
| 3208 | break; | 
|---|
| 3209 | } | 
|---|
| 3210 | nla_nest_end(skb: msg, start: nested); | 
|---|
| 3211 | if (i < rdev->wiphy.num_iftype_ext_capab) { | 
|---|
| 3212 | state->capa_start = i + 1; | 
|---|
| 3213 | break; | 
|---|
| 3214 | } | 
|---|
| 3215 | } | 
|---|
| 3216 |  | 
|---|
| 3217 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_BANDS, | 
|---|
| 3218 | value: rdev->wiphy.nan_supported_bands)) | 
|---|
| 3219 | goto nla_put_failure; | 
|---|
| 3220 |  | 
|---|
| 3221 | if (wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 3222 | ftidx: NL80211_EXT_FEATURE_TXQS)) { | 
|---|
| 3223 | struct cfg80211_txq_stats txqstats = {}; | 
|---|
| 3224 | int res; | 
|---|
| 3225 |  | 
|---|
| 3226 | res = rdev_get_txq_stats(rdev, NULL, txqstats: &txqstats); | 
|---|
| 3227 | if (!res && | 
|---|
| 3228 | !nl80211_put_txq_stats(msg, txqstats: &txqstats, | 
|---|
| 3229 | attrtype: NL80211_ATTR_TXQ_STATS)) | 
|---|
| 3230 | goto nla_put_failure; | 
|---|
| 3231 |  | 
|---|
| 3232 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_TXQ_LIMIT, | 
|---|
| 3233 | value: rdev->wiphy.txq_limit)) | 
|---|
| 3234 | goto nla_put_failure; | 
|---|
| 3235 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_TXQ_MEMORY_LIMIT, | 
|---|
| 3236 | value: rdev->wiphy.txq_memory_limit)) | 
|---|
| 3237 | goto nla_put_failure; | 
|---|
| 3238 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_TXQ_QUANTUM, | 
|---|
| 3239 | value: rdev->wiphy.txq_quantum)) | 
|---|
| 3240 | goto nla_put_failure; | 
|---|
| 3241 | } | 
|---|
| 3242 |  | 
|---|
| 3243 | state->split_start++; | 
|---|
| 3244 | break; | 
|---|
| 3245 | case 14: | 
|---|
| 3246 | if (nl80211_send_pmsr_capa(rdev, msg)) | 
|---|
| 3247 | goto nla_put_failure; | 
|---|
| 3248 |  | 
|---|
| 3249 | state->split_start++; | 
|---|
| 3250 | break; | 
|---|
| 3251 | case 15: | 
|---|
| 3252 | if (rdev->wiphy.akm_suites && | 
|---|
| 3253 | nla_put(skb: msg, NL80211_ATTR_AKM_SUITES, | 
|---|
| 3254 | attrlen: sizeof(u32) * rdev->wiphy.n_akm_suites, | 
|---|
| 3255 | data: rdev->wiphy.akm_suites)) | 
|---|
| 3256 | goto nla_put_failure; | 
|---|
| 3257 |  | 
|---|
| 3258 | if (nl80211_put_iftype_akm_suites(rdev, msg)) | 
|---|
| 3259 | goto nla_put_failure; | 
|---|
| 3260 |  | 
|---|
| 3261 | if (nl80211_put_tid_config_support(rdev, msg)) | 
|---|
| 3262 | goto nla_put_failure; | 
|---|
| 3263 | state->split_start++; | 
|---|
| 3264 | break; | 
|---|
| 3265 | case 16: | 
|---|
| 3266 | if (nl80211_put_sar_specs(rdev, msg)) | 
|---|
| 3267 | goto nla_put_failure; | 
|---|
| 3268 |  | 
|---|
| 3269 | if (nl80211_put_mbssid_support(wiphy: &rdev->wiphy, msg)) | 
|---|
| 3270 | goto nla_put_failure; | 
|---|
| 3271 |  | 
|---|
| 3272 | if (nla_put_u16(skb: msg, attrtype: NL80211_ATTR_MAX_NUM_AKM_SUITES, | 
|---|
| 3273 | value: rdev->wiphy.max_num_akm_suites)) | 
|---|
| 3274 | goto nla_put_failure; | 
|---|
| 3275 |  | 
|---|
| 3276 | if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_MLO) | 
|---|
| 3277 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_MLO_SUPPORT); | 
|---|
| 3278 |  | 
|---|
| 3279 | if (rdev->wiphy.hw_timestamp_max_peers && | 
|---|
| 3280 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS, | 
|---|
| 3281 | value: rdev->wiphy.hw_timestamp_max_peers)) | 
|---|
| 3282 | goto nla_put_failure; | 
|---|
| 3283 |  | 
|---|
| 3284 | state->split_start++; | 
|---|
| 3285 | break; | 
|---|
| 3286 | case 17: | 
|---|
| 3287 | if (nl80211_put_radios(wiphy: &rdev->wiphy, msg)) | 
|---|
| 3288 | goto nla_put_failure; | 
|---|
| 3289 |  | 
|---|
| 3290 | state->split_start++; | 
|---|
| 3291 | break; | 
|---|
| 3292 | case 18: | 
|---|
| 3293 | if (nl80211_put_nan_capa(wiphy: &rdev->wiphy, msg)) | 
|---|
| 3294 | goto nla_put_failure; | 
|---|
| 3295 |  | 
|---|
| 3296 | /* done */ | 
|---|
| 3297 | state->split_start = 0; | 
|---|
| 3298 | break; | 
|---|
| 3299 | } | 
|---|
| 3300 | finish: | 
|---|
| 3301 | genlmsg_end(skb: msg, hdr); | 
|---|
| 3302 | return 0; | 
|---|
| 3303 |  | 
|---|
| 3304 | nla_put_failure: | 
|---|
| 3305 | genlmsg_cancel(skb: msg, hdr); | 
|---|
| 3306 | return -EMSGSIZE; | 
|---|
| 3307 | } | 
|---|
| 3308 |  | 
|---|
| 3309 | static int nl80211_dump_wiphy_parse(struct sk_buff *skb, | 
|---|
| 3310 | struct netlink_callback *cb, | 
|---|
| 3311 | struct nl80211_dump_wiphy_state *state) | 
|---|
| 3312 | { | 
|---|
| 3313 | struct nlattr **tb = kcalloc(NUM_NL80211_ATTR, sizeof(*tb), GFP_KERNEL); | 
|---|
| 3314 | int ret; | 
|---|
| 3315 |  | 
|---|
| 3316 | if (!tb) | 
|---|
| 3317 | return -ENOMEM; | 
|---|
| 3318 |  | 
|---|
| 3319 | ret = nlmsg_parse_deprecated(nlh: cb->nlh, | 
|---|
| 3320 | GENL_HDRLEN + nl80211_fam.hdrsize, | 
|---|
| 3321 | tb, maxtype: nl80211_fam.maxattr, | 
|---|
| 3322 | policy: nl80211_policy, NULL); | 
|---|
| 3323 | /* ignore parse errors for backward compatibility */ | 
|---|
| 3324 | if (ret) { | 
|---|
| 3325 | ret = 0; | 
|---|
| 3326 | goto out; | 
|---|
| 3327 | } | 
|---|
| 3328 |  | 
|---|
| 3329 | state->split = tb[NL80211_ATTR_SPLIT_WIPHY_DUMP]; | 
|---|
| 3330 | if (tb[NL80211_ATTR_WIPHY]) | 
|---|
| 3331 | state->filter_wiphy = nla_get_u32(nla: tb[NL80211_ATTR_WIPHY]); | 
|---|
| 3332 | if (tb[NL80211_ATTR_WDEV]) | 
|---|
| 3333 | state->filter_wiphy = nla_get_u64(nla: tb[NL80211_ATTR_WDEV]) >> 32; | 
|---|
| 3334 | if (tb[NL80211_ATTR_IFINDEX]) { | 
|---|
| 3335 | struct net_device *netdev; | 
|---|
| 3336 | struct cfg80211_registered_device *rdev; | 
|---|
| 3337 | int ifidx = nla_get_u32(nla: tb[NL80211_ATTR_IFINDEX]); | 
|---|
| 3338 |  | 
|---|
| 3339 | netdev = __dev_get_by_index(net: sock_net(sk: skb->sk), ifindex: ifidx); | 
|---|
| 3340 | if (!netdev) { | 
|---|
| 3341 | ret = -ENODEV; | 
|---|
| 3342 | goto out; | 
|---|
| 3343 | } | 
|---|
| 3344 | if (netdev->ieee80211_ptr) { | 
|---|
| 3345 | rdev = wiphy_to_rdev( | 
|---|
| 3346 | wiphy: netdev->ieee80211_ptr->wiphy); | 
|---|
| 3347 | state->filter_wiphy = rdev->wiphy_idx; | 
|---|
| 3348 | } | 
|---|
| 3349 | } | 
|---|
| 3350 |  | 
|---|
| 3351 | ret = 0; | 
|---|
| 3352 | out: | 
|---|
| 3353 | kfree(objp: tb); | 
|---|
| 3354 | return ret; | 
|---|
| 3355 | } | 
|---|
| 3356 |  | 
|---|
| 3357 | static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) | 
|---|
| 3358 | { | 
|---|
| 3359 | int idx = 0, ret; | 
|---|
| 3360 | struct nl80211_dump_wiphy_state *state = (void *)cb->args[0]; | 
|---|
| 3361 | struct cfg80211_registered_device *rdev; | 
|---|
| 3362 |  | 
|---|
| 3363 | rtnl_lock(); | 
|---|
| 3364 | if (!state) { | 
|---|
| 3365 | state = kzalloc(sizeof(*state), GFP_KERNEL); | 
|---|
| 3366 | if (!state) { | 
|---|
| 3367 | rtnl_unlock(); | 
|---|
| 3368 | return -ENOMEM; | 
|---|
| 3369 | } | 
|---|
| 3370 | state->filter_wiphy = -1; | 
|---|
| 3371 | ret = nl80211_dump_wiphy_parse(skb, cb, state); | 
|---|
| 3372 | if (ret) { | 
|---|
| 3373 | kfree(objp: state); | 
|---|
| 3374 | rtnl_unlock(); | 
|---|
| 3375 | return ret; | 
|---|
| 3376 | } | 
|---|
| 3377 | cb->args[0] = (long)state; | 
|---|
| 3378 | } | 
|---|
| 3379 |  | 
|---|
| 3380 | for_each_rdev(rdev) { | 
|---|
| 3381 | if (!net_eq(net1: wiphy_net(wiphy: &rdev->wiphy), net2: sock_net(sk: skb->sk))) | 
|---|
| 3382 | continue; | 
|---|
| 3383 | if (++idx <= state->start) | 
|---|
| 3384 | continue; | 
|---|
| 3385 | if (state->filter_wiphy != -1 && | 
|---|
| 3386 | state->filter_wiphy != rdev->wiphy_idx) | 
|---|
| 3387 | continue; | 
|---|
| 3388 | wiphy_lock(wiphy: &rdev->wiphy); | 
|---|
| 3389 | /* attempt to fit multiple wiphy data chunks into the skb */ | 
|---|
| 3390 | do { | 
|---|
| 3391 | ret = nl80211_send_wiphy(rdev, cmd: NL80211_CMD_NEW_WIPHY, | 
|---|
| 3392 | msg: skb, | 
|---|
| 3393 | NETLINK_CB(cb->skb).portid, | 
|---|
| 3394 | seq: cb->nlh->nlmsg_seq, | 
|---|
| 3395 | NLM_F_MULTI, state); | 
|---|
| 3396 | if (ret < 0) { | 
|---|
| 3397 | /* | 
|---|
| 3398 | * If sending the wiphy data didn't fit (ENOBUFS | 
|---|
| 3399 | * or EMSGSIZE returned), this SKB is still | 
|---|
| 3400 | * empty (so it's not too big because another | 
|---|
| 3401 | * wiphy dataset is already in the skb) and | 
|---|
| 3402 | * we've not tried to adjust the dump allocation | 
|---|
| 3403 | * yet ... then adjust the alloc size to be | 
|---|
| 3404 | * bigger, and return 1 but with the empty skb. | 
|---|
| 3405 | * This results in an empty message being RX'ed | 
|---|
| 3406 | * in userspace, but that is ignored. | 
|---|
| 3407 | * | 
|---|
| 3408 | * We can then retry with the larger buffer. | 
|---|
| 3409 | */ | 
|---|
| 3410 | if ((ret == -ENOBUFS || ret == -EMSGSIZE) && | 
|---|
| 3411 | !skb->len && !state->split && | 
|---|
| 3412 | cb->min_dump_alloc < 4096) { | 
|---|
| 3413 | cb->min_dump_alloc = 4096; | 
|---|
| 3414 | state->split_start = 0; | 
|---|
| 3415 | wiphy_unlock(wiphy: &rdev->wiphy); | 
|---|
| 3416 | rtnl_unlock(); | 
|---|
| 3417 | return 1; | 
|---|
| 3418 | } | 
|---|
| 3419 | idx--; | 
|---|
| 3420 | break; | 
|---|
| 3421 | } | 
|---|
| 3422 | } while (state->split_start > 0); | 
|---|
| 3423 | wiphy_unlock(wiphy: &rdev->wiphy); | 
|---|
| 3424 | break; | 
|---|
| 3425 | } | 
|---|
| 3426 | rtnl_unlock(); | 
|---|
| 3427 |  | 
|---|
| 3428 | state->start = idx; | 
|---|
| 3429 |  | 
|---|
| 3430 | return skb->len; | 
|---|
| 3431 | } | 
|---|
| 3432 |  | 
|---|
| 3433 | static int nl80211_dump_wiphy_done(struct netlink_callback *cb) | 
|---|
| 3434 | { | 
|---|
| 3435 | kfree(objp: (void *)cb->args[0]); | 
|---|
| 3436 | return 0; | 
|---|
| 3437 | } | 
|---|
| 3438 |  | 
|---|
| 3439 | static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 3440 | { | 
|---|
| 3441 | struct sk_buff *msg; | 
|---|
| 3442 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 3443 | struct nl80211_dump_wiphy_state state = {}; | 
|---|
| 3444 |  | 
|---|
| 3445 | msg = nlmsg_new(payload: 4096, GFP_KERNEL); | 
|---|
| 3446 | if (!msg) | 
|---|
| 3447 | return -ENOMEM; | 
|---|
| 3448 |  | 
|---|
| 3449 | if (nl80211_send_wiphy(rdev, cmd: NL80211_CMD_NEW_WIPHY, msg, | 
|---|
| 3450 | portid: info->snd_portid, seq: info->snd_seq, flags: 0, | 
|---|
| 3451 | state: &state) < 0) { | 
|---|
| 3452 | nlmsg_free(skb: msg); | 
|---|
| 3453 | return -ENOBUFS; | 
|---|
| 3454 | } | 
|---|
| 3455 |  | 
|---|
| 3456 | return genlmsg_reply(skb: msg, info); | 
|---|
| 3457 | } | 
|---|
| 3458 |  | 
|---|
| 3459 | static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = { | 
|---|
| 3460 | [NL80211_TXQ_ATTR_QUEUE]		= { .type = NLA_U8 }, | 
|---|
| 3461 | [NL80211_TXQ_ATTR_TXOP]			= { .type = NLA_U16 }, | 
|---|
| 3462 | [NL80211_TXQ_ATTR_CWMIN]		= { .type = NLA_U16 }, | 
|---|
| 3463 | [NL80211_TXQ_ATTR_CWMAX]		= { .type = NLA_U16 }, | 
|---|
| 3464 | [NL80211_TXQ_ATTR_AIFS]			= { .type = NLA_U8 }, | 
|---|
| 3465 | }; | 
|---|
| 3466 |  | 
|---|
| 3467 | static int parse_txq_params(struct nlattr *tb[], | 
|---|
| 3468 | struct ieee80211_txq_params *txq_params) | 
|---|
| 3469 | { | 
|---|
| 3470 | u8 ac; | 
|---|
| 3471 |  | 
|---|
| 3472 | if (!tb[NL80211_TXQ_ATTR_AC] || !tb[NL80211_TXQ_ATTR_TXOP] || | 
|---|
| 3473 | !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] || | 
|---|
| 3474 | !tb[NL80211_TXQ_ATTR_AIFS]) | 
|---|
| 3475 | return -EINVAL; | 
|---|
| 3476 |  | 
|---|
| 3477 | ac = nla_get_u8(nla: tb[NL80211_TXQ_ATTR_AC]); | 
|---|
| 3478 | txq_params->txop = nla_get_u16(nla: tb[NL80211_TXQ_ATTR_TXOP]); | 
|---|
| 3479 | txq_params->cwmin = nla_get_u16(nla: tb[NL80211_TXQ_ATTR_CWMIN]); | 
|---|
| 3480 | txq_params->cwmax = nla_get_u16(nla: tb[NL80211_TXQ_ATTR_CWMAX]); | 
|---|
| 3481 | txq_params->aifs = nla_get_u8(nla: tb[NL80211_TXQ_ATTR_AIFS]); | 
|---|
| 3482 |  | 
|---|
| 3483 | if (ac >= NL80211_NUM_ACS) | 
|---|
| 3484 | return -EINVAL; | 
|---|
| 3485 | txq_params->ac = array_index_nospec(ac, NL80211_NUM_ACS); | 
|---|
| 3486 | return 0; | 
|---|
| 3487 | } | 
|---|
| 3488 |  | 
|---|
| 3489 | static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) | 
|---|
| 3490 | { | 
|---|
| 3491 | /* | 
|---|
| 3492 | * You can only set the channel explicitly for some interfaces, | 
|---|
| 3493 | * most have their channel managed via their respective | 
|---|
| 3494 | * "establish a connection" command (connect, join, ...) | 
|---|
| 3495 | * | 
|---|
| 3496 | * For AP/GO and mesh mode, the channel can be set with the | 
|---|
| 3497 | * channel userspace API, but is only stored and passed to the | 
|---|
| 3498 | * low-level driver when the AP starts or the mesh is joined. | 
|---|
| 3499 | * This is for backward compatibility, userspace can also give | 
|---|
| 3500 | * the channel in the start-ap or join-mesh commands instead. | 
|---|
| 3501 | * | 
|---|
| 3502 | * Monitors are special as they are normally slaved to | 
|---|
| 3503 | * whatever else is going on, so they have their own special | 
|---|
| 3504 | * operation to set the monitor channel if possible. | 
|---|
| 3505 | */ | 
|---|
| 3506 | return !wdev || | 
|---|
| 3507 | wdev->iftype == NL80211_IFTYPE_AP || | 
|---|
| 3508 | wdev->iftype == NL80211_IFTYPE_MESH_POINT || | 
|---|
| 3509 | wdev->iftype == NL80211_IFTYPE_MONITOR || | 
|---|
| 3510 | wdev->iftype == NL80211_IFTYPE_P2P_GO; | 
|---|
| 3511 | } | 
|---|
| 3512 |  | 
|---|
| 3513 | static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev, | 
|---|
| 3514 | struct genl_info *info, bool monitor, | 
|---|
| 3515 | struct cfg80211_chan_def *chandef) | 
|---|
| 3516 | { | 
|---|
| 3517 | struct netlink_ext_ack *extack = info->extack; | 
|---|
| 3518 | struct nlattr **attrs = info->attrs; | 
|---|
| 3519 | u32 control_freq; | 
|---|
| 3520 |  | 
|---|
| 3521 | if (!attrs[NL80211_ATTR_WIPHY_FREQ]) { | 
|---|
| 3522 | NL_SET_ERR_MSG_ATTR(extack, attrs[NL80211_ATTR_WIPHY_FREQ], | 
|---|
| 3523 | "Frequency is missing"); | 
|---|
| 3524 | return -EINVAL; | 
|---|
| 3525 | } | 
|---|
| 3526 |  | 
|---|
| 3527 | control_freq = MHZ_TO_KHZ( | 
|---|
| 3528 | nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); | 
|---|
| 3529 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]) | 
|---|
| 3530 | control_freq += | 
|---|
| 3531 | nla_get_u32(nla: info->attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]); | 
|---|
| 3532 |  | 
|---|
| 3533 | memset(s: chandef, c: 0, n: sizeof(*chandef)); | 
|---|
| 3534 | chandef->chan = ieee80211_get_channel_khz(wiphy: &rdev->wiphy, freq: control_freq); | 
|---|
| 3535 | chandef->width = NL80211_CHAN_WIDTH_20_NOHT; | 
|---|
| 3536 | chandef->center_freq1 = KHZ_TO_MHZ(control_freq); | 
|---|
| 3537 | chandef->freq1_offset = control_freq % 1000; | 
|---|
| 3538 | chandef->center_freq2 = 0; | 
|---|
| 3539 | chandef->s1g_primary_2mhz = false; | 
|---|
| 3540 |  | 
|---|
| 3541 | if (!chandef->chan) { | 
|---|
| 3542 | NL_SET_ERR_MSG_ATTR(extack, attrs[NL80211_ATTR_WIPHY_FREQ], | 
|---|
| 3543 | "Unknown channel"); | 
|---|
| 3544 | return -EINVAL; | 
|---|
| 3545 | } | 
|---|
| 3546 |  | 
|---|
| 3547 | if (attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | 
|---|
| 3548 | enum nl80211_channel_type chantype; | 
|---|
| 3549 |  | 
|---|
| 3550 | chantype = nla_get_u32(nla: attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | 
|---|
| 3551 |  | 
|---|
| 3552 | switch (chantype) { | 
|---|
| 3553 | case NL80211_CHAN_NO_HT: | 
|---|
| 3554 | case NL80211_CHAN_HT20: | 
|---|
| 3555 | case NL80211_CHAN_HT40PLUS: | 
|---|
| 3556 | case NL80211_CHAN_HT40MINUS: | 
|---|
| 3557 | cfg80211_chandef_create(chandef, channel: chandef->chan, | 
|---|
| 3558 | chantype); | 
|---|
| 3559 | /* user input for center_freq is incorrect */ | 
|---|
| 3560 | if (attrs[NL80211_ATTR_CENTER_FREQ1] && | 
|---|
| 3561 | chandef->center_freq1 != nla_get_u32(nla: attrs[NL80211_ATTR_CENTER_FREQ1])) { | 
|---|
| 3562 | NL_SET_ERR_MSG_ATTR(extack, | 
|---|
| 3563 | attrs[NL80211_ATTR_CENTER_FREQ1], | 
|---|
| 3564 | "bad center frequency 1"); | 
|---|
| 3565 | return -EINVAL; | 
|---|
| 3566 | } | 
|---|
| 3567 | /* center_freq2 must be zero */ | 
|---|
| 3568 | if (attrs[NL80211_ATTR_CENTER_FREQ2] && | 
|---|
| 3569 | nla_get_u32(nla: attrs[NL80211_ATTR_CENTER_FREQ2])) { | 
|---|
| 3570 | NL_SET_ERR_MSG_ATTR(extack, | 
|---|
| 3571 | attrs[NL80211_ATTR_CENTER_FREQ2], | 
|---|
| 3572 | "center frequency 2 can't be used"); | 
|---|
| 3573 | return -EINVAL; | 
|---|
| 3574 | } | 
|---|
| 3575 | break; | 
|---|
| 3576 | default: | 
|---|
| 3577 | NL_SET_ERR_MSG_ATTR(extack, | 
|---|
| 3578 | attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE], | 
|---|
| 3579 | "invalid channel type"); | 
|---|
| 3580 | return -EINVAL; | 
|---|
| 3581 | } | 
|---|
| 3582 | } else if (attrs[NL80211_ATTR_CHANNEL_WIDTH]) { | 
|---|
| 3583 | chandef->width = nla_get_u32(nla: attrs[NL80211_ATTR_CHANNEL_WIDTH]); | 
|---|
| 3584 | if (attrs[NL80211_ATTR_CENTER_FREQ1]) { | 
|---|
| 3585 | chandef->center_freq1 = | 
|---|
| 3586 | nla_get_u32(nla: attrs[NL80211_ATTR_CENTER_FREQ1]); | 
|---|
| 3587 | chandef->freq1_offset = nla_get_u32_default( | 
|---|
| 3588 | nla: attrs[NL80211_ATTR_CENTER_FREQ1_OFFSET], defvalue: 0); | 
|---|
| 3589 | } | 
|---|
| 3590 |  | 
|---|
| 3591 | if (attrs[NL80211_ATTR_CENTER_FREQ2]) | 
|---|
| 3592 | chandef->center_freq2 = | 
|---|
| 3593 | nla_get_u32(nla: attrs[NL80211_ATTR_CENTER_FREQ2]); | 
|---|
| 3594 |  | 
|---|
| 3595 | chandef->s1g_primary_2mhz = nla_get_flag( | 
|---|
| 3596 | nla: attrs[NL80211_ATTR_S1G_PRIMARY_2MHZ]); | 
|---|
| 3597 | } | 
|---|
| 3598 |  | 
|---|
| 3599 | if (info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNELS]) { | 
|---|
| 3600 | chandef->edmg.channels = | 
|---|
| 3601 | nla_get_u8(nla: info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNELS]); | 
|---|
| 3602 |  | 
|---|
| 3603 | if (info->attrs[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG]) | 
|---|
| 3604 | chandef->edmg.bw_config = | 
|---|
| 3605 | nla_get_u8(nla: info->attrs[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG]); | 
|---|
| 3606 | } else { | 
|---|
| 3607 | chandef->edmg.bw_config = 0; | 
|---|
| 3608 | chandef->edmg.channels = 0; | 
|---|
| 3609 | } | 
|---|
| 3610 |  | 
|---|
| 3611 | if (info->attrs[NL80211_ATTR_PUNCT_BITMAP]) { | 
|---|
| 3612 | chandef->punctured = | 
|---|
| 3613 | nla_get_u32(nla: info->attrs[NL80211_ATTR_PUNCT_BITMAP]); | 
|---|
| 3614 |  | 
|---|
| 3615 | if (chandef->punctured && | 
|---|
| 3616 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 3617 | ftidx: NL80211_EXT_FEATURE_PUNCT)) { | 
|---|
| 3618 | NL_SET_ERR_MSG(extack, | 
|---|
| 3619 | "driver doesn't support puncturing"); | 
|---|
| 3620 | return -EINVAL; | 
|---|
| 3621 | } | 
|---|
| 3622 | } | 
|---|
| 3623 |  | 
|---|
| 3624 | if (!cfg80211_chandef_valid(chandef)) { | 
|---|
| 3625 | NL_SET_ERR_MSG(extack, "invalid channel definition"); | 
|---|
| 3626 | return -EINVAL; | 
|---|
| 3627 | } | 
|---|
| 3628 |  | 
|---|
| 3629 | if (!_cfg80211_chandef_usable(wiphy: &rdev->wiphy, chandef, | 
|---|
| 3630 | prohibited_flags: IEEE80211_CHAN_DISABLED, | 
|---|
| 3631 | permitting_flags: monitor ? IEEE80211_CHAN_CAN_MONITOR : 0)) { | 
|---|
| 3632 | NL_SET_ERR_MSG(extack, "(extension) channel is disabled"); | 
|---|
| 3633 | return -EINVAL; | 
|---|
| 3634 | } | 
|---|
| 3635 |  | 
|---|
| 3636 | if ((chandef->width == NL80211_CHAN_WIDTH_5 || | 
|---|
| 3637 | chandef->width == NL80211_CHAN_WIDTH_10) && | 
|---|
| 3638 | !(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ)) { | 
|---|
| 3639 | NL_SET_ERR_MSG(extack, "5/10 MHz not supported"); | 
|---|
| 3640 | return -EINVAL; | 
|---|
| 3641 | } | 
|---|
| 3642 |  | 
|---|
| 3643 | return 0; | 
|---|
| 3644 | } | 
|---|
| 3645 |  | 
|---|
| 3646 | int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, | 
|---|
| 3647 | struct genl_info *info, | 
|---|
| 3648 | struct cfg80211_chan_def *chandef) | 
|---|
| 3649 | { | 
|---|
| 3650 | return _nl80211_parse_chandef(rdev, info, monitor: false, chandef); | 
|---|
| 3651 | } | 
|---|
| 3652 |  | 
|---|
| 3653 | static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, | 
|---|
| 3654 | struct net_device *dev, | 
|---|
| 3655 | struct genl_info *info, | 
|---|
| 3656 | int _link_id) | 
|---|
| 3657 | { | 
|---|
| 3658 | struct cfg80211_chan_def chandef; | 
|---|
| 3659 | int result; | 
|---|
| 3660 | enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR; | 
|---|
| 3661 | struct wireless_dev *wdev = NULL; | 
|---|
| 3662 | int link_id = _link_id; | 
|---|
| 3663 |  | 
|---|
| 3664 | if (dev) | 
|---|
| 3665 | wdev = dev->ieee80211_ptr; | 
|---|
| 3666 | if (!nl80211_can_set_dev_channel(wdev)) | 
|---|
| 3667 | return -EOPNOTSUPP; | 
|---|
| 3668 | if (wdev) | 
|---|
| 3669 | iftype = wdev->iftype; | 
|---|
| 3670 |  | 
|---|
| 3671 | if (link_id < 0) { | 
|---|
| 3672 | if (wdev && wdev->valid_links) | 
|---|
| 3673 | return -EINVAL; | 
|---|
| 3674 | link_id = 0; | 
|---|
| 3675 | } | 
|---|
| 3676 |  | 
|---|
| 3677 | result = _nl80211_parse_chandef(rdev, info, | 
|---|
| 3678 | monitor: iftype == NL80211_IFTYPE_MONITOR, | 
|---|
| 3679 | chandef: &chandef); | 
|---|
| 3680 | if (result) | 
|---|
| 3681 | return result; | 
|---|
| 3682 |  | 
|---|
| 3683 | switch (iftype) { | 
|---|
| 3684 | case NL80211_IFTYPE_AP: | 
|---|
| 3685 | case NL80211_IFTYPE_P2P_GO: | 
|---|
| 3686 | if (!cfg80211_reg_can_beacon_relax(wiphy: &rdev->wiphy, chandef: &chandef, | 
|---|
| 3687 | iftype)) | 
|---|
| 3688 | return -EINVAL; | 
|---|
| 3689 | if (wdev->links[link_id].ap.beacon_interval) { | 
|---|
| 3690 | struct ieee80211_channel *cur_chan; | 
|---|
| 3691 |  | 
|---|
| 3692 | if (!dev || !rdev->ops->set_ap_chanwidth || | 
|---|
| 3693 | !(rdev->wiphy.features & | 
|---|
| 3694 | NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)) | 
|---|
| 3695 | return -EBUSY; | 
|---|
| 3696 |  | 
|---|
| 3697 | /* Only allow dynamic channel width changes */ | 
|---|
| 3698 | cur_chan = wdev->links[link_id].ap.chandef.chan; | 
|---|
| 3699 | if (chandef.chan != cur_chan) | 
|---|
| 3700 | return -EBUSY; | 
|---|
| 3701 |  | 
|---|
| 3702 | /* only allow this for regular channel widths */ | 
|---|
| 3703 | switch (wdev->links[link_id].ap.chandef.width) { | 
|---|
| 3704 | case NL80211_CHAN_WIDTH_20_NOHT: | 
|---|
| 3705 | case NL80211_CHAN_WIDTH_20: | 
|---|
| 3706 | case NL80211_CHAN_WIDTH_40: | 
|---|
| 3707 | case NL80211_CHAN_WIDTH_80: | 
|---|
| 3708 | case NL80211_CHAN_WIDTH_80P80: | 
|---|
| 3709 | case NL80211_CHAN_WIDTH_160: | 
|---|
| 3710 | case NL80211_CHAN_WIDTH_320: | 
|---|
| 3711 | break; | 
|---|
| 3712 | default: | 
|---|
| 3713 | return -EINVAL; | 
|---|
| 3714 | } | 
|---|
| 3715 |  | 
|---|
| 3716 | switch (chandef.width) { | 
|---|
| 3717 | case NL80211_CHAN_WIDTH_20_NOHT: | 
|---|
| 3718 | case NL80211_CHAN_WIDTH_20: | 
|---|
| 3719 | case NL80211_CHAN_WIDTH_40: | 
|---|
| 3720 | case NL80211_CHAN_WIDTH_80: | 
|---|
| 3721 | case NL80211_CHAN_WIDTH_80P80: | 
|---|
| 3722 | case NL80211_CHAN_WIDTH_160: | 
|---|
| 3723 | case NL80211_CHAN_WIDTH_320: | 
|---|
| 3724 | break; | 
|---|
| 3725 | default: | 
|---|
| 3726 | return -EINVAL; | 
|---|
| 3727 | } | 
|---|
| 3728 |  | 
|---|
| 3729 | result = rdev_set_ap_chanwidth(rdev, dev, link_id, | 
|---|
| 3730 | chandef: &chandef); | 
|---|
| 3731 | if (result) | 
|---|
| 3732 | return result; | 
|---|
| 3733 | wdev->links[link_id].ap.chandef = chandef; | 
|---|
| 3734 | } else { | 
|---|
| 3735 | wdev->u.ap.preset_chandef = chandef; | 
|---|
| 3736 | } | 
|---|
| 3737 | return 0; | 
|---|
| 3738 | case NL80211_IFTYPE_MESH_POINT: | 
|---|
| 3739 | return cfg80211_set_mesh_channel(rdev, wdev, chandef: &chandef); | 
|---|
| 3740 | case NL80211_IFTYPE_MONITOR: | 
|---|
| 3741 | return cfg80211_set_monitor_channel(rdev, dev, chandef: &chandef); | 
|---|
| 3742 | default: | 
|---|
| 3743 | break; | 
|---|
| 3744 | } | 
|---|
| 3745 |  | 
|---|
| 3746 | return -EINVAL; | 
|---|
| 3747 | } | 
|---|
| 3748 |  | 
|---|
| 3749 | static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 3750 | { | 
|---|
| 3751 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 3752 | int link_id = nl80211_link_id_or_invalid(attrs: info->attrs); | 
|---|
| 3753 | struct net_device *netdev = info->user_ptr[1]; | 
|---|
| 3754 |  | 
|---|
| 3755 | return __nl80211_set_channel(rdev, dev: netdev, info, link_id: link_id); | 
|---|
| 3756 | } | 
|---|
| 3757 |  | 
|---|
| 3758 | static int nl80211_set_wiphy_radio(struct genl_info *info, | 
|---|
| 3759 | struct cfg80211_registered_device *rdev, | 
|---|
| 3760 | int radio_idx) | 
|---|
| 3761 | { | 
|---|
| 3762 | u32 rts_threshold = 0, old_rts, changed = 0; | 
|---|
| 3763 | int result; | 
|---|
| 3764 |  | 
|---|
| 3765 | if (!rdev->ops->set_wiphy_params) | 
|---|
| 3766 | return -EOPNOTSUPP; | 
|---|
| 3767 |  | 
|---|
| 3768 | if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) { | 
|---|
| 3769 | rts_threshold = nla_get_u32( | 
|---|
| 3770 | nla: info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]); | 
|---|
| 3771 | changed |= WIPHY_PARAM_RTS_THRESHOLD; | 
|---|
| 3772 | } | 
|---|
| 3773 |  | 
|---|
| 3774 | old_rts = rdev->wiphy.radio_cfg[radio_idx].rts_threshold; | 
|---|
| 3775 |  | 
|---|
| 3776 | rdev->wiphy.radio_cfg[radio_idx].rts_threshold = rts_threshold; | 
|---|
| 3777 |  | 
|---|
| 3778 | result = rdev_set_wiphy_params(rdev, radio_idx, changed); | 
|---|
| 3779 | if (result) | 
|---|
| 3780 | rdev->wiphy.radio_cfg[radio_idx].rts_threshold = old_rts; | 
|---|
| 3781 |  | 
|---|
| 3782 | return 0; | 
|---|
| 3783 | } | 
|---|
| 3784 |  | 
|---|
| 3785 | static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 3786 | { | 
|---|
| 3787 | struct cfg80211_registered_device *rdev = NULL; | 
|---|
| 3788 | struct net_device *netdev = NULL; | 
|---|
| 3789 | struct wireless_dev *wdev; | 
|---|
| 3790 | int result = 0, rem_txq_params = 0; | 
|---|
| 3791 | struct nlattr *nl_txq_params; | 
|---|
| 3792 | u32 changed; | 
|---|
| 3793 | u8 retry_short = 0, retry_long = 0; | 
|---|
| 3794 | u32 frag_threshold = 0, rts_threshold = 0; | 
|---|
| 3795 | u8 coverage_class = 0; | 
|---|
| 3796 | u32 txq_limit = 0, txq_memory_limit = 0, txq_quantum = 0; | 
|---|
| 3797 | int radio_idx = -1; | 
|---|
| 3798 |  | 
|---|
| 3799 | rtnl_lock(); | 
|---|
| 3800 | /* | 
|---|
| 3801 | * Try to find the wiphy and netdev. Normally this | 
|---|
| 3802 | * function shouldn't need the netdev, but this is | 
|---|
| 3803 | * done for backward compatibility -- previously | 
|---|
| 3804 | * setting the channel was done per wiphy, but now | 
|---|
| 3805 | * it is per netdev. Previous userland like hostapd | 
|---|
| 3806 | * also passed a netdev to set_wiphy, so that it is | 
|---|
| 3807 | * possible to let that go to the right netdev! | 
|---|
| 3808 | */ | 
|---|
| 3809 |  | 
|---|
| 3810 | if (info->attrs[NL80211_ATTR_IFINDEX]) { | 
|---|
| 3811 | int ifindex = nla_get_u32(nla: info->attrs[NL80211_ATTR_IFINDEX]); | 
|---|
| 3812 |  | 
|---|
| 3813 | netdev = __dev_get_by_index(net: genl_info_net(info), ifindex); | 
|---|
| 3814 | if (netdev && netdev->ieee80211_ptr) | 
|---|
| 3815 | rdev = wiphy_to_rdev(wiphy: netdev->ieee80211_ptr->wiphy); | 
|---|
| 3816 | else | 
|---|
| 3817 | netdev = NULL; | 
|---|
| 3818 | } | 
|---|
| 3819 |  | 
|---|
| 3820 | if (!netdev) { | 
|---|
| 3821 | rdev = __cfg80211_rdev_from_attrs(netns: genl_info_net(info), | 
|---|
| 3822 | attrs: info->attrs); | 
|---|
| 3823 | if (IS_ERR(ptr: rdev)) { | 
|---|
| 3824 | rtnl_unlock(); | 
|---|
| 3825 | return PTR_ERR(ptr: rdev); | 
|---|
| 3826 | } | 
|---|
| 3827 | wdev = NULL; | 
|---|
| 3828 | netdev = NULL; | 
|---|
| 3829 | result = 0; | 
|---|
| 3830 | } else | 
|---|
| 3831 | wdev = netdev->ieee80211_ptr; | 
|---|
| 3832 |  | 
|---|
| 3833 | guard(wiphy)(T: &rdev->wiphy); | 
|---|
| 3834 |  | 
|---|
| 3835 | /* | 
|---|
| 3836 | * end workaround code, by now the rdev is available | 
|---|
| 3837 | * and locked, and wdev may or may not be NULL. | 
|---|
| 3838 | */ | 
|---|
| 3839 |  | 
|---|
| 3840 | if (info->attrs[NL80211_ATTR_WIPHY_NAME]) | 
|---|
| 3841 | result = cfg80211_dev_rename( | 
|---|
| 3842 | rdev, newname: nla_data(nla: info->attrs[NL80211_ATTR_WIPHY_NAME])); | 
|---|
| 3843 | rtnl_unlock(); | 
|---|
| 3844 |  | 
|---|
| 3845 | if (result) | 
|---|
| 3846 | return result; | 
|---|
| 3847 |  | 
|---|
| 3848 | if (info->attrs[NL80211_ATTR_WIPHY_RADIO_INDEX]) { | 
|---|
| 3849 | /* Radio idx is not expected for non-multi radio wiphy */ | 
|---|
| 3850 | if (rdev->wiphy.n_radio <= 0) | 
|---|
| 3851 | return -EINVAL; | 
|---|
| 3852 |  | 
|---|
| 3853 | radio_idx = nla_get_u8( | 
|---|
| 3854 | nla: info->attrs[NL80211_ATTR_WIPHY_RADIO_INDEX]); | 
|---|
| 3855 | if (radio_idx >= rdev->wiphy.n_radio) | 
|---|
| 3856 | return -EINVAL; | 
|---|
| 3857 |  | 
|---|
| 3858 | return nl80211_set_wiphy_radio(info, rdev, radio_idx); | 
|---|
| 3859 | } | 
|---|
| 3860 |  | 
|---|
| 3861 | if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) { | 
|---|
| 3862 | struct ieee80211_txq_params txq_params; | 
|---|
| 3863 | struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1]; | 
|---|
| 3864 |  | 
|---|
| 3865 | if (!rdev->ops->set_txq_params) | 
|---|
| 3866 | return -EOPNOTSUPP; | 
|---|
| 3867 |  | 
|---|
| 3868 | if (!netdev) | 
|---|
| 3869 | return -EINVAL; | 
|---|
| 3870 |  | 
|---|
| 3871 | if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | 
|---|
| 3872 | netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) | 
|---|
| 3873 | return -EINVAL; | 
|---|
| 3874 |  | 
|---|
| 3875 | if (!netif_running(dev: netdev)) | 
|---|
| 3876 | return -ENETDOWN; | 
|---|
| 3877 |  | 
|---|
| 3878 | nla_for_each_nested(nl_txq_params, | 
|---|
| 3879 | info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS], | 
|---|
| 3880 | rem_txq_params) { | 
|---|
| 3881 | result = nla_parse_nested_deprecated(tb, | 
|---|
| 3882 | maxtype: NL80211_TXQ_ATTR_MAX, | 
|---|
| 3883 | nla: nl_txq_params, | 
|---|
| 3884 | policy: txq_params_policy, | 
|---|
| 3885 | extack: info->extack); | 
|---|
| 3886 | if (result) | 
|---|
| 3887 | return result; | 
|---|
| 3888 |  | 
|---|
| 3889 | result = parse_txq_params(tb, txq_params: &txq_params); | 
|---|
| 3890 | if (result) | 
|---|
| 3891 | return result; | 
|---|
| 3892 |  | 
|---|
| 3893 | txq_params.link_id = | 
|---|
| 3894 | nl80211_link_id_or_invalid(attrs: info->attrs); | 
|---|
| 3895 |  | 
|---|
| 3896 | if (txq_params.link_id >= 0 && | 
|---|
| 3897 | !(netdev->ieee80211_ptr->valid_links & | 
|---|
| 3898 | BIT(txq_params.link_id))) | 
|---|
| 3899 | result = -ENOLINK; | 
|---|
| 3900 | else if (txq_params.link_id >= 0 && | 
|---|
| 3901 | !netdev->ieee80211_ptr->valid_links) | 
|---|
| 3902 | result = -EINVAL; | 
|---|
| 3903 | else | 
|---|
| 3904 | result = rdev_set_txq_params(rdev, dev: netdev, | 
|---|
| 3905 | params: &txq_params); | 
|---|
| 3906 | if (result) | 
|---|
| 3907 | return result; | 
|---|
| 3908 | } | 
|---|
| 3909 | } | 
|---|
| 3910 |  | 
|---|
| 3911 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { | 
|---|
| 3912 | int link_id = nl80211_link_id_or_invalid(attrs: info->attrs); | 
|---|
| 3913 |  | 
|---|
| 3914 | if (wdev) { | 
|---|
| 3915 | result = __nl80211_set_channel( | 
|---|
| 3916 | rdev, | 
|---|
| 3917 | dev: nl80211_can_set_dev_channel(wdev) ? netdev : NULL, | 
|---|
| 3918 | info, link_id: link_id); | 
|---|
| 3919 | } else { | 
|---|
| 3920 | result = __nl80211_set_channel(rdev, dev: netdev, info, link_id: link_id); | 
|---|
| 3921 | } | 
|---|
| 3922 |  | 
|---|
| 3923 | if (result) | 
|---|
| 3924 | return result; | 
|---|
| 3925 | } | 
|---|
| 3926 |  | 
|---|
| 3927 | if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) { | 
|---|
| 3928 | struct wireless_dev *txp_wdev = wdev; | 
|---|
| 3929 | enum nl80211_tx_power_setting type; | 
|---|
| 3930 | int idx, mbm = 0; | 
|---|
| 3931 |  | 
|---|
| 3932 | if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER)) | 
|---|
| 3933 | txp_wdev = NULL; | 
|---|
| 3934 |  | 
|---|
| 3935 | if (!rdev->ops->set_tx_power) | 
|---|
| 3936 | return -EOPNOTSUPP; | 
|---|
| 3937 |  | 
|---|
| 3938 | idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING; | 
|---|
| 3939 | type = nla_get_u32(nla: info->attrs[idx]); | 
|---|
| 3940 |  | 
|---|
| 3941 | if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] && | 
|---|
| 3942 | (type != NL80211_TX_POWER_AUTOMATIC)) | 
|---|
| 3943 | return -EINVAL; | 
|---|
| 3944 |  | 
|---|
| 3945 | if (type != NL80211_TX_POWER_AUTOMATIC) { | 
|---|
| 3946 | idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL; | 
|---|
| 3947 | mbm = nla_get_u32(nla: info->attrs[idx]); | 
|---|
| 3948 | } | 
|---|
| 3949 |  | 
|---|
| 3950 | result = rdev_set_tx_power(rdev, wdev: txp_wdev, radio_idx, type, | 
|---|
| 3951 | mbm); | 
|---|
| 3952 | if (result) | 
|---|
| 3953 | return result; | 
|---|
| 3954 | } | 
|---|
| 3955 |  | 
|---|
| 3956 | if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] && | 
|---|
| 3957 | info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) { | 
|---|
| 3958 | u32 tx_ant, rx_ant; | 
|---|
| 3959 |  | 
|---|
| 3960 | if ((!rdev->wiphy.available_antennas_tx && | 
|---|
| 3961 | !rdev->wiphy.available_antennas_rx) || | 
|---|
| 3962 | !rdev->ops->set_antenna) | 
|---|
| 3963 | return -EOPNOTSUPP; | 
|---|
| 3964 |  | 
|---|
| 3965 | tx_ant = nla_get_u32(nla: info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]); | 
|---|
| 3966 | rx_ant = nla_get_u32(nla: info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]); | 
|---|
| 3967 |  | 
|---|
| 3968 | /* reject antenna configurations which don't match the | 
|---|
| 3969 | * available antenna masks, except for the "all" mask */ | 
|---|
| 3970 | if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) || | 
|---|
| 3971 | (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) | 
|---|
| 3972 | return -EINVAL; | 
|---|
| 3973 |  | 
|---|
| 3974 | tx_ant = tx_ant & rdev->wiphy.available_antennas_tx; | 
|---|
| 3975 | rx_ant = rx_ant & rdev->wiphy.available_antennas_rx; | 
|---|
| 3976 |  | 
|---|
| 3977 | result = rdev_set_antenna(rdev, radio_idx, tx_ant, rx_ant); | 
|---|
| 3978 | if (result) | 
|---|
| 3979 | return result; | 
|---|
| 3980 | } | 
|---|
| 3981 |  | 
|---|
| 3982 | changed = 0; | 
|---|
| 3983 |  | 
|---|
| 3984 | if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) { | 
|---|
| 3985 | retry_short = nla_get_u8( | 
|---|
| 3986 | nla: info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]); | 
|---|
| 3987 |  | 
|---|
| 3988 | changed |= WIPHY_PARAM_RETRY_SHORT; | 
|---|
| 3989 | } | 
|---|
| 3990 |  | 
|---|
| 3991 | if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) { | 
|---|
| 3992 | retry_long = nla_get_u8( | 
|---|
| 3993 | nla: info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]); | 
|---|
| 3994 |  | 
|---|
| 3995 | changed |= WIPHY_PARAM_RETRY_LONG; | 
|---|
| 3996 | } | 
|---|
| 3997 |  | 
|---|
| 3998 | if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) { | 
|---|
| 3999 | frag_threshold = nla_get_u32( | 
|---|
| 4000 | nla: info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]); | 
|---|
| 4001 | if (frag_threshold < 256) | 
|---|
| 4002 | return -EINVAL; | 
|---|
| 4003 |  | 
|---|
| 4004 | if (frag_threshold != (u32) -1) { | 
|---|
| 4005 | /* | 
|---|
| 4006 | * Fragments (apart from the last one) are required to | 
|---|
| 4007 | * have even length. Make the fragmentation code | 
|---|
| 4008 | * simpler by stripping LSB should someone try to use | 
|---|
| 4009 | * odd threshold value. | 
|---|
| 4010 | */ | 
|---|
| 4011 | frag_threshold &= ~0x1; | 
|---|
| 4012 | } | 
|---|
| 4013 | changed |= WIPHY_PARAM_FRAG_THRESHOLD; | 
|---|
| 4014 | } | 
|---|
| 4015 |  | 
|---|
| 4016 | if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) { | 
|---|
| 4017 | rts_threshold = nla_get_u32( | 
|---|
| 4018 | nla: info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]); | 
|---|
| 4019 | changed |= WIPHY_PARAM_RTS_THRESHOLD; | 
|---|
| 4020 | } | 
|---|
| 4021 |  | 
|---|
| 4022 | if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) { | 
|---|
| 4023 | if (info->attrs[NL80211_ATTR_WIPHY_DYN_ACK]) | 
|---|
| 4024 | return -EINVAL; | 
|---|
| 4025 |  | 
|---|
| 4026 | coverage_class = nla_get_u8( | 
|---|
| 4027 | nla: info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]); | 
|---|
| 4028 | changed |= WIPHY_PARAM_COVERAGE_CLASS; | 
|---|
| 4029 | } | 
|---|
| 4030 |  | 
|---|
| 4031 | if (info->attrs[NL80211_ATTR_WIPHY_DYN_ACK]) { | 
|---|
| 4032 | if (!(rdev->wiphy.features & NL80211_FEATURE_ACKTO_ESTIMATION)) | 
|---|
| 4033 | return -EOPNOTSUPP; | 
|---|
| 4034 |  | 
|---|
| 4035 | changed |= WIPHY_PARAM_DYN_ACK; | 
|---|
| 4036 | } | 
|---|
| 4037 |  | 
|---|
| 4038 | if (info->attrs[NL80211_ATTR_TXQ_LIMIT]) { | 
|---|
| 4039 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 4040 | ftidx: NL80211_EXT_FEATURE_TXQS)) | 
|---|
| 4041 | return -EOPNOTSUPP; | 
|---|
| 4042 |  | 
|---|
| 4043 | txq_limit = nla_get_u32( | 
|---|
| 4044 | nla: info->attrs[NL80211_ATTR_TXQ_LIMIT]); | 
|---|
| 4045 | changed |= WIPHY_PARAM_TXQ_LIMIT; | 
|---|
| 4046 | } | 
|---|
| 4047 |  | 
|---|
| 4048 | if (info->attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT]) { | 
|---|
| 4049 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 4050 | ftidx: NL80211_EXT_FEATURE_TXQS)) | 
|---|
| 4051 | return -EOPNOTSUPP; | 
|---|
| 4052 |  | 
|---|
| 4053 | txq_memory_limit = nla_get_u32( | 
|---|
| 4054 | nla: info->attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT]); | 
|---|
| 4055 | changed |= WIPHY_PARAM_TXQ_MEMORY_LIMIT; | 
|---|
| 4056 | } | 
|---|
| 4057 |  | 
|---|
| 4058 | if (info->attrs[NL80211_ATTR_TXQ_QUANTUM]) { | 
|---|
| 4059 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 4060 | ftidx: NL80211_EXT_FEATURE_TXQS)) | 
|---|
| 4061 | return -EOPNOTSUPP; | 
|---|
| 4062 |  | 
|---|
| 4063 | txq_quantum = nla_get_u32( | 
|---|
| 4064 | nla: info->attrs[NL80211_ATTR_TXQ_QUANTUM]); | 
|---|
| 4065 | changed |= WIPHY_PARAM_TXQ_QUANTUM; | 
|---|
| 4066 | } | 
|---|
| 4067 |  | 
|---|
| 4068 | if (changed) { | 
|---|
| 4069 | u8 old_retry_short, old_retry_long; | 
|---|
| 4070 | u32 old_frag_threshold, old_rts_threshold; | 
|---|
| 4071 | u8 old_coverage_class, i; | 
|---|
| 4072 | u32 old_txq_limit, old_txq_memory_limit, old_txq_quantum; | 
|---|
| 4073 | u32 *old_radio_rts_threshold = NULL; | 
|---|
| 4074 |  | 
|---|
| 4075 | if (!rdev->ops->set_wiphy_params) | 
|---|
| 4076 | return -EOPNOTSUPP; | 
|---|
| 4077 |  | 
|---|
| 4078 | if (rdev->wiphy.n_radio) { | 
|---|
| 4079 | old_radio_rts_threshold = kcalloc(rdev->wiphy.n_radio, | 
|---|
| 4080 | sizeof(u32), | 
|---|
| 4081 | GFP_KERNEL); | 
|---|
| 4082 | if (!old_radio_rts_threshold) | 
|---|
| 4083 | return -ENOMEM; | 
|---|
| 4084 | } | 
|---|
| 4085 |  | 
|---|
| 4086 | old_retry_short = rdev->wiphy.retry_short; | 
|---|
| 4087 | old_retry_long = rdev->wiphy.retry_long; | 
|---|
| 4088 | old_frag_threshold = rdev->wiphy.frag_threshold; | 
|---|
| 4089 | old_rts_threshold = rdev->wiphy.rts_threshold; | 
|---|
| 4090 | if (old_radio_rts_threshold) { | 
|---|
| 4091 | for (i = 0 ; i < rdev->wiphy.n_radio; i++) | 
|---|
| 4092 | old_radio_rts_threshold[i] = | 
|---|
| 4093 | rdev->wiphy.radio_cfg[i].rts_threshold; | 
|---|
| 4094 | } | 
|---|
| 4095 | old_coverage_class = rdev->wiphy.coverage_class; | 
|---|
| 4096 | old_txq_limit = rdev->wiphy.txq_limit; | 
|---|
| 4097 | old_txq_memory_limit = rdev->wiphy.txq_memory_limit; | 
|---|
| 4098 | old_txq_quantum = rdev->wiphy.txq_quantum; | 
|---|
| 4099 |  | 
|---|
| 4100 | if (changed & WIPHY_PARAM_RETRY_SHORT) | 
|---|
| 4101 | rdev->wiphy.retry_short = retry_short; | 
|---|
| 4102 | if (changed & WIPHY_PARAM_RETRY_LONG) | 
|---|
| 4103 | rdev->wiphy.retry_long = retry_long; | 
|---|
| 4104 | if (changed & WIPHY_PARAM_FRAG_THRESHOLD) | 
|---|
| 4105 | rdev->wiphy.frag_threshold = frag_threshold; | 
|---|
| 4106 | if ((changed & WIPHY_PARAM_RTS_THRESHOLD) && | 
|---|
| 4107 | old_radio_rts_threshold) { | 
|---|
| 4108 | rdev->wiphy.rts_threshold = rts_threshold; | 
|---|
| 4109 | for (i = 0 ; i < rdev->wiphy.n_radio; i++) | 
|---|
| 4110 | rdev->wiphy.radio_cfg[i].rts_threshold = | 
|---|
| 4111 | rdev->wiphy.rts_threshold; | 
|---|
| 4112 | } | 
|---|
| 4113 | if (changed & WIPHY_PARAM_COVERAGE_CLASS) | 
|---|
| 4114 | rdev->wiphy.coverage_class = coverage_class; | 
|---|
| 4115 | if (changed & WIPHY_PARAM_TXQ_LIMIT) | 
|---|
| 4116 | rdev->wiphy.txq_limit = txq_limit; | 
|---|
| 4117 | if (changed & WIPHY_PARAM_TXQ_MEMORY_LIMIT) | 
|---|
| 4118 | rdev->wiphy.txq_memory_limit = txq_memory_limit; | 
|---|
| 4119 | if (changed & WIPHY_PARAM_TXQ_QUANTUM) | 
|---|
| 4120 | rdev->wiphy.txq_quantum = txq_quantum; | 
|---|
| 4121 |  | 
|---|
| 4122 | result = rdev_set_wiphy_params(rdev, radio_idx, changed); | 
|---|
| 4123 | if (result) { | 
|---|
| 4124 | rdev->wiphy.retry_short = old_retry_short; | 
|---|
| 4125 | rdev->wiphy.retry_long = old_retry_long; | 
|---|
| 4126 | rdev->wiphy.frag_threshold = old_frag_threshold; | 
|---|
| 4127 | rdev->wiphy.rts_threshold = old_rts_threshold; | 
|---|
| 4128 | if (old_radio_rts_threshold) { | 
|---|
| 4129 | for (i = 0 ; i < rdev->wiphy.n_radio; i++) | 
|---|
| 4130 | rdev->wiphy.radio_cfg[i].rts_threshold = | 
|---|
| 4131 | old_radio_rts_threshold[i]; | 
|---|
| 4132 | } | 
|---|
| 4133 | rdev->wiphy.coverage_class = old_coverage_class; | 
|---|
| 4134 | rdev->wiphy.txq_limit = old_txq_limit; | 
|---|
| 4135 | rdev->wiphy.txq_memory_limit = old_txq_memory_limit; | 
|---|
| 4136 | rdev->wiphy.txq_quantum = old_txq_quantum; | 
|---|
| 4137 | } | 
|---|
| 4138 |  | 
|---|
| 4139 | if (old_rts_threshold) | 
|---|
| 4140 | kfree(objp: old_radio_rts_threshold); | 
|---|
| 4141 | return result; | 
|---|
| 4142 | } | 
|---|
| 4143 |  | 
|---|
| 4144 | return 0; | 
|---|
| 4145 | } | 
|---|
| 4146 |  | 
|---|
| 4147 | int nl80211_send_chandef(struct sk_buff *msg, const struct cfg80211_chan_def *chandef) | 
|---|
| 4148 | { | 
|---|
| 4149 | if (WARN_ON(!cfg80211_chandef_valid(chandef))) | 
|---|
| 4150 | return -EINVAL; | 
|---|
| 4151 |  | 
|---|
| 4152 | if (nla_put_u32(skb: msg, NL80211_ATTR_WIPHY_FREQ, | 
|---|
| 4153 | value: chandef->chan->center_freq)) | 
|---|
| 4154 | return -ENOBUFS; | 
|---|
| 4155 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY_FREQ_OFFSET, | 
|---|
| 4156 | value: chandef->chan->freq_offset)) | 
|---|
| 4157 | return -ENOBUFS; | 
|---|
| 4158 | switch (chandef->width) { | 
|---|
| 4159 | case NL80211_CHAN_WIDTH_20_NOHT: | 
|---|
| 4160 | case NL80211_CHAN_WIDTH_20: | 
|---|
| 4161 | case NL80211_CHAN_WIDTH_40: | 
|---|
| 4162 | if (nla_put_u32(skb: msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, | 
|---|
| 4163 | value: cfg80211_get_chandef_type(chandef))) | 
|---|
| 4164 | return -ENOBUFS; | 
|---|
| 4165 | break; | 
|---|
| 4166 | default: | 
|---|
| 4167 | break; | 
|---|
| 4168 | } | 
|---|
| 4169 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CHANNEL_WIDTH, value: chandef->width)) | 
|---|
| 4170 | return -ENOBUFS; | 
|---|
| 4171 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CENTER_FREQ1, value: chandef->center_freq1)) | 
|---|
| 4172 | return -ENOBUFS; | 
|---|
| 4173 | if (chandef->center_freq2 && | 
|---|
| 4174 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CENTER_FREQ2, value: chandef->center_freq2)) | 
|---|
| 4175 | return -ENOBUFS; | 
|---|
| 4176 | if (chandef->punctured && | 
|---|
| 4177 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_PUNCT_BITMAP, value: chandef->punctured)) | 
|---|
| 4178 | return -ENOBUFS; | 
|---|
| 4179 |  | 
|---|
| 4180 | return 0; | 
|---|
| 4181 | } | 
|---|
| 4182 | EXPORT_SYMBOL(nl80211_send_chandef); | 
|---|
| 4183 |  | 
|---|
| 4184 | static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags, | 
|---|
| 4185 | struct cfg80211_registered_device *rdev, | 
|---|
| 4186 | struct wireless_dev *wdev, | 
|---|
| 4187 | enum nl80211_commands cmd) | 
|---|
| 4188 | { | 
|---|
| 4189 | struct net_device *dev = wdev->netdev; | 
|---|
| 4190 | void *hdr; | 
|---|
| 4191 |  | 
|---|
| 4192 | lockdep_assert_wiphy(&rdev->wiphy); | 
|---|
| 4193 |  | 
|---|
| 4194 | WARN_ON(cmd != NL80211_CMD_NEW_INTERFACE && | 
|---|
| 4195 | cmd != NL80211_CMD_DEL_INTERFACE && | 
|---|
| 4196 | cmd != NL80211_CMD_SET_INTERFACE); | 
|---|
| 4197 |  | 
|---|
| 4198 | hdr = nl80211hdr_put(skb: msg, portid, seq, flags, cmd); | 
|---|
| 4199 | if (!hdr) | 
|---|
| 4200 | return -1; | 
|---|
| 4201 |  | 
|---|
| 4202 | if (dev && | 
|---|
| 4203 | (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || | 
|---|
| 4204 | nla_put_string(skb: msg, attrtype: NL80211_ATTR_IFNAME, str: dev->name))) | 
|---|
| 4205 | goto nla_put_failure; | 
|---|
| 4206 |  | 
|---|
| 4207 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 4208 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFTYPE, value: wdev->iftype) || | 
|---|
| 4209 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), | 
|---|
| 4210 | padattr: NL80211_ATTR_PAD) || | 
|---|
| 4211 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: wdev_address(wdev)) || | 
|---|
| 4212 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_GENERATION, | 
|---|
| 4213 | value: rdev->devlist_generation ^ | 
|---|
| 4214 | (cfg80211_rdev_list_generation << 2)) || | 
|---|
| 4215 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_4ADDR, value: wdev->use_4addr) || | 
|---|
| 4216 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_VIF_RADIO_MASK, value: wdev->radio_mask)) | 
|---|
| 4217 | goto nla_put_failure; | 
|---|
| 4218 |  | 
|---|
| 4219 | if (rdev->ops->get_channel && !wdev->valid_links) { | 
|---|
| 4220 | struct cfg80211_chan_def chandef = {}; | 
|---|
| 4221 | int ret; | 
|---|
| 4222 |  | 
|---|
| 4223 | ret = rdev_get_channel(rdev, wdev, link_id: 0, chandef: &chandef); | 
|---|
| 4224 | if (ret == 0 && nl80211_send_chandef(msg, &chandef)) | 
|---|
| 4225 | goto nla_put_failure; | 
|---|
| 4226 | } | 
|---|
| 4227 |  | 
|---|
| 4228 | if (rdev->ops->get_tx_power && !wdev->valid_links) { | 
|---|
| 4229 | int dbm, ret; | 
|---|
| 4230 |  | 
|---|
| 4231 | ret = rdev_get_tx_power(rdev, wdev, radio_idx: -1, link_id: 0, dbm: &dbm); | 
|---|
| 4232 | if (ret == 0 && | 
|---|
| 4233 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY_TX_POWER_LEVEL, | 
|---|
| 4234 | DBM_TO_MBM(dbm))) | 
|---|
| 4235 | goto nla_put_failure; | 
|---|
| 4236 | } | 
|---|
| 4237 |  | 
|---|
| 4238 | switch (wdev->iftype) { | 
|---|
| 4239 | case NL80211_IFTYPE_AP: | 
|---|
| 4240 | case NL80211_IFTYPE_P2P_GO: | 
|---|
| 4241 | if (wdev->u.ap.ssid_len && | 
|---|
| 4242 | nla_put(skb: msg, NL80211_ATTR_SSID, attrlen: wdev->u.ap.ssid_len, | 
|---|
| 4243 | data: wdev->u.ap.ssid)) | 
|---|
| 4244 | goto nla_put_failure; | 
|---|
| 4245 | break; | 
|---|
| 4246 | case NL80211_IFTYPE_STATION: | 
|---|
| 4247 | case NL80211_IFTYPE_P2P_CLIENT: | 
|---|
| 4248 | if (wdev->u.client.ssid_len && | 
|---|
| 4249 | nla_put(skb: msg, NL80211_ATTR_SSID, attrlen: wdev->u.client.ssid_len, | 
|---|
| 4250 | data: wdev->u.client.ssid)) | 
|---|
| 4251 | goto nla_put_failure; | 
|---|
| 4252 | break; | 
|---|
| 4253 | case NL80211_IFTYPE_ADHOC: | 
|---|
| 4254 | if (wdev->u.ibss.ssid_len && | 
|---|
| 4255 | nla_put(skb: msg, NL80211_ATTR_SSID, attrlen: wdev->u.ibss.ssid_len, | 
|---|
| 4256 | data: wdev->u.ibss.ssid)) | 
|---|
| 4257 | goto nla_put_failure; | 
|---|
| 4258 | break; | 
|---|
| 4259 | default: | 
|---|
| 4260 | /* nothing */ | 
|---|
| 4261 | break; | 
|---|
| 4262 | } | 
|---|
| 4263 |  | 
|---|
| 4264 | if (rdev->ops->get_txq_stats) { | 
|---|
| 4265 | struct cfg80211_txq_stats txqstats = {}; | 
|---|
| 4266 | int ret = rdev_get_txq_stats(rdev, wdev, txqstats: &txqstats); | 
|---|
| 4267 |  | 
|---|
| 4268 | if (ret == 0 && | 
|---|
| 4269 | !nl80211_put_txq_stats(msg, txqstats: &txqstats, | 
|---|
| 4270 | attrtype: NL80211_ATTR_TXQ_STATS)) | 
|---|
| 4271 | goto nla_put_failure; | 
|---|
| 4272 | } | 
|---|
| 4273 |  | 
|---|
| 4274 | if (wdev->valid_links) { | 
|---|
| 4275 | unsigned int link_id; | 
|---|
| 4276 | struct nlattr *links = nla_nest_start(skb: msg, | 
|---|
| 4277 | attrtype: NL80211_ATTR_MLO_LINKS); | 
|---|
| 4278 |  | 
|---|
| 4279 | if (!links) | 
|---|
| 4280 | goto nla_put_failure; | 
|---|
| 4281 |  | 
|---|
| 4282 | for_each_valid_link(wdev, link_id) { | 
|---|
| 4283 | struct nlattr *link = nla_nest_start(skb: msg, attrtype: link_id + 1); | 
|---|
| 4284 | struct cfg80211_chan_def chandef = {}; | 
|---|
| 4285 | int ret; | 
|---|
| 4286 |  | 
|---|
| 4287 | if (!link) | 
|---|
| 4288 | goto nla_put_failure; | 
|---|
| 4289 |  | 
|---|
| 4290 | if (nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: link_id)) | 
|---|
| 4291 | goto nla_put_failure; | 
|---|
| 4292 | if (nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, | 
|---|
| 4293 | data: wdev->links[link_id].addr)) | 
|---|
| 4294 | goto nla_put_failure; | 
|---|
| 4295 |  | 
|---|
| 4296 | ret = rdev_get_channel(rdev, wdev, link_id, chandef: &chandef); | 
|---|
| 4297 | if (ret == 0 && nl80211_send_chandef(msg, &chandef)) | 
|---|
| 4298 | goto nla_put_failure; | 
|---|
| 4299 |  | 
|---|
| 4300 | if (rdev->ops->get_tx_power) { | 
|---|
| 4301 | int dbm, ret; | 
|---|
| 4302 |  | 
|---|
| 4303 | ret = rdev_get_tx_power(rdev, wdev, radio_idx: -1, link_id, dbm: &dbm); | 
|---|
| 4304 | if (ret == 0 && | 
|---|
| 4305 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY_TX_POWER_LEVEL, | 
|---|
| 4306 | DBM_TO_MBM(dbm))) | 
|---|
| 4307 | goto nla_put_failure; | 
|---|
| 4308 | } | 
|---|
| 4309 | nla_nest_end(skb: msg, start: link); | 
|---|
| 4310 | } | 
|---|
| 4311 |  | 
|---|
| 4312 | nla_nest_end(skb: msg, start: links); | 
|---|
| 4313 | } | 
|---|
| 4314 |  | 
|---|
| 4315 | genlmsg_end(skb: msg, hdr); | 
|---|
| 4316 | return 0; | 
|---|
| 4317 |  | 
|---|
| 4318 | nla_put_failure: | 
|---|
| 4319 | genlmsg_cancel(skb: msg, hdr); | 
|---|
| 4320 | return -EMSGSIZE; | 
|---|
| 4321 | } | 
|---|
| 4322 |  | 
|---|
| 4323 | static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb) | 
|---|
| 4324 | { | 
|---|
| 4325 | int wp_idx = 0; | 
|---|
| 4326 | int if_idx = 0; | 
|---|
| 4327 | int wp_start = cb->args[0]; | 
|---|
| 4328 | int if_start = cb->args[1]; | 
|---|
| 4329 | int filter_wiphy = -1; | 
|---|
| 4330 | struct cfg80211_registered_device *rdev; | 
|---|
| 4331 | struct wireless_dev *wdev; | 
|---|
| 4332 | int ret; | 
|---|
| 4333 |  | 
|---|
| 4334 | rtnl_lock(); | 
|---|
| 4335 | if (!cb->args[2]) { | 
|---|
| 4336 | struct nl80211_dump_wiphy_state state = { | 
|---|
| 4337 | .filter_wiphy = -1, | 
|---|
| 4338 | }; | 
|---|
| 4339 |  | 
|---|
| 4340 | ret = nl80211_dump_wiphy_parse(skb, cb, state: &state); | 
|---|
| 4341 | if (ret) | 
|---|
| 4342 | goto out_unlock; | 
|---|
| 4343 |  | 
|---|
| 4344 | filter_wiphy = state.filter_wiphy; | 
|---|
| 4345 |  | 
|---|
| 4346 | /* | 
|---|
| 4347 | * if filtering, set cb->args[2] to +1 since 0 is the default | 
|---|
| 4348 | * value needed to determine that parsing is necessary. | 
|---|
| 4349 | */ | 
|---|
| 4350 | if (filter_wiphy >= 0) | 
|---|
| 4351 | cb->args[2] = filter_wiphy + 1; | 
|---|
| 4352 | else | 
|---|
| 4353 | cb->args[2] = -1; | 
|---|
| 4354 | } else if (cb->args[2] > 0) { | 
|---|
| 4355 | filter_wiphy = cb->args[2] - 1; | 
|---|
| 4356 | } | 
|---|
| 4357 |  | 
|---|
| 4358 | for_each_rdev(rdev) { | 
|---|
| 4359 | if (!net_eq(net1: wiphy_net(wiphy: &rdev->wiphy), net2: sock_net(sk: skb->sk))) | 
|---|
| 4360 | continue; | 
|---|
| 4361 | if (wp_idx < wp_start) { | 
|---|
| 4362 | wp_idx++; | 
|---|
| 4363 | continue; | 
|---|
| 4364 | } | 
|---|
| 4365 |  | 
|---|
| 4366 | if (filter_wiphy >= 0 && filter_wiphy != rdev->wiphy_idx) | 
|---|
| 4367 | continue; | 
|---|
| 4368 |  | 
|---|
| 4369 | if_idx = 0; | 
|---|
| 4370 |  | 
|---|
| 4371 | guard(wiphy)(T: &rdev->wiphy); | 
|---|
| 4372 |  | 
|---|
| 4373 | list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { | 
|---|
| 4374 | if (if_idx < if_start) { | 
|---|
| 4375 | if_idx++; | 
|---|
| 4376 | continue; | 
|---|
| 4377 | } | 
|---|
| 4378 |  | 
|---|
| 4379 | if (nl80211_send_iface(msg: skb, NETLINK_CB(cb->skb).portid, | 
|---|
| 4380 | seq: cb->nlh->nlmsg_seq, NLM_F_MULTI, | 
|---|
| 4381 | rdev, wdev, | 
|---|
| 4382 | cmd: NL80211_CMD_NEW_INTERFACE) < 0) | 
|---|
| 4383 | goto out; | 
|---|
| 4384 |  | 
|---|
| 4385 | if_idx++; | 
|---|
| 4386 | } | 
|---|
| 4387 |  | 
|---|
| 4388 | if_start = 0; | 
|---|
| 4389 | wp_idx++; | 
|---|
| 4390 | } | 
|---|
| 4391 | out: | 
|---|
| 4392 | cb->args[0] = wp_idx; | 
|---|
| 4393 | cb->args[1] = if_idx; | 
|---|
| 4394 |  | 
|---|
| 4395 | ret = skb->len; | 
|---|
| 4396 | out_unlock: | 
|---|
| 4397 | rtnl_unlock(); | 
|---|
| 4398 |  | 
|---|
| 4399 | return ret; | 
|---|
| 4400 | } | 
|---|
| 4401 |  | 
|---|
| 4402 | static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 4403 | { | 
|---|
| 4404 | struct sk_buff *msg; | 
|---|
| 4405 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 4406 | struct wireless_dev *wdev = info->user_ptr[1]; | 
|---|
| 4407 |  | 
|---|
| 4408 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 4409 | if (!msg) | 
|---|
| 4410 | return -ENOMEM; | 
|---|
| 4411 |  | 
|---|
| 4412 | if (nl80211_send_iface(msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, | 
|---|
| 4413 | rdev, wdev, cmd: NL80211_CMD_NEW_INTERFACE) < 0) { | 
|---|
| 4414 | nlmsg_free(skb: msg); | 
|---|
| 4415 | return -ENOBUFS; | 
|---|
| 4416 | } | 
|---|
| 4417 |  | 
|---|
| 4418 | return genlmsg_reply(skb: msg, info); | 
|---|
| 4419 | } | 
|---|
| 4420 |  | 
|---|
| 4421 | static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = { | 
|---|
| 4422 | [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG }, | 
|---|
| 4423 | [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG }, | 
|---|
| 4424 | [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG }, | 
|---|
| 4425 | [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG }, | 
|---|
| 4426 | [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG }, | 
|---|
| 4427 | [NL80211_MNTR_FLAG_ACTIVE] = { .type = NLA_FLAG }, | 
|---|
| 4428 | [NL80211_MNTR_FLAG_SKIP_TX] = { .type = NLA_FLAG }, | 
|---|
| 4429 | }; | 
|---|
| 4430 |  | 
|---|
| 4431 | static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) | 
|---|
| 4432 | { | 
|---|
| 4433 | struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1]; | 
|---|
| 4434 | int flag; | 
|---|
| 4435 |  | 
|---|
| 4436 | *mntrflags = 0; | 
|---|
| 4437 |  | 
|---|
| 4438 | if (!nla) | 
|---|
| 4439 | return -EINVAL; | 
|---|
| 4440 |  | 
|---|
| 4441 | if (nla_parse_nested_deprecated(tb: flags, maxtype: NL80211_MNTR_FLAG_MAX, nla, policy: mntr_flags_policy, NULL)) | 
|---|
| 4442 | return -EINVAL; | 
|---|
| 4443 |  | 
|---|
| 4444 | for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++) | 
|---|
| 4445 | if (flags[flag]) | 
|---|
| 4446 | *mntrflags |= (1<<flag); | 
|---|
| 4447 |  | 
|---|
| 4448 | /* cooked monitor mode is incompatible with other modes */ | 
|---|
| 4449 | if (*mntrflags & MONITOR_FLAG_COOK_FRAMES && | 
|---|
| 4450 | *mntrflags != MONITOR_FLAG_COOK_FRAMES) | 
|---|
| 4451 | return -EOPNOTSUPP; | 
|---|
| 4452 |  | 
|---|
| 4453 | *mntrflags |= MONITOR_FLAG_CHANGED; | 
|---|
| 4454 |  | 
|---|
| 4455 | return 0; | 
|---|
| 4456 | } | 
|---|
| 4457 |  | 
|---|
| 4458 | static int nl80211_parse_mon_options(struct cfg80211_registered_device *rdev, | 
|---|
| 4459 | enum nl80211_iftype type, | 
|---|
| 4460 | struct genl_info *info, | 
|---|
| 4461 | struct vif_params *params) | 
|---|
| 4462 | { | 
|---|
| 4463 | bool change = false; | 
|---|
| 4464 | int err; | 
|---|
| 4465 |  | 
|---|
| 4466 | if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { | 
|---|
| 4467 | if (type != NL80211_IFTYPE_MONITOR) | 
|---|
| 4468 | return -EINVAL; | 
|---|
| 4469 |  | 
|---|
| 4470 | err = parse_monitor_flags(nla: info->attrs[NL80211_ATTR_MNTR_FLAGS], | 
|---|
| 4471 | mntrflags: ¶ms->flags); | 
|---|
| 4472 | if (err) | 
|---|
| 4473 | return err; | 
|---|
| 4474 |  | 
|---|
| 4475 | change = true; | 
|---|
| 4476 | } | 
|---|
| 4477 |  | 
|---|
| 4478 | /* MONITOR_FLAG_COOK_FRAMES is deprecated, refuse cooperation */ | 
|---|
| 4479 | if (params->flags & MONITOR_FLAG_COOK_FRAMES) | 
|---|
| 4480 | return -EOPNOTSUPP; | 
|---|
| 4481 |  | 
|---|
| 4482 | if (params->flags & MONITOR_FLAG_ACTIVE && | 
|---|
| 4483 | !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR)) | 
|---|
| 4484 | return -EOPNOTSUPP; | 
|---|
| 4485 |  | 
|---|
| 4486 | if (info->attrs[NL80211_ATTR_MU_MIMO_GROUP_DATA]) { | 
|---|
| 4487 | const u8 *mumimo_groups; | 
|---|
| 4488 | u32 cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER; | 
|---|
| 4489 |  | 
|---|
| 4490 | if (type != NL80211_IFTYPE_MONITOR) | 
|---|
| 4491 | return -EINVAL; | 
|---|
| 4492 |  | 
|---|
| 4493 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, ftidx: cap_flag)) | 
|---|
| 4494 | return -EOPNOTSUPP; | 
|---|
| 4495 |  | 
|---|
| 4496 | mumimo_groups = | 
|---|
| 4497 | nla_data(nla: info->attrs[NL80211_ATTR_MU_MIMO_GROUP_DATA]); | 
|---|
| 4498 |  | 
|---|
| 4499 | /* bits 0 and 63 are reserved and must be zero */ | 
|---|
| 4500 | if ((mumimo_groups[0] & BIT(0)) || | 
|---|
| 4501 | (mumimo_groups[VHT_MUMIMO_GROUPS_DATA_LEN - 1] & BIT(7))) | 
|---|
| 4502 | return -EINVAL; | 
|---|
| 4503 |  | 
|---|
| 4504 | params->vht_mumimo_groups = mumimo_groups; | 
|---|
| 4505 | change = true; | 
|---|
| 4506 | } | 
|---|
| 4507 |  | 
|---|
| 4508 | if (info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR]) { | 
|---|
| 4509 | u32 cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER; | 
|---|
| 4510 |  | 
|---|
| 4511 | if (type != NL80211_IFTYPE_MONITOR) | 
|---|
| 4512 | return -EINVAL; | 
|---|
| 4513 |  | 
|---|
| 4514 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, ftidx: cap_flag)) | 
|---|
| 4515 | return -EOPNOTSUPP; | 
|---|
| 4516 |  | 
|---|
| 4517 | params->vht_mumimo_follow_addr = | 
|---|
| 4518 | nla_data(nla: info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR]); | 
|---|
| 4519 | change = true; | 
|---|
| 4520 | } | 
|---|
| 4521 |  | 
|---|
| 4522 | return change ? 1 : 0; | 
|---|
| 4523 | } | 
|---|
| 4524 |  | 
|---|
| 4525 | static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev, | 
|---|
| 4526 | struct net_device *netdev, u8 use_4addr, | 
|---|
| 4527 | enum nl80211_iftype iftype) | 
|---|
| 4528 | { | 
|---|
| 4529 | if (!use_4addr) { | 
|---|
| 4530 | if (netdev && netif_is_bridge_port(dev: netdev)) | 
|---|
| 4531 | return -EBUSY; | 
|---|
| 4532 | return 0; | 
|---|
| 4533 | } | 
|---|
| 4534 |  | 
|---|
| 4535 | switch (iftype) { | 
|---|
| 4536 | case NL80211_IFTYPE_AP_VLAN: | 
|---|
| 4537 | if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP) | 
|---|
| 4538 | return 0; | 
|---|
| 4539 | break; | 
|---|
| 4540 | case NL80211_IFTYPE_STATION: | 
|---|
| 4541 | if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION) | 
|---|
| 4542 | return 0; | 
|---|
| 4543 | break; | 
|---|
| 4544 | default: | 
|---|
| 4545 | break; | 
|---|
| 4546 | } | 
|---|
| 4547 |  | 
|---|
| 4548 | return -EOPNOTSUPP; | 
|---|
| 4549 | } | 
|---|
| 4550 |  | 
|---|
| 4551 | static int nl80211_parse_vif_radio_mask(struct genl_info *info, | 
|---|
| 4552 | u32 *radio_mask) | 
|---|
| 4553 | { | 
|---|
| 4554 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 4555 | struct nlattr *attr = info->attrs[NL80211_ATTR_VIF_RADIO_MASK]; | 
|---|
| 4556 | u32 mask, allowed; | 
|---|
| 4557 |  | 
|---|
| 4558 | if (!attr) { | 
|---|
| 4559 | *radio_mask = 0; | 
|---|
| 4560 | return 0; | 
|---|
| 4561 | } | 
|---|
| 4562 |  | 
|---|
| 4563 | allowed = BIT(rdev->wiphy.n_radio) - 1; | 
|---|
| 4564 | mask = nla_get_u32(nla: attr); | 
|---|
| 4565 | if (mask & ~allowed) | 
|---|
| 4566 | return -EINVAL; | 
|---|
| 4567 | if (!mask) | 
|---|
| 4568 | mask = allowed; | 
|---|
| 4569 | *radio_mask = mask; | 
|---|
| 4570 |  | 
|---|
| 4571 | return 1; | 
|---|
| 4572 | } | 
|---|
| 4573 |  | 
|---|
| 4574 | static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 4575 | { | 
|---|
| 4576 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 4577 | struct vif_params params; | 
|---|
| 4578 | int err; | 
|---|
| 4579 | enum nl80211_iftype otype, ntype; | 
|---|
| 4580 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 4581 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 4582 | u32 radio_mask = 0; | 
|---|
| 4583 | bool change = false; | 
|---|
| 4584 |  | 
|---|
| 4585 | memset(s: ¶ms, c: 0, n: sizeof(params)); | 
|---|
| 4586 |  | 
|---|
| 4587 | otype = ntype = dev->ieee80211_ptr->iftype; | 
|---|
| 4588 |  | 
|---|
| 4589 | if (info->attrs[NL80211_ATTR_IFTYPE]) { | 
|---|
| 4590 | ntype = nla_get_u32(nla: info->attrs[NL80211_ATTR_IFTYPE]); | 
|---|
| 4591 | if (otype != ntype) | 
|---|
| 4592 | change = true; | 
|---|
| 4593 | } | 
|---|
| 4594 |  | 
|---|
| 4595 | if (info->attrs[NL80211_ATTR_MESH_ID]) { | 
|---|
| 4596 | if (ntype != NL80211_IFTYPE_MESH_POINT) | 
|---|
| 4597 | return -EINVAL; | 
|---|
| 4598 | if (otype != NL80211_IFTYPE_MESH_POINT) | 
|---|
| 4599 | return -EINVAL; | 
|---|
| 4600 | if (netif_running(dev)) | 
|---|
| 4601 | return -EBUSY; | 
|---|
| 4602 |  | 
|---|
| 4603 | wdev->u.mesh.id_up_len = | 
|---|
| 4604 | nla_len(nla: info->attrs[NL80211_ATTR_MESH_ID]); | 
|---|
| 4605 | memcpy(to: wdev->u.mesh.id, | 
|---|
| 4606 | from: nla_data(nla: info->attrs[NL80211_ATTR_MESH_ID]), | 
|---|
| 4607 | len: wdev->u.mesh.id_up_len); | 
|---|
| 4608 | } | 
|---|
| 4609 |  | 
|---|
| 4610 | if (info->attrs[NL80211_ATTR_4ADDR]) { | 
|---|
| 4611 | params.use_4addr = !!nla_get_u8(nla: info->attrs[NL80211_ATTR_4ADDR]); | 
|---|
| 4612 | change = true; | 
|---|
| 4613 | err = nl80211_valid_4addr(rdev, netdev: dev, use_4addr: params.use_4addr, iftype: ntype); | 
|---|
| 4614 | if (err) | 
|---|
| 4615 | return err; | 
|---|
| 4616 | } else { | 
|---|
| 4617 | params.use_4addr = -1; | 
|---|
| 4618 | } | 
|---|
| 4619 |  | 
|---|
| 4620 | err = nl80211_parse_mon_options(rdev, type: ntype, info, params: ¶ms); | 
|---|
| 4621 | if (err < 0) | 
|---|
| 4622 | return err; | 
|---|
| 4623 | if (err > 0) | 
|---|
| 4624 | change = true; | 
|---|
| 4625 |  | 
|---|
| 4626 | err = nl80211_parse_vif_radio_mask(info, radio_mask: &radio_mask); | 
|---|
| 4627 | if (err < 0) | 
|---|
| 4628 | return err; | 
|---|
| 4629 | if (err && netif_running(dev)) | 
|---|
| 4630 | return -EBUSY; | 
|---|
| 4631 |  | 
|---|
| 4632 | if (change) | 
|---|
| 4633 | err = cfg80211_change_iface(rdev, dev, ntype, params: ¶ms); | 
|---|
| 4634 | else | 
|---|
| 4635 | err = 0; | 
|---|
| 4636 |  | 
|---|
| 4637 | if (!err && params.use_4addr != -1) | 
|---|
| 4638 | dev->ieee80211_ptr->use_4addr = params.use_4addr; | 
|---|
| 4639 |  | 
|---|
| 4640 | if (radio_mask) | 
|---|
| 4641 | wdev->radio_mask = radio_mask; | 
|---|
| 4642 |  | 
|---|
| 4643 | if (change && !err) | 
|---|
| 4644 | nl80211_notify_iface(rdev, wdev, cmd: NL80211_CMD_SET_INTERFACE); | 
|---|
| 4645 |  | 
|---|
| 4646 | return err; | 
|---|
| 4647 | } | 
|---|
| 4648 |  | 
|---|
| 4649 | static int _nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 4650 | { | 
|---|
| 4651 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 4652 | struct vif_params params; | 
|---|
| 4653 | struct wireless_dev *wdev; | 
|---|
| 4654 | struct sk_buff *msg; | 
|---|
| 4655 | u32 radio_mask; | 
|---|
| 4656 | int err; | 
|---|
| 4657 | enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; | 
|---|
| 4658 |  | 
|---|
| 4659 | memset(s: ¶ms, c: 0, n: sizeof(params)); | 
|---|
| 4660 |  | 
|---|
| 4661 | if (!info->attrs[NL80211_ATTR_IFNAME]) | 
|---|
| 4662 | return -EINVAL; | 
|---|
| 4663 |  | 
|---|
| 4664 | if (info->attrs[NL80211_ATTR_IFTYPE]) | 
|---|
| 4665 | type = nla_get_u32(nla: info->attrs[NL80211_ATTR_IFTYPE]); | 
|---|
| 4666 |  | 
|---|
| 4667 | if (!rdev->ops->add_virtual_intf) | 
|---|
| 4668 | return -EOPNOTSUPP; | 
|---|
| 4669 |  | 
|---|
| 4670 | if ((type == NL80211_IFTYPE_P2P_DEVICE || type == NL80211_IFTYPE_NAN || | 
|---|
| 4671 | rdev->wiphy.features & NL80211_FEATURE_MAC_ON_CREATE) && | 
|---|
| 4672 | info->attrs[NL80211_ATTR_MAC]) { | 
|---|
| 4673 | nla_memcpy(dest: params.macaddr, src: info->attrs[NL80211_ATTR_MAC], | 
|---|
| 4674 | ETH_ALEN); | 
|---|
| 4675 | if (!is_valid_ether_addr(addr: params.macaddr)) | 
|---|
| 4676 | return -EADDRNOTAVAIL; | 
|---|
| 4677 | } | 
|---|
| 4678 |  | 
|---|
| 4679 | if (info->attrs[NL80211_ATTR_4ADDR]) { | 
|---|
| 4680 | params.use_4addr = !!nla_get_u8(nla: info->attrs[NL80211_ATTR_4ADDR]); | 
|---|
| 4681 | err = nl80211_valid_4addr(rdev, NULL, use_4addr: params.use_4addr, iftype: type); | 
|---|
| 4682 | if (err) | 
|---|
| 4683 | return err; | 
|---|
| 4684 | } | 
|---|
| 4685 |  | 
|---|
| 4686 | if (!cfg80211_iftype_allowed(wiphy: &rdev->wiphy, iftype: type, is_4addr: params.use_4addr, check_swif: 0)) | 
|---|
| 4687 | return -EOPNOTSUPP; | 
|---|
| 4688 |  | 
|---|
| 4689 | err = nl80211_parse_mon_options(rdev, type, info, params: ¶ms); | 
|---|
| 4690 | if (err < 0) | 
|---|
| 4691 | return err; | 
|---|
| 4692 |  | 
|---|
| 4693 | err = nl80211_parse_vif_radio_mask(info, radio_mask: &radio_mask); | 
|---|
| 4694 | if (err < 0) | 
|---|
| 4695 | return err; | 
|---|
| 4696 |  | 
|---|
| 4697 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 4698 | if (!msg) | 
|---|
| 4699 | return -ENOMEM; | 
|---|
| 4700 |  | 
|---|
| 4701 | wdev = rdev_add_virtual_intf(rdev, | 
|---|
| 4702 | name: nla_data(nla: info->attrs[NL80211_ATTR_IFNAME]), | 
|---|
| 4703 | NET_NAME_USER, type, params: ¶ms); | 
|---|
| 4704 | if (WARN_ON(!wdev)) { | 
|---|
| 4705 | nlmsg_free(skb: msg); | 
|---|
| 4706 | return -EPROTO; | 
|---|
| 4707 | } else if (IS_ERR(ptr: wdev)) { | 
|---|
| 4708 | nlmsg_free(skb: msg); | 
|---|
| 4709 | return PTR_ERR(ptr: wdev); | 
|---|
| 4710 | } | 
|---|
| 4711 |  | 
|---|
| 4712 | if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) | 
|---|
| 4713 | wdev->owner_nlportid = info->snd_portid; | 
|---|
| 4714 |  | 
|---|
| 4715 | switch (type) { | 
|---|
| 4716 | case NL80211_IFTYPE_MESH_POINT: | 
|---|
| 4717 | if (!info->attrs[NL80211_ATTR_MESH_ID]) | 
|---|
| 4718 | break; | 
|---|
| 4719 | wdev->u.mesh.id_up_len = | 
|---|
| 4720 | nla_len(nla: info->attrs[NL80211_ATTR_MESH_ID]); | 
|---|
| 4721 | memcpy(to: wdev->u.mesh.id, | 
|---|
| 4722 | from: nla_data(nla: info->attrs[NL80211_ATTR_MESH_ID]), | 
|---|
| 4723 | len: wdev->u.mesh.id_up_len); | 
|---|
| 4724 | break; | 
|---|
| 4725 | case NL80211_IFTYPE_NAN: | 
|---|
| 4726 | case NL80211_IFTYPE_P2P_DEVICE: | 
|---|
| 4727 | /* | 
|---|
| 4728 | * P2P Device and NAN do not have a netdev, so don't go | 
|---|
| 4729 | * through the netdev notifier and must be added here | 
|---|
| 4730 | */ | 
|---|
| 4731 | cfg80211_init_wdev(wdev); | 
|---|
| 4732 | cfg80211_register_wdev(rdev, wdev); | 
|---|
| 4733 | break; | 
|---|
| 4734 | default: | 
|---|
| 4735 | break; | 
|---|
| 4736 | } | 
|---|
| 4737 |  | 
|---|
| 4738 | if (radio_mask) | 
|---|
| 4739 | wdev->radio_mask = radio_mask; | 
|---|
| 4740 |  | 
|---|
| 4741 | if (nl80211_send_iface(msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, | 
|---|
| 4742 | rdev, wdev, cmd: NL80211_CMD_NEW_INTERFACE) < 0) { | 
|---|
| 4743 | nlmsg_free(skb: msg); | 
|---|
| 4744 | return -ENOBUFS; | 
|---|
| 4745 | } | 
|---|
| 4746 |  | 
|---|
| 4747 | return genlmsg_reply(skb: msg, info); | 
|---|
| 4748 | } | 
|---|
| 4749 |  | 
|---|
| 4750 | static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 4751 | { | 
|---|
| 4752 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 4753 |  | 
|---|
| 4754 | /* to avoid failing a new interface creation due to pending removal */ | 
|---|
| 4755 | cfg80211_destroy_ifaces(rdev); | 
|---|
| 4756 |  | 
|---|
| 4757 | guard(wiphy)(T: &rdev->wiphy); | 
|---|
| 4758 |  | 
|---|
| 4759 | return _nl80211_new_interface(skb, info); | 
|---|
| 4760 | } | 
|---|
| 4761 |  | 
|---|
| 4762 | static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 4763 | { | 
|---|
| 4764 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 4765 | struct wireless_dev *wdev = info->user_ptr[1]; | 
|---|
| 4766 |  | 
|---|
| 4767 | if (!rdev->ops->del_virtual_intf) | 
|---|
| 4768 | return -EOPNOTSUPP; | 
|---|
| 4769 |  | 
|---|
| 4770 | /* | 
|---|
| 4771 | * We hold RTNL, so this is safe, without RTNL opencount cannot | 
|---|
| 4772 | * reach 0, and thus the rdev cannot be deleted. | 
|---|
| 4773 | * | 
|---|
| 4774 | * We need to do it for the dev_close(), since that will call | 
|---|
| 4775 | * the netdev notifiers, and we need to acquire the mutex there | 
|---|
| 4776 | * but don't know if we get there from here or from some other | 
|---|
| 4777 | * place (e.g. "ip link set ... down"). | 
|---|
| 4778 | */ | 
|---|
| 4779 | mutex_unlock(lock: &rdev->wiphy.mtx); | 
|---|
| 4780 |  | 
|---|
| 4781 | /* | 
|---|
| 4782 | * If we remove a wireless device without a netdev then clear | 
|---|
| 4783 | * user_ptr[1] so that nl80211_post_doit won't dereference it | 
|---|
| 4784 | * to check if it needs to do dev_put(). Otherwise it crashes | 
|---|
| 4785 | * since the wdev has been freed, unlike with a netdev where | 
|---|
| 4786 | * we need the dev_put() for the netdev to really be freed. | 
|---|
| 4787 | */ | 
|---|
| 4788 | if (!wdev->netdev) | 
|---|
| 4789 | info->user_ptr[1] = NULL; | 
|---|
| 4790 | else | 
|---|
| 4791 | dev_close(dev: wdev->netdev); | 
|---|
| 4792 |  | 
|---|
| 4793 | mutex_lock(lock: &rdev->wiphy.mtx); | 
|---|
| 4794 |  | 
|---|
| 4795 | return cfg80211_remove_virtual_intf(rdev, wdev); | 
|---|
| 4796 | } | 
|---|
| 4797 |  | 
|---|
| 4798 | static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 4799 | { | 
|---|
| 4800 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 4801 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 4802 | u16 noack_map; | 
|---|
| 4803 |  | 
|---|
| 4804 | if (!info->attrs[NL80211_ATTR_NOACK_MAP]) | 
|---|
| 4805 | return -EINVAL; | 
|---|
| 4806 |  | 
|---|
| 4807 | if (!rdev->ops->set_noack_map) | 
|---|
| 4808 | return -EOPNOTSUPP; | 
|---|
| 4809 |  | 
|---|
| 4810 | noack_map = nla_get_u16(nla: info->attrs[NL80211_ATTR_NOACK_MAP]); | 
|---|
| 4811 |  | 
|---|
| 4812 | return rdev_set_noack_map(rdev, dev, noack_map); | 
|---|
| 4813 | } | 
|---|
| 4814 |  | 
|---|
| 4815 | static int nl80211_validate_key_link_id(struct genl_info *info, | 
|---|
| 4816 | struct wireless_dev *wdev, | 
|---|
| 4817 | int link_id, bool pairwise) | 
|---|
| 4818 | { | 
|---|
| 4819 | if (pairwise) { | 
|---|
| 4820 | if (link_id != -1) { | 
|---|
| 4821 | GENL_SET_ERR_MSG(info, | 
|---|
| 4822 | "link ID not allowed for pairwise key"); | 
|---|
| 4823 | return -EINVAL; | 
|---|
| 4824 | } | 
|---|
| 4825 |  | 
|---|
| 4826 | return 0; | 
|---|
| 4827 | } | 
|---|
| 4828 |  | 
|---|
| 4829 | if (wdev->valid_links) { | 
|---|
| 4830 | if (link_id == -1) { | 
|---|
| 4831 | GENL_SET_ERR_MSG(info, | 
|---|
| 4832 | "link ID must for MLO group key"); | 
|---|
| 4833 | return -EINVAL; | 
|---|
| 4834 | } | 
|---|
| 4835 | if (!(wdev->valid_links & BIT(link_id))) { | 
|---|
| 4836 | GENL_SET_ERR_MSG(info, "invalid link ID for MLO group key"); | 
|---|
| 4837 | return -EINVAL; | 
|---|
| 4838 | } | 
|---|
| 4839 | } else if (link_id != -1) { | 
|---|
| 4840 | GENL_SET_ERR_MSG(info, "link ID not allowed for non-MLO group key"); | 
|---|
| 4841 | return -EINVAL; | 
|---|
| 4842 | } | 
|---|
| 4843 |  | 
|---|
| 4844 | return 0; | 
|---|
| 4845 | } | 
|---|
| 4846 |  | 
|---|
| 4847 | struct get_key_cookie { | 
|---|
| 4848 | struct sk_buff *msg; | 
|---|
| 4849 | int error; | 
|---|
| 4850 | int idx; | 
|---|
| 4851 | }; | 
|---|
| 4852 |  | 
|---|
| 4853 | static void get_key_callback(void *c, struct key_params *params) | 
|---|
| 4854 | { | 
|---|
| 4855 | struct nlattr *key; | 
|---|
| 4856 | struct get_key_cookie *cookie = c; | 
|---|
| 4857 |  | 
|---|
| 4858 | if ((params->seq && | 
|---|
| 4859 | nla_put(skb: cookie->msg, attrtype: NL80211_ATTR_KEY_SEQ, | 
|---|
| 4860 | attrlen: params->seq_len, data: params->seq)) || | 
|---|
| 4861 | (params->cipher && | 
|---|
| 4862 | nla_put_u32(skb: cookie->msg, attrtype: NL80211_ATTR_KEY_CIPHER, | 
|---|
| 4863 | value: params->cipher))) | 
|---|
| 4864 | goto nla_put_failure; | 
|---|
| 4865 |  | 
|---|
| 4866 | key = nla_nest_start_noflag(skb: cookie->msg, NL80211_ATTR_KEY); | 
|---|
| 4867 | if (!key) | 
|---|
| 4868 | goto nla_put_failure; | 
|---|
| 4869 |  | 
|---|
| 4870 | if ((params->seq && | 
|---|
| 4871 | nla_put(skb: cookie->msg, attrtype: NL80211_KEY_SEQ, | 
|---|
| 4872 | attrlen: params->seq_len, data: params->seq)) || | 
|---|
| 4873 | (params->cipher && | 
|---|
| 4874 | nla_put_u32(skb: cookie->msg, attrtype: NL80211_KEY_CIPHER, | 
|---|
| 4875 | value: params->cipher))) | 
|---|
| 4876 | goto nla_put_failure; | 
|---|
| 4877 |  | 
|---|
| 4878 | if (nla_put_u8(skb: cookie->msg, attrtype: NL80211_KEY_IDX, value: cookie->idx)) | 
|---|
| 4879 | goto nla_put_failure; | 
|---|
| 4880 |  | 
|---|
| 4881 | nla_nest_end(skb: cookie->msg, start: key); | 
|---|
| 4882 |  | 
|---|
| 4883 | return; | 
|---|
| 4884 | nla_put_failure: | 
|---|
| 4885 | cookie->error = 1; | 
|---|
| 4886 | } | 
|---|
| 4887 |  | 
|---|
| 4888 | static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 4889 | { | 
|---|
| 4890 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 4891 | int err; | 
|---|
| 4892 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 4893 | u8 key_idx = 0; | 
|---|
| 4894 | const u8 *mac_addr = NULL; | 
|---|
| 4895 | bool pairwise; | 
|---|
| 4896 | struct get_key_cookie cookie = { | 
|---|
| 4897 | .error = 0, | 
|---|
| 4898 | }; | 
|---|
| 4899 | void *hdr; | 
|---|
| 4900 | struct sk_buff *msg; | 
|---|
| 4901 | bool bigtk_support = false; | 
|---|
| 4902 | int link_id = nl80211_link_id_or_invalid(attrs: info->attrs); | 
|---|
| 4903 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 4904 |  | 
|---|
| 4905 | if (wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 4906 | ftidx: NL80211_EXT_FEATURE_BEACON_PROTECTION)) | 
|---|
| 4907 | bigtk_support = true; | 
|---|
| 4908 |  | 
|---|
| 4909 | if ((wdev->iftype == NL80211_IFTYPE_STATION || | 
|---|
| 4910 | wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) && | 
|---|
| 4911 | wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 4912 | ftidx: NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT)) | 
|---|
| 4913 | bigtk_support = true; | 
|---|
| 4914 |  | 
|---|
| 4915 | if (info->attrs[NL80211_ATTR_KEY_IDX]) { | 
|---|
| 4916 | key_idx = nla_get_u8(nla: info->attrs[NL80211_ATTR_KEY_IDX]); | 
|---|
| 4917 |  | 
|---|
| 4918 | if (key_idx >= 6 && key_idx <= 7 && !bigtk_support) { | 
|---|
| 4919 | GENL_SET_ERR_MSG(info, "BIGTK not supported"); | 
|---|
| 4920 | return -EINVAL; | 
|---|
| 4921 | } | 
|---|
| 4922 | } | 
|---|
| 4923 |  | 
|---|
| 4924 | if (info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 4925 | mac_addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 4926 |  | 
|---|
| 4927 | pairwise = !!mac_addr; | 
|---|
| 4928 | if (info->attrs[NL80211_ATTR_KEY_TYPE]) { | 
|---|
| 4929 | u32 kt = nla_get_u32(nla: info->attrs[NL80211_ATTR_KEY_TYPE]); | 
|---|
| 4930 |  | 
|---|
| 4931 | if (kt != NL80211_KEYTYPE_GROUP && | 
|---|
| 4932 | kt != NL80211_KEYTYPE_PAIRWISE) | 
|---|
| 4933 | return -EINVAL; | 
|---|
| 4934 | pairwise = kt == NL80211_KEYTYPE_PAIRWISE; | 
|---|
| 4935 | } | 
|---|
| 4936 |  | 
|---|
| 4937 | if (!rdev->ops->get_key) | 
|---|
| 4938 | return -EOPNOTSUPP; | 
|---|
| 4939 |  | 
|---|
| 4940 | if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) | 
|---|
| 4941 | return -ENOENT; | 
|---|
| 4942 |  | 
|---|
| 4943 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 4944 | if (!msg) | 
|---|
| 4945 | return -ENOMEM; | 
|---|
| 4946 |  | 
|---|
| 4947 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, | 
|---|
| 4948 | cmd: NL80211_CMD_NEW_KEY); | 
|---|
| 4949 | if (!hdr) | 
|---|
| 4950 | goto nla_put_failure; | 
|---|
| 4951 |  | 
|---|
| 4952 | cookie.msg = msg; | 
|---|
| 4953 | cookie.idx = key_idx; | 
|---|
| 4954 |  | 
|---|
| 4955 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || | 
|---|
| 4956 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_KEY_IDX, value: key_idx)) | 
|---|
| 4957 | goto nla_put_failure; | 
|---|
| 4958 | if (mac_addr && | 
|---|
| 4959 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: mac_addr)) | 
|---|
| 4960 | goto nla_put_failure; | 
|---|
| 4961 |  | 
|---|
| 4962 | err = nl80211_validate_key_link_id(info, wdev, link_id, pairwise); | 
|---|
| 4963 | if (err) | 
|---|
| 4964 | goto free_msg; | 
|---|
| 4965 |  | 
|---|
| 4966 | err = rdev_get_key(rdev, netdev: dev, link_id, key_index: key_idx, pairwise, mac_addr, | 
|---|
| 4967 | cookie: &cookie, callback: get_key_callback); | 
|---|
| 4968 |  | 
|---|
| 4969 | if (err) | 
|---|
| 4970 | goto free_msg; | 
|---|
| 4971 |  | 
|---|
| 4972 | if (cookie.error) | 
|---|
| 4973 | goto nla_put_failure; | 
|---|
| 4974 |  | 
|---|
| 4975 | genlmsg_end(skb: msg, hdr); | 
|---|
| 4976 | return genlmsg_reply(skb: msg, info); | 
|---|
| 4977 |  | 
|---|
| 4978 | nla_put_failure: | 
|---|
| 4979 | err = -ENOBUFS; | 
|---|
| 4980 | free_msg: | 
|---|
| 4981 | nlmsg_free(skb: msg); | 
|---|
| 4982 | return err; | 
|---|
| 4983 | } | 
|---|
| 4984 |  | 
|---|
| 4985 | static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 4986 | { | 
|---|
| 4987 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 4988 | struct key_parse key; | 
|---|
| 4989 | int err; | 
|---|
| 4990 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 4991 | int link_id = nl80211_link_id_or_invalid(attrs: info->attrs); | 
|---|
| 4992 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 4993 |  | 
|---|
| 4994 | err = nl80211_parse_key(info, k: &key); | 
|---|
| 4995 | if (err) | 
|---|
| 4996 | return err; | 
|---|
| 4997 |  | 
|---|
| 4998 | if (key.idx < 0) | 
|---|
| 4999 | return -EINVAL; | 
|---|
| 5000 |  | 
|---|
| 5001 | /* Only support setting default key and | 
|---|
| 5002 | * Extended Key ID action NL80211_KEY_SET_TX. | 
|---|
| 5003 | */ | 
|---|
| 5004 | if (!key.def && !key.defmgmt && !key.defbeacon && | 
|---|
| 5005 | !(key.p.mode == NL80211_KEY_SET_TX)) | 
|---|
| 5006 | return -EINVAL; | 
|---|
| 5007 |  | 
|---|
| 5008 | if (key.def) { | 
|---|
| 5009 | if (!rdev->ops->set_default_key) | 
|---|
| 5010 | return -EOPNOTSUPP; | 
|---|
| 5011 |  | 
|---|
| 5012 | err = nl80211_key_allowed(wdev); | 
|---|
| 5013 | if (err) | 
|---|
| 5014 | return err; | 
|---|
| 5015 |  | 
|---|
| 5016 | err = nl80211_validate_key_link_id(info, wdev, link_id, pairwise: false); | 
|---|
| 5017 | if (err) | 
|---|
| 5018 | return err; | 
|---|
| 5019 |  | 
|---|
| 5020 | err = rdev_set_default_key(rdev, netdev: dev, link_id, key_index: key.idx, | 
|---|
| 5021 | unicast: key.def_uni, multicast: key.def_multi); | 
|---|
| 5022 |  | 
|---|
| 5023 | if (err) | 
|---|
| 5024 | return err; | 
|---|
| 5025 |  | 
|---|
| 5026 | #ifdef CONFIG_CFG80211_WEXT | 
|---|
| 5027 | wdev->wext.default_key = key.idx; | 
|---|
| 5028 | #endif | 
|---|
| 5029 | return 0; | 
|---|
| 5030 | } else if (key.defmgmt) { | 
|---|
| 5031 | if (key.def_uni || !key.def_multi) | 
|---|
| 5032 | return -EINVAL; | 
|---|
| 5033 |  | 
|---|
| 5034 | if (!rdev->ops->set_default_mgmt_key) | 
|---|
| 5035 | return -EOPNOTSUPP; | 
|---|
| 5036 |  | 
|---|
| 5037 | err = nl80211_key_allowed(wdev); | 
|---|
| 5038 | if (err) | 
|---|
| 5039 | return err; | 
|---|
| 5040 |  | 
|---|
| 5041 | err = nl80211_validate_key_link_id(info, wdev, link_id, pairwise: false); | 
|---|
| 5042 | if (err) | 
|---|
| 5043 | return err; | 
|---|
| 5044 |  | 
|---|
| 5045 | err = rdev_set_default_mgmt_key(rdev, netdev: dev, link_id, key_index: key.idx); | 
|---|
| 5046 | if (err) | 
|---|
| 5047 | return err; | 
|---|
| 5048 |  | 
|---|
| 5049 | #ifdef CONFIG_CFG80211_WEXT | 
|---|
| 5050 | wdev->wext.default_mgmt_key = key.idx; | 
|---|
| 5051 | #endif | 
|---|
| 5052 | return 0; | 
|---|
| 5053 | } else if (key.defbeacon) { | 
|---|
| 5054 | if (key.def_uni || !key.def_multi) | 
|---|
| 5055 | return -EINVAL; | 
|---|
| 5056 |  | 
|---|
| 5057 | if (!rdev->ops->set_default_beacon_key) | 
|---|
| 5058 | return -EOPNOTSUPP; | 
|---|
| 5059 |  | 
|---|
| 5060 | err = nl80211_key_allowed(wdev); | 
|---|
| 5061 | if (err) | 
|---|
| 5062 | return err; | 
|---|
| 5063 |  | 
|---|
| 5064 | err = nl80211_validate_key_link_id(info, wdev, link_id, pairwise: false); | 
|---|
| 5065 | if (err) | 
|---|
| 5066 | return err; | 
|---|
| 5067 |  | 
|---|
| 5068 | return rdev_set_default_beacon_key(rdev, netdev: dev, link_id, key_index: key.idx); | 
|---|
| 5069 | } else if (key.p.mode == NL80211_KEY_SET_TX && | 
|---|
| 5070 | wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 5071 | ftidx: NL80211_EXT_FEATURE_EXT_KEY_ID)) { | 
|---|
| 5072 | u8 *mac_addr = NULL; | 
|---|
| 5073 |  | 
|---|
| 5074 | if (info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 5075 | mac_addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 5076 |  | 
|---|
| 5077 | if (!mac_addr || key.idx < 0 || key.idx > 1) | 
|---|
| 5078 | return -EINVAL; | 
|---|
| 5079 |  | 
|---|
| 5080 | err = nl80211_validate_key_link_id(info, wdev, link_id, pairwise: true); | 
|---|
| 5081 | if (err) | 
|---|
| 5082 | return err; | 
|---|
| 5083 |  | 
|---|
| 5084 | return rdev_add_key(rdev, netdev: dev, link_id, key_index: key.idx, | 
|---|
| 5085 | pairwise: NL80211_KEYTYPE_PAIRWISE, | 
|---|
| 5086 | mac_addr, params: &key.p); | 
|---|
| 5087 | } | 
|---|
| 5088 |  | 
|---|
| 5089 | return -EINVAL; | 
|---|
| 5090 | } | 
|---|
| 5091 |  | 
|---|
| 5092 | static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 5093 | { | 
|---|
| 5094 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 5095 | int err; | 
|---|
| 5096 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 5097 | struct key_parse key; | 
|---|
| 5098 | const u8 *mac_addr = NULL; | 
|---|
| 5099 | int link_id = nl80211_link_id_or_invalid(attrs: info->attrs); | 
|---|
| 5100 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 5101 |  | 
|---|
| 5102 | err = nl80211_parse_key(info, k: &key); | 
|---|
| 5103 | if (err) | 
|---|
| 5104 | return err; | 
|---|
| 5105 |  | 
|---|
| 5106 | if (!key.p.key) { | 
|---|
| 5107 | GENL_SET_ERR_MSG(info, "no key"); | 
|---|
| 5108 | return -EINVAL; | 
|---|
| 5109 | } | 
|---|
| 5110 |  | 
|---|
| 5111 | if (info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 5112 | mac_addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 5113 |  | 
|---|
| 5114 | if (key.type == -1) { | 
|---|
| 5115 | if (mac_addr) | 
|---|
| 5116 | key.type = NL80211_KEYTYPE_PAIRWISE; | 
|---|
| 5117 | else | 
|---|
| 5118 | key.type = NL80211_KEYTYPE_GROUP; | 
|---|
| 5119 | } | 
|---|
| 5120 |  | 
|---|
| 5121 | /* for now */ | 
|---|
| 5122 | if (key.type != NL80211_KEYTYPE_PAIRWISE && | 
|---|
| 5123 | key.type != NL80211_KEYTYPE_GROUP) { | 
|---|
| 5124 | GENL_SET_ERR_MSG(info, "key type not pairwise or group"); | 
|---|
| 5125 | return -EINVAL; | 
|---|
| 5126 | } | 
|---|
| 5127 |  | 
|---|
| 5128 | if (key.type == NL80211_KEYTYPE_GROUP && | 
|---|
| 5129 | info->attrs[NL80211_ATTR_VLAN_ID]) | 
|---|
| 5130 | key.p.vlan_id = nla_get_u16(nla: info->attrs[NL80211_ATTR_VLAN_ID]); | 
|---|
| 5131 |  | 
|---|
| 5132 | if (!rdev->ops->add_key) | 
|---|
| 5133 | return -EOPNOTSUPP; | 
|---|
| 5134 |  | 
|---|
| 5135 | if (cfg80211_validate_key_settings(rdev, params: &key.p, key_idx: key.idx, | 
|---|
| 5136 | pairwise: key.type == NL80211_KEYTYPE_PAIRWISE, | 
|---|
| 5137 | mac_addr)) { | 
|---|
| 5138 | GENL_SET_ERR_MSG(info, "key setting validation failed"); | 
|---|
| 5139 | return -EINVAL; | 
|---|
| 5140 | } | 
|---|
| 5141 |  | 
|---|
| 5142 | err = nl80211_key_allowed(wdev); | 
|---|
| 5143 | if (err) | 
|---|
| 5144 | GENL_SET_ERR_MSG(info, "key not allowed"); | 
|---|
| 5145 |  | 
|---|
| 5146 | if (!err) | 
|---|
| 5147 | err = nl80211_validate_key_link_id(info, wdev, link_id, | 
|---|
| 5148 | pairwise: key.type == NL80211_KEYTYPE_PAIRWISE); | 
|---|
| 5149 |  | 
|---|
| 5150 | if (!err) { | 
|---|
| 5151 | err = rdev_add_key(rdev, netdev: dev, link_id, key_index: key.idx, | 
|---|
| 5152 | pairwise: key.type == NL80211_KEYTYPE_PAIRWISE, | 
|---|
| 5153 | mac_addr, params: &key.p); | 
|---|
| 5154 | if (err) | 
|---|
| 5155 | GENL_SET_ERR_MSG(info, "key addition failed"); | 
|---|
| 5156 | } | 
|---|
| 5157 |  | 
|---|
| 5158 | return err; | 
|---|
| 5159 | } | 
|---|
| 5160 |  | 
|---|
| 5161 | static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 5162 | { | 
|---|
| 5163 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 5164 | int err; | 
|---|
| 5165 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 5166 | u8 *mac_addr = NULL; | 
|---|
| 5167 | struct key_parse key; | 
|---|
| 5168 | int link_id = nl80211_link_id_or_invalid(attrs: info->attrs); | 
|---|
| 5169 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 5170 |  | 
|---|
| 5171 | err = nl80211_parse_key(info, k: &key); | 
|---|
| 5172 | if (err) | 
|---|
| 5173 | return err; | 
|---|
| 5174 |  | 
|---|
| 5175 | if (info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 5176 | mac_addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 5177 |  | 
|---|
| 5178 | if (key.type == -1) { | 
|---|
| 5179 | if (mac_addr) | 
|---|
| 5180 | key.type = NL80211_KEYTYPE_PAIRWISE; | 
|---|
| 5181 | else | 
|---|
| 5182 | key.type = NL80211_KEYTYPE_GROUP; | 
|---|
| 5183 | } | 
|---|
| 5184 |  | 
|---|
| 5185 | /* for now */ | 
|---|
| 5186 | if (key.type != NL80211_KEYTYPE_PAIRWISE && | 
|---|
| 5187 | key.type != NL80211_KEYTYPE_GROUP) | 
|---|
| 5188 | return -EINVAL; | 
|---|
| 5189 |  | 
|---|
| 5190 | if (!cfg80211_valid_key_idx(rdev, key_idx: key.idx, | 
|---|
| 5191 | pairwise: key.type == NL80211_KEYTYPE_PAIRWISE)) | 
|---|
| 5192 | return -EINVAL; | 
|---|
| 5193 |  | 
|---|
| 5194 | if (!rdev->ops->del_key) | 
|---|
| 5195 | return -EOPNOTSUPP; | 
|---|
| 5196 |  | 
|---|
| 5197 | err = nl80211_key_allowed(wdev); | 
|---|
| 5198 |  | 
|---|
| 5199 | if (key.type == NL80211_KEYTYPE_GROUP && mac_addr && | 
|---|
| 5200 | !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) | 
|---|
| 5201 | err = -ENOENT; | 
|---|
| 5202 |  | 
|---|
| 5203 | if (!err) | 
|---|
| 5204 | err = nl80211_validate_key_link_id(info, wdev, link_id, | 
|---|
| 5205 | pairwise: key.type == NL80211_KEYTYPE_PAIRWISE); | 
|---|
| 5206 |  | 
|---|
| 5207 | if (!err) | 
|---|
| 5208 | err = rdev_del_key(rdev, netdev: dev, link_id, key_index: key.idx, | 
|---|
| 5209 | pairwise: key.type == NL80211_KEYTYPE_PAIRWISE, | 
|---|
| 5210 | mac_addr); | 
|---|
| 5211 |  | 
|---|
| 5212 | #ifdef CONFIG_CFG80211_WEXT | 
|---|
| 5213 | if (!err) { | 
|---|
| 5214 | if (key.idx == wdev->wext.default_key) | 
|---|
| 5215 | wdev->wext.default_key = -1; | 
|---|
| 5216 | else if (key.idx == wdev->wext.default_mgmt_key) | 
|---|
| 5217 | wdev->wext.default_mgmt_key = -1; | 
|---|
| 5218 | } | 
|---|
| 5219 | #endif | 
|---|
| 5220 |  | 
|---|
| 5221 | return err; | 
|---|
| 5222 | } | 
|---|
| 5223 |  | 
|---|
| 5224 | /* This function returns an error or the number of nested attributes */ | 
|---|
| 5225 | static int validate_acl_mac_addrs(struct nlattr *nl_attr) | 
|---|
| 5226 | { | 
|---|
| 5227 | struct nlattr *attr; | 
|---|
| 5228 | int n_entries = 0, tmp; | 
|---|
| 5229 |  | 
|---|
| 5230 | nla_for_each_nested(attr, nl_attr, tmp) { | 
|---|
| 5231 | if (nla_len(nla: attr) != ETH_ALEN) | 
|---|
| 5232 | return -EINVAL; | 
|---|
| 5233 |  | 
|---|
| 5234 | n_entries++; | 
|---|
| 5235 | } | 
|---|
| 5236 |  | 
|---|
| 5237 | return n_entries; | 
|---|
| 5238 | } | 
|---|
| 5239 |  | 
|---|
| 5240 | /* | 
|---|
| 5241 | * This function parses ACL information and allocates memory for ACL data. | 
|---|
| 5242 | * On successful return, the calling function is responsible to free the | 
|---|
| 5243 | * ACL buffer returned by this function. | 
|---|
| 5244 | */ | 
|---|
| 5245 | static struct cfg80211_acl_data *parse_acl_data(struct wiphy *wiphy, | 
|---|
| 5246 | struct genl_info *info) | 
|---|
| 5247 | { | 
|---|
| 5248 | enum nl80211_acl_policy acl_policy; | 
|---|
| 5249 | struct nlattr *attr; | 
|---|
| 5250 | struct cfg80211_acl_data *acl; | 
|---|
| 5251 | int i = 0, n_entries, tmp; | 
|---|
| 5252 |  | 
|---|
| 5253 | if (!wiphy->max_acl_mac_addrs) | 
|---|
| 5254 | return ERR_PTR(error: -EOPNOTSUPP); | 
|---|
| 5255 |  | 
|---|
| 5256 | if (!info->attrs[NL80211_ATTR_ACL_POLICY]) | 
|---|
| 5257 | return ERR_PTR(error: -EINVAL); | 
|---|
| 5258 |  | 
|---|
| 5259 | acl_policy = nla_get_u32(nla: info->attrs[NL80211_ATTR_ACL_POLICY]); | 
|---|
| 5260 | if (acl_policy != NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED && | 
|---|
| 5261 | acl_policy != NL80211_ACL_POLICY_DENY_UNLESS_LISTED) | 
|---|
| 5262 | return ERR_PTR(error: -EINVAL); | 
|---|
| 5263 |  | 
|---|
| 5264 | if (!info->attrs[NL80211_ATTR_MAC_ADDRS]) | 
|---|
| 5265 | return ERR_PTR(error: -EINVAL); | 
|---|
| 5266 |  | 
|---|
| 5267 | n_entries = validate_acl_mac_addrs(nl_attr: info->attrs[NL80211_ATTR_MAC_ADDRS]); | 
|---|
| 5268 | if (n_entries < 0) | 
|---|
| 5269 | return ERR_PTR(error: n_entries); | 
|---|
| 5270 |  | 
|---|
| 5271 | if (n_entries > wiphy->max_acl_mac_addrs) | 
|---|
| 5272 | return ERR_PTR(error: -EOPNOTSUPP); | 
|---|
| 5273 |  | 
|---|
| 5274 | acl = kzalloc(struct_size(acl, mac_addrs, n_entries), GFP_KERNEL); | 
|---|
| 5275 | if (!acl) | 
|---|
| 5276 | return ERR_PTR(error: -ENOMEM); | 
|---|
| 5277 | acl->n_acl_entries = n_entries; | 
|---|
| 5278 |  | 
|---|
| 5279 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_MAC_ADDRS], tmp) { | 
|---|
| 5280 | memcpy(to: acl->mac_addrs[i].addr, from: nla_data(nla: attr), ETH_ALEN); | 
|---|
| 5281 | i++; | 
|---|
| 5282 | } | 
|---|
| 5283 | acl->acl_policy = acl_policy; | 
|---|
| 5284 |  | 
|---|
| 5285 | return acl; | 
|---|
| 5286 | } | 
|---|
| 5287 |  | 
|---|
| 5288 | static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 5289 | { | 
|---|
| 5290 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 5291 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 5292 | struct cfg80211_acl_data *acl; | 
|---|
| 5293 | int err; | 
|---|
| 5294 |  | 
|---|
| 5295 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | 
|---|
| 5296 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) | 
|---|
| 5297 | return -EOPNOTSUPP; | 
|---|
| 5298 |  | 
|---|
| 5299 | if (!dev->ieee80211_ptr->links[0].ap.beacon_interval) | 
|---|
| 5300 | return -EINVAL; | 
|---|
| 5301 |  | 
|---|
| 5302 | acl = parse_acl_data(wiphy: &rdev->wiphy, info); | 
|---|
| 5303 | if (IS_ERR(ptr: acl)) | 
|---|
| 5304 | return PTR_ERR(ptr: acl); | 
|---|
| 5305 |  | 
|---|
| 5306 | err = rdev_set_mac_acl(rdev, dev, params: acl); | 
|---|
| 5307 |  | 
|---|
| 5308 | kfree(objp: acl); | 
|---|
| 5309 |  | 
|---|
| 5310 | return err; | 
|---|
| 5311 | } | 
|---|
| 5312 |  | 
|---|
| 5313 | static u32 rateset_to_mask(struct ieee80211_supported_band *sband, | 
|---|
| 5314 | u8 *rates, u8 rates_len) | 
|---|
| 5315 | { | 
|---|
| 5316 | u8 i; | 
|---|
| 5317 | u32 mask = 0; | 
|---|
| 5318 |  | 
|---|
| 5319 | for (i = 0; i < rates_len; i++) { | 
|---|
| 5320 | int rate = (rates[i] & 0x7f) * 5; | 
|---|
| 5321 | int ridx; | 
|---|
| 5322 |  | 
|---|
| 5323 | for (ridx = 0; ridx < sband->n_bitrates; ridx++) { | 
|---|
| 5324 | struct ieee80211_rate *srate = | 
|---|
| 5325 | &sband->bitrates[ridx]; | 
|---|
| 5326 | if (rate == srate->bitrate) { | 
|---|
| 5327 | mask |= 1 << ridx; | 
|---|
| 5328 | break; | 
|---|
| 5329 | } | 
|---|
| 5330 | } | 
|---|
| 5331 | if (ridx == sband->n_bitrates) | 
|---|
| 5332 | return 0; /* rate not found */ | 
|---|
| 5333 | } | 
|---|
| 5334 |  | 
|---|
| 5335 | return mask; | 
|---|
| 5336 | } | 
|---|
| 5337 |  | 
|---|
| 5338 | static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband, | 
|---|
| 5339 | u8 *rates, u8 rates_len, | 
|---|
| 5340 | u8 mcs[IEEE80211_HT_MCS_MASK_LEN]) | 
|---|
| 5341 | { | 
|---|
| 5342 | u8 i; | 
|---|
| 5343 |  | 
|---|
| 5344 | memset(s: mcs, c: 0, IEEE80211_HT_MCS_MASK_LEN); | 
|---|
| 5345 |  | 
|---|
| 5346 | for (i = 0; i < rates_len; i++) { | 
|---|
| 5347 | int ridx, rbit; | 
|---|
| 5348 |  | 
|---|
| 5349 | ridx = rates[i] / 8; | 
|---|
| 5350 | rbit = BIT(rates[i] % 8); | 
|---|
| 5351 |  | 
|---|
| 5352 | /* check validity */ | 
|---|
| 5353 | if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN)) | 
|---|
| 5354 | return false; | 
|---|
| 5355 |  | 
|---|
| 5356 | /* check availability */ | 
|---|
| 5357 | ridx = array_index_nospec(ridx, IEEE80211_HT_MCS_MASK_LEN); | 
|---|
| 5358 | if (sband->ht_cap.mcs.rx_mask[ridx] & rbit) | 
|---|
| 5359 | mcs[ridx] |= rbit; | 
|---|
| 5360 | else | 
|---|
| 5361 | return false; | 
|---|
| 5362 | } | 
|---|
| 5363 |  | 
|---|
| 5364 | return true; | 
|---|
| 5365 | } | 
|---|
| 5366 |  | 
|---|
| 5367 | static u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map) | 
|---|
| 5368 | { | 
|---|
| 5369 | u16 mcs_mask = 0; | 
|---|
| 5370 |  | 
|---|
| 5371 | switch (vht_mcs_map) { | 
|---|
| 5372 | case IEEE80211_VHT_MCS_NOT_SUPPORTED: | 
|---|
| 5373 | break; | 
|---|
| 5374 | case IEEE80211_VHT_MCS_SUPPORT_0_7: | 
|---|
| 5375 | mcs_mask = 0x00FF; | 
|---|
| 5376 | break; | 
|---|
| 5377 | case IEEE80211_VHT_MCS_SUPPORT_0_8: | 
|---|
| 5378 | mcs_mask = 0x01FF; | 
|---|
| 5379 | break; | 
|---|
| 5380 | case IEEE80211_VHT_MCS_SUPPORT_0_9: | 
|---|
| 5381 | mcs_mask = 0x03FF; | 
|---|
| 5382 | break; | 
|---|
| 5383 | default: | 
|---|
| 5384 | break; | 
|---|
| 5385 | } | 
|---|
| 5386 |  | 
|---|
| 5387 | return mcs_mask; | 
|---|
| 5388 | } | 
|---|
| 5389 |  | 
|---|
| 5390 | static void vht_build_mcs_mask(u16 vht_mcs_map, | 
|---|
| 5391 | u16 vht_mcs_mask[NL80211_VHT_NSS_MAX]) | 
|---|
| 5392 | { | 
|---|
| 5393 | u8 nss; | 
|---|
| 5394 |  | 
|---|
| 5395 | for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) { | 
|---|
| 5396 | vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map: vht_mcs_map & 0x03); | 
|---|
| 5397 | vht_mcs_map >>= 2; | 
|---|
| 5398 | } | 
|---|
| 5399 | } | 
|---|
| 5400 |  | 
|---|
| 5401 | static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband, | 
|---|
| 5402 | struct nl80211_txrate_vht *txrate, | 
|---|
| 5403 | u16 mcs[NL80211_VHT_NSS_MAX]) | 
|---|
| 5404 | { | 
|---|
| 5405 | u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); | 
|---|
| 5406 | u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {}; | 
|---|
| 5407 | u8 i; | 
|---|
| 5408 |  | 
|---|
| 5409 | if (!sband->vht_cap.vht_supported) | 
|---|
| 5410 | return false; | 
|---|
| 5411 |  | 
|---|
| 5412 | memset(s: mcs, c: 0, n: sizeof(u16) * NL80211_VHT_NSS_MAX); | 
|---|
| 5413 |  | 
|---|
| 5414 | /* Build vht_mcs_mask from VHT capabilities */ | 
|---|
| 5415 | vht_build_mcs_mask(vht_mcs_map: tx_mcs_map, vht_mcs_mask: tx_mcs_mask); | 
|---|
| 5416 |  | 
|---|
| 5417 | for (i = 0; i < NL80211_VHT_NSS_MAX; i++) { | 
|---|
| 5418 | if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i]) | 
|---|
| 5419 | mcs[i] = txrate->mcs[i]; | 
|---|
| 5420 | else | 
|---|
| 5421 | return false; | 
|---|
| 5422 | } | 
|---|
| 5423 |  | 
|---|
| 5424 | return true; | 
|---|
| 5425 | } | 
|---|
| 5426 |  | 
|---|
| 5427 | static u16 he_mcs_map_to_mcs_mask(u8 he_mcs_map) | 
|---|
| 5428 | { | 
|---|
| 5429 | switch (he_mcs_map) { | 
|---|
| 5430 | case IEEE80211_HE_MCS_NOT_SUPPORTED: | 
|---|
| 5431 | return 0; | 
|---|
| 5432 | case IEEE80211_HE_MCS_SUPPORT_0_7: | 
|---|
| 5433 | return 0x00FF; | 
|---|
| 5434 | case IEEE80211_HE_MCS_SUPPORT_0_9: | 
|---|
| 5435 | return 0x03FF; | 
|---|
| 5436 | case IEEE80211_HE_MCS_SUPPORT_0_11: | 
|---|
| 5437 | return 0xFFF; | 
|---|
| 5438 | default: | 
|---|
| 5439 | break; | 
|---|
| 5440 | } | 
|---|
| 5441 | return 0; | 
|---|
| 5442 | } | 
|---|
| 5443 |  | 
|---|
| 5444 | static void he_build_mcs_mask(u16 he_mcs_map, | 
|---|
| 5445 | u16 he_mcs_mask[NL80211_HE_NSS_MAX]) | 
|---|
| 5446 | { | 
|---|
| 5447 | u8 nss; | 
|---|
| 5448 |  | 
|---|
| 5449 | for (nss = 0; nss < NL80211_HE_NSS_MAX; nss++) { | 
|---|
| 5450 | he_mcs_mask[nss] = he_mcs_map_to_mcs_mask(he_mcs_map: he_mcs_map & 0x03); | 
|---|
| 5451 | he_mcs_map >>= 2; | 
|---|
| 5452 | } | 
|---|
| 5453 | } | 
|---|
| 5454 |  | 
|---|
| 5455 | static u16 he_get_txmcsmap(struct genl_info *info, unsigned int link_id, | 
|---|
| 5456 | const struct ieee80211_sta_he_cap *he_cap) | 
|---|
| 5457 | { | 
|---|
| 5458 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 5459 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 5460 | struct cfg80211_chan_def *chandef; | 
|---|
| 5461 | __le16 tx_mcs; | 
|---|
| 5462 |  | 
|---|
| 5463 | chandef = wdev_chandef(wdev, link_id); | 
|---|
| 5464 | if (!chandef) { | 
|---|
| 5465 | /* | 
|---|
| 5466 | * This is probably broken, but we never maintained | 
|---|
| 5467 | * a chandef in these cases, so it always was. | 
|---|
| 5468 | */ | 
|---|
| 5469 | return le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80); | 
|---|
| 5470 | } | 
|---|
| 5471 |  | 
|---|
| 5472 | switch (chandef->width) { | 
|---|
| 5473 | case NL80211_CHAN_WIDTH_80P80: | 
|---|
| 5474 | tx_mcs = he_cap->he_mcs_nss_supp.tx_mcs_80p80; | 
|---|
| 5475 | break; | 
|---|
| 5476 | case NL80211_CHAN_WIDTH_160: | 
|---|
| 5477 | tx_mcs = he_cap->he_mcs_nss_supp.tx_mcs_160; | 
|---|
| 5478 | break; | 
|---|
| 5479 | default: | 
|---|
| 5480 | tx_mcs = he_cap->he_mcs_nss_supp.tx_mcs_80; | 
|---|
| 5481 | break; | 
|---|
| 5482 | } | 
|---|
| 5483 |  | 
|---|
| 5484 | return le16_to_cpu(tx_mcs); | 
|---|
| 5485 | } | 
|---|
| 5486 |  | 
|---|
| 5487 | static bool he_set_mcs_mask(struct genl_info *info, | 
|---|
| 5488 | struct wireless_dev *wdev, | 
|---|
| 5489 | struct ieee80211_supported_band *sband, | 
|---|
| 5490 | struct nl80211_txrate_he *txrate, | 
|---|
| 5491 | u16 mcs[NL80211_HE_NSS_MAX], | 
|---|
| 5492 | unsigned int link_id) | 
|---|
| 5493 | { | 
|---|
| 5494 | const struct ieee80211_sta_he_cap *he_cap; | 
|---|
| 5495 | u16 tx_mcs_mask[NL80211_HE_NSS_MAX] = {}; | 
|---|
| 5496 | u16 tx_mcs_map = 0; | 
|---|
| 5497 | u8 i; | 
|---|
| 5498 |  | 
|---|
| 5499 | he_cap = ieee80211_get_he_iftype_cap(sband, iftype: wdev->iftype); | 
|---|
| 5500 | if (!he_cap) | 
|---|
| 5501 | return false; | 
|---|
| 5502 |  | 
|---|
| 5503 | memset(s: mcs, c: 0, n: sizeof(u16) * NL80211_HE_NSS_MAX); | 
|---|
| 5504 |  | 
|---|
| 5505 | tx_mcs_map = he_get_txmcsmap(info, link_id, he_cap); | 
|---|
| 5506 |  | 
|---|
| 5507 | /* Build he_mcs_mask from HE capabilities */ | 
|---|
| 5508 | he_build_mcs_mask(he_mcs_map: tx_mcs_map, he_mcs_mask: tx_mcs_mask); | 
|---|
| 5509 |  | 
|---|
| 5510 | for (i = 0; i < NL80211_HE_NSS_MAX; i++) { | 
|---|
| 5511 | if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i]) | 
|---|
| 5512 | mcs[i] = txrate->mcs[i]; | 
|---|
| 5513 | else | 
|---|
| 5514 | return false; | 
|---|
| 5515 | } | 
|---|
| 5516 |  | 
|---|
| 5517 | return true; | 
|---|
| 5518 | } | 
|---|
| 5519 |  | 
|---|
| 5520 | static void eht_build_mcs_mask(struct genl_info *info, | 
|---|
| 5521 | const struct ieee80211_sta_eht_cap *eht_cap, | 
|---|
| 5522 | u8 mcs_nss_len, u16 *mcs_mask) | 
|---|
| 5523 | { | 
|---|
| 5524 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 5525 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 5526 | u8 nss, mcs_7 = 0, mcs_9 = 0, mcs_11 = 0, mcs_13 = 0; | 
|---|
| 5527 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); | 
|---|
| 5528 |  | 
|---|
| 5529 | if (mcs_nss_len == 4) { | 
|---|
| 5530 | const struct ieee80211_eht_mcs_nss_supp_20mhz_only *mcs = | 
|---|
| 5531 | &eht_cap->eht_mcs_nss_supp.only_20mhz; | 
|---|
| 5532 |  | 
|---|
| 5533 | mcs_7 = u8_get_bits(v: mcs->rx_tx_mcs7_max_nss, | 
|---|
| 5534 | IEEE80211_EHT_MCS_NSS_TX); | 
|---|
| 5535 | mcs_9 = u8_get_bits(v: mcs->rx_tx_mcs9_max_nss, | 
|---|
| 5536 | IEEE80211_EHT_MCS_NSS_TX); | 
|---|
| 5537 | mcs_11 = u8_get_bits(v: mcs->rx_tx_mcs11_max_nss, | 
|---|
| 5538 | IEEE80211_EHT_MCS_NSS_TX); | 
|---|
| 5539 | mcs_13 = u8_get_bits(v: mcs->rx_tx_mcs13_max_nss, | 
|---|
| 5540 | IEEE80211_EHT_MCS_NSS_TX); | 
|---|
| 5541 |  | 
|---|
| 5542 | } else { | 
|---|
| 5543 | const struct ieee80211_eht_mcs_nss_supp_bw *mcs; | 
|---|
| 5544 | enum nl80211_chan_width width; | 
|---|
| 5545 |  | 
|---|
| 5546 | switch (wdev->iftype) { | 
|---|
| 5547 | case NL80211_IFTYPE_ADHOC: | 
|---|
| 5548 | width = wdev->u.ibss.chandef.width; | 
|---|
| 5549 | break; | 
|---|
| 5550 | case NL80211_IFTYPE_MESH_POINT: | 
|---|
| 5551 | width = wdev->u.mesh.chandef.width; | 
|---|
| 5552 | break; | 
|---|
| 5553 | case NL80211_IFTYPE_OCB: | 
|---|
| 5554 | width = wdev->u.ocb.chandef.width; | 
|---|
| 5555 | break; | 
|---|
| 5556 | default: | 
|---|
| 5557 | if (wdev->valid_links) | 
|---|
| 5558 | width = wdev->links[link_id].ap.chandef.width; | 
|---|
| 5559 | else | 
|---|
| 5560 | width = wdev->u.ap.preset_chandef.width; | 
|---|
| 5561 | break; | 
|---|
| 5562 | } | 
|---|
| 5563 |  | 
|---|
| 5564 | switch (width) { | 
|---|
| 5565 | case NL80211_CHAN_WIDTH_320: | 
|---|
| 5566 | mcs = &eht_cap->eht_mcs_nss_supp.bw._320; | 
|---|
| 5567 | break; | 
|---|
| 5568 | case NL80211_CHAN_WIDTH_160: | 
|---|
| 5569 | mcs = &eht_cap->eht_mcs_nss_supp.bw._160; | 
|---|
| 5570 | break; | 
|---|
| 5571 | default: | 
|---|
| 5572 | mcs = &eht_cap->eht_mcs_nss_supp.bw._80; | 
|---|
| 5573 | break; | 
|---|
| 5574 | } | 
|---|
| 5575 |  | 
|---|
| 5576 | mcs_7 = u8_get_bits(v: mcs->rx_tx_mcs9_max_nss, | 
|---|
| 5577 | IEEE80211_EHT_MCS_NSS_TX); | 
|---|
| 5578 | mcs_9 = u8_get_bits(v: mcs->rx_tx_mcs9_max_nss, | 
|---|
| 5579 | IEEE80211_EHT_MCS_NSS_TX); | 
|---|
| 5580 | mcs_11 = u8_get_bits(v: mcs->rx_tx_mcs11_max_nss, | 
|---|
| 5581 | IEEE80211_EHT_MCS_NSS_TX); | 
|---|
| 5582 | mcs_13 = u8_get_bits(v: mcs->rx_tx_mcs13_max_nss, | 
|---|
| 5583 | IEEE80211_EHT_MCS_NSS_TX); | 
|---|
| 5584 | } | 
|---|
| 5585 |  | 
|---|
| 5586 | /* Enable MCS 14 for NSS 0 */ | 
|---|
| 5587 | if (eht_cap->eht_cap_elem.phy_cap_info[6] & | 
|---|
| 5588 | IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP) | 
|---|
| 5589 | mcs_mask[0] |= 0x4000; | 
|---|
| 5590 |  | 
|---|
| 5591 | /* Enable MCS 15 for NSS 0 */ | 
|---|
| 5592 | mcs_mask[0] |= 0x8000; | 
|---|
| 5593 |  | 
|---|
| 5594 | for (nss = 0; nss < NL80211_EHT_NSS_MAX; nss++) { | 
|---|
| 5595 | if (!mcs_7) | 
|---|
| 5596 | continue; | 
|---|
| 5597 | mcs_mask[nss] |= 0x00FF; | 
|---|
| 5598 | mcs_7--; | 
|---|
| 5599 |  | 
|---|
| 5600 | if (!mcs_9) | 
|---|
| 5601 | continue; | 
|---|
| 5602 | mcs_mask[nss] |= 0x0300; | 
|---|
| 5603 | mcs_9--; | 
|---|
| 5604 |  | 
|---|
| 5605 | if (!mcs_11) | 
|---|
| 5606 | continue; | 
|---|
| 5607 | mcs_mask[nss] |= 0x0C00; | 
|---|
| 5608 | mcs_11--; | 
|---|
| 5609 |  | 
|---|
| 5610 | if (!mcs_13) | 
|---|
| 5611 | continue; | 
|---|
| 5612 | mcs_mask[nss] |= 0x3000; | 
|---|
| 5613 | mcs_13--; | 
|---|
| 5614 | } | 
|---|
| 5615 | } | 
|---|
| 5616 |  | 
|---|
| 5617 | static bool eht_set_mcs_mask(struct genl_info *info, struct wireless_dev *wdev, | 
|---|
| 5618 | struct ieee80211_supported_band *sband, | 
|---|
| 5619 | struct nl80211_txrate_eht *txrate, | 
|---|
| 5620 | u16 mcs[NL80211_EHT_NSS_MAX]) | 
|---|
| 5621 | { | 
|---|
| 5622 | const struct ieee80211_sta_he_cap *he_cap; | 
|---|
| 5623 | const struct ieee80211_sta_eht_cap *eht_cap; | 
|---|
| 5624 | u16 tx_mcs_mask[NL80211_EHT_NSS_MAX] = { 0 }; | 
|---|
| 5625 | u8 i, mcs_nss_len; | 
|---|
| 5626 |  | 
|---|
| 5627 | he_cap = ieee80211_get_he_iftype_cap(sband, iftype: wdev->iftype); | 
|---|
| 5628 | if (!he_cap) | 
|---|
| 5629 | return false; | 
|---|
| 5630 |  | 
|---|
| 5631 | eht_cap = ieee80211_get_eht_iftype_cap(sband, iftype: wdev->iftype); | 
|---|
| 5632 | if (!eht_cap) | 
|---|
| 5633 | return false; | 
|---|
| 5634 |  | 
|---|
| 5635 | /* Checks for MCS 14 */ | 
|---|
| 5636 | if (txrate->mcs[0] & 0x4000) { | 
|---|
| 5637 | if (sband->band != NL80211_BAND_6GHZ) | 
|---|
| 5638 | return false; | 
|---|
| 5639 |  | 
|---|
| 5640 | if (!(eht_cap->eht_cap_elem.phy_cap_info[6] & | 
|---|
| 5641 | IEEE80211_EHT_PHY_CAP6_EHT_DUP_6GHZ_SUPP)) | 
|---|
| 5642 | return false; | 
|---|
| 5643 | } | 
|---|
| 5644 |  | 
|---|
| 5645 | mcs_nss_len = ieee80211_eht_mcs_nss_size(he_cap: &he_cap->he_cap_elem, | 
|---|
| 5646 | eht_cap: &eht_cap->eht_cap_elem, | 
|---|
| 5647 | from_ap: wdev->iftype == | 
|---|
| 5648 | NL80211_IFTYPE_STATION); | 
|---|
| 5649 |  | 
|---|
| 5650 | if (mcs_nss_len == 3) { | 
|---|
| 5651 | /* Supported iftypes for setting non-20 MHZ only EHT MCS */ | 
|---|
| 5652 | switch (wdev->iftype) { | 
|---|
| 5653 | case NL80211_IFTYPE_ADHOC: | 
|---|
| 5654 | case NL80211_IFTYPE_AP: | 
|---|
| 5655 | case NL80211_IFTYPE_P2P_GO: | 
|---|
| 5656 | case NL80211_IFTYPE_MESH_POINT: | 
|---|
| 5657 | case NL80211_IFTYPE_OCB: | 
|---|
| 5658 | break; | 
|---|
| 5659 | default: | 
|---|
| 5660 | return false; | 
|---|
| 5661 | } | 
|---|
| 5662 | } | 
|---|
| 5663 |  | 
|---|
| 5664 | /* Build eht_mcs_mask from EHT and HE capabilities */ | 
|---|
| 5665 | eht_build_mcs_mask(info, eht_cap, mcs_nss_len, mcs_mask: tx_mcs_mask); | 
|---|
| 5666 |  | 
|---|
| 5667 | memset(s: mcs, c: 0, n: sizeof(u16) * NL80211_EHT_NSS_MAX); | 
|---|
| 5668 | for (i = 0; i < NL80211_EHT_NSS_MAX; i++) { | 
|---|
| 5669 | if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i]) | 
|---|
| 5670 | mcs[i] = txrate->mcs[i]; | 
|---|
| 5671 | else | 
|---|
| 5672 | return false; | 
|---|
| 5673 | } | 
|---|
| 5674 |  | 
|---|
| 5675 | return true; | 
|---|
| 5676 | } | 
|---|
| 5677 |  | 
|---|
| 5678 | static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, | 
|---|
| 5679 | struct nlattr *attrs[], | 
|---|
| 5680 | enum nl80211_attrs attr, | 
|---|
| 5681 | struct cfg80211_bitrate_mask *mask, | 
|---|
| 5682 | struct net_device *dev, | 
|---|
| 5683 | bool default_all_enabled, | 
|---|
| 5684 | unsigned int link_id) | 
|---|
| 5685 | { | 
|---|
| 5686 | struct nlattr *tb[NL80211_TXRATE_MAX + 1]; | 
|---|
| 5687 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 5688 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 5689 | int rem, i; | 
|---|
| 5690 | struct nlattr *tx_rates; | 
|---|
| 5691 | struct ieee80211_supported_band *sband; | 
|---|
| 5692 | u16 vht_tx_mcs_map, he_tx_mcs_map; | 
|---|
| 5693 |  | 
|---|
| 5694 | memset(s: mask, c: 0, n: sizeof(*mask)); | 
|---|
| 5695 | /* Default to all rates enabled */ | 
|---|
| 5696 | for (i = 0; i < NUM_NL80211_BANDS; i++) { | 
|---|
| 5697 | const struct ieee80211_sta_he_cap *he_cap; | 
|---|
| 5698 | const struct ieee80211_sta_eht_cap *eht_cap; | 
|---|
| 5699 | u8 mcs_nss_len; | 
|---|
| 5700 |  | 
|---|
| 5701 | if (!default_all_enabled) | 
|---|
| 5702 | break; | 
|---|
| 5703 |  | 
|---|
| 5704 | sband = rdev->wiphy.bands[i]; | 
|---|
| 5705 |  | 
|---|
| 5706 | if (!sband) | 
|---|
| 5707 | continue; | 
|---|
| 5708 |  | 
|---|
| 5709 | mask->control[i].legacy = (1 << sband->n_bitrates) - 1; | 
|---|
| 5710 | memcpy(to: mask->control[i].ht_mcs, | 
|---|
| 5711 | from: sband->ht_cap.mcs.rx_mask, | 
|---|
| 5712 | len: sizeof(mask->control[i].ht_mcs)); | 
|---|
| 5713 |  | 
|---|
| 5714 | if (sband->vht_cap.vht_supported) { | 
|---|
| 5715 | vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); | 
|---|
| 5716 | vht_build_mcs_mask(vht_mcs_map: vht_tx_mcs_map, vht_mcs_mask: mask->control[i].vht_mcs); | 
|---|
| 5717 | } | 
|---|
| 5718 |  | 
|---|
| 5719 | he_cap = ieee80211_get_he_iftype_cap(sband, iftype: wdev->iftype); | 
|---|
| 5720 | if (!he_cap) | 
|---|
| 5721 | continue; | 
|---|
| 5722 |  | 
|---|
| 5723 | he_tx_mcs_map = he_get_txmcsmap(info, link_id, he_cap); | 
|---|
| 5724 | he_build_mcs_mask(he_mcs_map: he_tx_mcs_map, he_mcs_mask: mask->control[i].he_mcs); | 
|---|
| 5725 |  | 
|---|
| 5726 | mask->control[i].he_gi = 0xFF; | 
|---|
| 5727 | mask->control[i].he_ltf = 0xFF; | 
|---|
| 5728 |  | 
|---|
| 5729 | eht_cap = ieee80211_get_eht_iftype_cap(sband, iftype: wdev->iftype); | 
|---|
| 5730 | if (!eht_cap) | 
|---|
| 5731 | continue; | 
|---|
| 5732 |  | 
|---|
| 5733 | mcs_nss_len = ieee80211_eht_mcs_nss_size(he_cap: &he_cap->he_cap_elem, | 
|---|
| 5734 | eht_cap: &eht_cap->eht_cap_elem, | 
|---|
| 5735 | from_ap: wdev->iftype == | 
|---|
| 5736 | NL80211_IFTYPE_STATION); | 
|---|
| 5737 |  | 
|---|
| 5738 | eht_build_mcs_mask(info, eht_cap, mcs_nss_len, | 
|---|
| 5739 | mcs_mask: mask->control[i].eht_mcs); | 
|---|
| 5740 |  | 
|---|
| 5741 | mask->control[i].eht_gi = 0xFF; | 
|---|
| 5742 | mask->control[i].eht_ltf = 0xFF; | 
|---|
| 5743 | } | 
|---|
| 5744 |  | 
|---|
| 5745 | /* if no rates are given set it back to the defaults */ | 
|---|
| 5746 | if (!attrs[attr]) | 
|---|
| 5747 | goto out; | 
|---|
| 5748 |  | 
|---|
| 5749 | /* The nested attribute uses enum nl80211_band as the index. This maps | 
|---|
| 5750 | * directly to the enum nl80211_band values used in cfg80211. | 
|---|
| 5751 | */ | 
|---|
| 5752 | BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8); | 
|---|
| 5753 | nla_for_each_nested(tx_rates, attrs[attr], rem) { | 
|---|
| 5754 | enum nl80211_band band = nla_type(nla: tx_rates); | 
|---|
| 5755 | int err; | 
|---|
| 5756 |  | 
|---|
| 5757 | if (band < 0 || band >= NUM_NL80211_BANDS) | 
|---|
| 5758 | return -EINVAL; | 
|---|
| 5759 | sband = rdev->wiphy.bands[band]; | 
|---|
| 5760 | if (sband == NULL) | 
|---|
| 5761 | return -EINVAL; | 
|---|
| 5762 | err = nla_parse_nested_deprecated(tb, maxtype: NL80211_TXRATE_MAX, | 
|---|
| 5763 | nla: tx_rates, | 
|---|
| 5764 | policy: nl80211_txattr_policy, | 
|---|
| 5765 | extack: info->extack); | 
|---|
| 5766 | if (err) | 
|---|
| 5767 | return err; | 
|---|
| 5768 | if (tb[NL80211_TXRATE_LEGACY]) { | 
|---|
| 5769 | mask->control[band].legacy = rateset_to_mask( | 
|---|
| 5770 | sband, | 
|---|
| 5771 | rates: nla_data(nla: tb[NL80211_TXRATE_LEGACY]), | 
|---|
| 5772 | rates_len: nla_len(nla: tb[NL80211_TXRATE_LEGACY])); | 
|---|
| 5773 | if ((mask->control[band].legacy == 0) && | 
|---|
| 5774 | nla_len(nla: tb[NL80211_TXRATE_LEGACY])) | 
|---|
| 5775 | return -EINVAL; | 
|---|
| 5776 | } | 
|---|
| 5777 | if (tb[NL80211_TXRATE_HT]) { | 
|---|
| 5778 | if (!ht_rateset_to_mask( | 
|---|
| 5779 | sband, | 
|---|
| 5780 | rates: nla_data(nla: tb[NL80211_TXRATE_HT]), | 
|---|
| 5781 | rates_len: nla_len(nla: tb[NL80211_TXRATE_HT]), | 
|---|
| 5782 | mcs: mask->control[band].ht_mcs)) | 
|---|
| 5783 | return -EINVAL; | 
|---|
| 5784 | } | 
|---|
| 5785 |  | 
|---|
| 5786 | if (tb[NL80211_TXRATE_VHT]) { | 
|---|
| 5787 | if (!vht_set_mcs_mask( | 
|---|
| 5788 | sband, | 
|---|
| 5789 | txrate: nla_data(nla: tb[NL80211_TXRATE_VHT]), | 
|---|
| 5790 | mcs: mask->control[band].vht_mcs)) | 
|---|
| 5791 | return -EINVAL; | 
|---|
| 5792 | } | 
|---|
| 5793 |  | 
|---|
| 5794 | if (tb[NL80211_TXRATE_GI]) { | 
|---|
| 5795 | mask->control[band].gi = | 
|---|
| 5796 | nla_get_u8(nla: tb[NL80211_TXRATE_GI]); | 
|---|
| 5797 | if (mask->control[band].gi > NL80211_TXRATE_FORCE_LGI) | 
|---|
| 5798 | return -EINVAL; | 
|---|
| 5799 | } | 
|---|
| 5800 | if (tb[NL80211_TXRATE_HE] && | 
|---|
| 5801 | !he_set_mcs_mask(info, wdev, sband, | 
|---|
| 5802 | txrate: nla_data(nla: tb[NL80211_TXRATE_HE]), | 
|---|
| 5803 | mcs: mask->control[band].he_mcs, | 
|---|
| 5804 | link_id)) | 
|---|
| 5805 | return -EINVAL; | 
|---|
| 5806 |  | 
|---|
| 5807 | if (tb[NL80211_TXRATE_HE_GI]) | 
|---|
| 5808 | mask->control[band].he_gi = | 
|---|
| 5809 | nla_get_u8(nla: tb[NL80211_TXRATE_HE_GI]); | 
|---|
| 5810 | if (tb[NL80211_TXRATE_HE_LTF]) | 
|---|
| 5811 | mask->control[band].he_ltf = | 
|---|
| 5812 | nla_get_u8(nla: tb[NL80211_TXRATE_HE_LTF]); | 
|---|
| 5813 |  | 
|---|
| 5814 | if (tb[NL80211_TXRATE_EHT] && | 
|---|
| 5815 | !eht_set_mcs_mask(info, wdev, sband, | 
|---|
| 5816 | txrate: nla_data(nla: tb[NL80211_TXRATE_EHT]), | 
|---|
| 5817 | mcs: mask->control[band].eht_mcs)) | 
|---|
| 5818 | return -EINVAL; | 
|---|
| 5819 |  | 
|---|
| 5820 | if (tb[NL80211_TXRATE_EHT_GI]) | 
|---|
| 5821 | mask->control[band].eht_gi = | 
|---|
| 5822 | nla_get_u8(nla: tb[NL80211_TXRATE_EHT_GI]); | 
|---|
| 5823 | if (tb[NL80211_TXRATE_EHT_LTF]) | 
|---|
| 5824 | mask->control[band].eht_ltf = | 
|---|
| 5825 | nla_get_u8(nla: tb[NL80211_TXRATE_EHT_LTF]); | 
|---|
| 5826 |  | 
|---|
| 5827 | if (mask->control[band].legacy == 0) { | 
|---|
| 5828 | /* don't allow empty legacy rates if HT, VHT, HE or EHT | 
|---|
| 5829 | * are not even supported. | 
|---|
| 5830 | */ | 
|---|
| 5831 | if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported || | 
|---|
| 5832 | rdev->wiphy.bands[band]->vht_cap.vht_supported || | 
|---|
| 5833 | ieee80211_get_he_iftype_cap(sband, iftype: wdev->iftype) || | 
|---|
| 5834 | ieee80211_get_eht_iftype_cap(sband, iftype: wdev->iftype))) | 
|---|
| 5835 | return -EINVAL; | 
|---|
| 5836 |  | 
|---|
| 5837 | for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) | 
|---|
| 5838 | if (mask->control[band].ht_mcs[i]) | 
|---|
| 5839 | goto out; | 
|---|
| 5840 |  | 
|---|
| 5841 | for (i = 0; i < NL80211_VHT_NSS_MAX; i++) | 
|---|
| 5842 | if (mask->control[band].vht_mcs[i]) | 
|---|
| 5843 | goto out; | 
|---|
| 5844 |  | 
|---|
| 5845 | for (i = 0; i < NL80211_HE_NSS_MAX; i++) | 
|---|
| 5846 | if (mask->control[band].he_mcs[i]) | 
|---|
| 5847 | goto out; | 
|---|
| 5848 |  | 
|---|
| 5849 | for (i = 0; i < NL80211_EHT_NSS_MAX; i++) | 
|---|
| 5850 | if (mask->control[band].eht_mcs[i]) | 
|---|
| 5851 | goto out; | 
|---|
| 5852 |  | 
|---|
| 5853 | /* legacy and mcs rates may not be both empty */ | 
|---|
| 5854 | return -EINVAL; | 
|---|
| 5855 | } | 
|---|
| 5856 | } | 
|---|
| 5857 |  | 
|---|
| 5858 | out: | 
|---|
| 5859 | return 0; | 
|---|
| 5860 | } | 
|---|
| 5861 |  | 
|---|
| 5862 | static int validate_beacon_tx_rate(struct cfg80211_registered_device *rdev, | 
|---|
| 5863 | enum nl80211_band band, | 
|---|
| 5864 | struct cfg80211_bitrate_mask *beacon_rate) | 
|---|
| 5865 | { | 
|---|
| 5866 | u32 count_ht, count_vht, count_he, count_eht, i; | 
|---|
| 5867 | u32 rate = beacon_rate->control[band].legacy; | 
|---|
| 5868 |  | 
|---|
| 5869 | /* Allow only one rate */ | 
|---|
| 5870 | if (hweight32(rate) > 1) | 
|---|
| 5871 | return -EINVAL; | 
|---|
| 5872 |  | 
|---|
| 5873 | count_ht = 0; | 
|---|
| 5874 | for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { | 
|---|
| 5875 | if (hweight8(beacon_rate->control[band].ht_mcs[i]) > 1) { | 
|---|
| 5876 | return -EINVAL; | 
|---|
| 5877 | } else if (beacon_rate->control[band].ht_mcs[i]) { | 
|---|
| 5878 | count_ht++; | 
|---|
| 5879 | if (count_ht > 1) | 
|---|
| 5880 | return -EINVAL; | 
|---|
| 5881 | } | 
|---|
| 5882 | if (count_ht && rate) | 
|---|
| 5883 | return -EINVAL; | 
|---|
| 5884 | } | 
|---|
| 5885 |  | 
|---|
| 5886 | count_vht = 0; | 
|---|
| 5887 | for (i = 0; i < NL80211_VHT_NSS_MAX; i++) { | 
|---|
| 5888 | if (hweight16(beacon_rate->control[band].vht_mcs[i]) > 1) { | 
|---|
| 5889 | return -EINVAL; | 
|---|
| 5890 | } else if (beacon_rate->control[band].vht_mcs[i]) { | 
|---|
| 5891 | count_vht++; | 
|---|
| 5892 | if (count_vht > 1) | 
|---|
| 5893 | return -EINVAL; | 
|---|
| 5894 | } | 
|---|
| 5895 | if (count_vht && rate) | 
|---|
| 5896 | return -EINVAL; | 
|---|
| 5897 | } | 
|---|
| 5898 |  | 
|---|
| 5899 | count_he = 0; | 
|---|
| 5900 | for (i = 0; i < NL80211_HE_NSS_MAX; i++) { | 
|---|
| 5901 | if (hweight16(beacon_rate->control[band].he_mcs[i]) > 1) { | 
|---|
| 5902 | return -EINVAL; | 
|---|
| 5903 | } else if (beacon_rate->control[band].he_mcs[i]) { | 
|---|
| 5904 | count_he++; | 
|---|
| 5905 | if (count_he > 1) | 
|---|
| 5906 | return -EINVAL; | 
|---|
| 5907 | } | 
|---|
| 5908 | if (count_he && rate) | 
|---|
| 5909 | return -EINVAL; | 
|---|
| 5910 | } | 
|---|
| 5911 |  | 
|---|
| 5912 | count_eht = 0; | 
|---|
| 5913 | for (i = 0; i < NL80211_EHT_NSS_MAX; i++) { | 
|---|
| 5914 | if (hweight16(beacon_rate->control[band].eht_mcs[i]) > 1) { | 
|---|
| 5915 | return -EINVAL; | 
|---|
| 5916 | } else if (beacon_rate->control[band].eht_mcs[i]) { | 
|---|
| 5917 | count_eht++; | 
|---|
| 5918 | if (count_eht > 1) | 
|---|
| 5919 | return -EINVAL; | 
|---|
| 5920 | } | 
|---|
| 5921 | if (count_eht && rate) | 
|---|
| 5922 | return -EINVAL; | 
|---|
| 5923 | } | 
|---|
| 5924 |  | 
|---|
| 5925 | if ((count_ht && count_vht && count_he && count_eht) || | 
|---|
| 5926 | (!rate && !count_ht && !count_vht && !count_he && !count_eht)) | 
|---|
| 5927 | return -EINVAL; | 
|---|
| 5928 |  | 
|---|
| 5929 | if (rate && | 
|---|
| 5930 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 5931 | ftidx: NL80211_EXT_FEATURE_BEACON_RATE_LEGACY)) | 
|---|
| 5932 | return -EINVAL; | 
|---|
| 5933 | if (count_ht && | 
|---|
| 5934 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 5935 | ftidx: NL80211_EXT_FEATURE_BEACON_RATE_HT)) | 
|---|
| 5936 | return -EINVAL; | 
|---|
| 5937 | if (count_vht && | 
|---|
| 5938 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 5939 | ftidx: NL80211_EXT_FEATURE_BEACON_RATE_VHT)) | 
|---|
| 5940 | return -EINVAL; | 
|---|
| 5941 | if (count_he && | 
|---|
| 5942 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 5943 | ftidx: NL80211_EXT_FEATURE_BEACON_RATE_HE)) | 
|---|
| 5944 | return -EINVAL; | 
|---|
| 5945 |  | 
|---|
| 5946 | if (count_eht && | 
|---|
| 5947 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 5948 | ftidx: NL80211_EXT_FEATURE_BEACON_RATE_EHT)) | 
|---|
| 5949 | return -EINVAL; | 
|---|
| 5950 |  | 
|---|
| 5951 | return 0; | 
|---|
| 5952 | } | 
|---|
| 5953 |  | 
|---|
| 5954 | static int nl80211_parse_mbssid_config(struct wiphy *wiphy, | 
|---|
| 5955 | struct net_device *dev, | 
|---|
| 5956 | unsigned int link_id, | 
|---|
| 5957 | struct nlattr *attrs, | 
|---|
| 5958 | struct cfg80211_mbssid_config *config, | 
|---|
| 5959 | u8 num_elems) | 
|---|
| 5960 | { | 
|---|
| 5961 | struct nlattr *tb[NL80211_MBSSID_CONFIG_ATTR_MAX + 1]; | 
|---|
| 5962 | int tx_link_id = -1; | 
|---|
| 5963 |  | 
|---|
| 5964 | if (!wiphy->mbssid_max_interfaces) | 
|---|
| 5965 | return -EOPNOTSUPP; | 
|---|
| 5966 |  | 
|---|
| 5967 | if (nla_parse_nested(tb, maxtype: NL80211_MBSSID_CONFIG_ATTR_MAX, nla: attrs, NULL, | 
|---|
| 5968 | NULL) || | 
|---|
| 5969 | !tb[NL80211_MBSSID_CONFIG_ATTR_INDEX]) | 
|---|
| 5970 | return -EINVAL; | 
|---|
| 5971 |  | 
|---|
| 5972 | config->ema = nla_get_flag(nla: tb[NL80211_MBSSID_CONFIG_ATTR_EMA]); | 
|---|
| 5973 | if (config->ema) { | 
|---|
| 5974 | if (!wiphy->ema_max_profile_periodicity) | 
|---|
| 5975 | return -EOPNOTSUPP; | 
|---|
| 5976 |  | 
|---|
| 5977 | if (num_elems > wiphy->ema_max_profile_periodicity) | 
|---|
| 5978 | return -EINVAL; | 
|---|
| 5979 | } | 
|---|
| 5980 |  | 
|---|
| 5981 | config->index = nla_get_u8(nla: tb[NL80211_MBSSID_CONFIG_ATTR_INDEX]); | 
|---|
| 5982 | if (config->index >= wiphy->mbssid_max_interfaces || | 
|---|
| 5983 | (!config->index && !num_elems)) | 
|---|
| 5984 | return -EINVAL; | 
|---|
| 5985 |  | 
|---|
| 5986 | if (tb[NL80211_MBSSID_CONFIG_ATTR_TX_LINK_ID]) | 
|---|
| 5987 | tx_link_id = nla_get_u8(nla: tb[NL80211_MBSSID_CONFIG_ATTR_TX_LINK_ID]); | 
|---|
| 5988 |  | 
|---|
| 5989 | if (tb[NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX]) { | 
|---|
| 5990 | u32 tx_ifindex = | 
|---|
| 5991 | nla_get_u32(nla: tb[NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX]); | 
|---|
| 5992 |  | 
|---|
| 5993 | if ((!config->index && tx_ifindex != dev->ifindex) || | 
|---|
| 5994 | (config->index && tx_ifindex == dev->ifindex)) | 
|---|
| 5995 | return -EINVAL; | 
|---|
| 5996 |  | 
|---|
| 5997 | if (tx_ifindex != dev->ifindex) { | 
|---|
| 5998 | struct net_device *tx_netdev = | 
|---|
| 5999 | dev_get_by_index(net: wiphy_net(wiphy), ifindex: tx_ifindex); | 
|---|
| 6000 |  | 
|---|
| 6001 | if (!tx_netdev || !tx_netdev->ieee80211_ptr || | 
|---|
| 6002 | tx_netdev->ieee80211_ptr->wiphy != wiphy || | 
|---|
| 6003 | tx_netdev->ieee80211_ptr->iftype != | 
|---|
| 6004 | NL80211_IFTYPE_AP) { | 
|---|
| 6005 | dev_put(dev: tx_netdev); | 
|---|
| 6006 | return -EINVAL; | 
|---|
| 6007 | } | 
|---|
| 6008 |  | 
|---|
| 6009 | config->tx_wdev = tx_netdev->ieee80211_ptr; | 
|---|
| 6010 | /* Caller should call dev_put(config->tx_wdev) from this point */ | 
|---|
| 6011 |  | 
|---|
| 6012 | if (config->tx_wdev->valid_links) { | 
|---|
| 6013 | if (tx_link_id == -1 || | 
|---|
| 6014 | !(config->tx_wdev->valid_links & BIT(tx_link_id))) | 
|---|
| 6015 | return -ENOLINK; | 
|---|
| 6016 |  | 
|---|
| 6017 | config->tx_link_id = tx_link_id; | 
|---|
| 6018 | } | 
|---|
| 6019 | } else { | 
|---|
| 6020 | if (tx_link_id >= 0 && tx_link_id != link_id) | 
|---|
| 6021 | return -EINVAL; | 
|---|
| 6022 |  | 
|---|
| 6023 | config->tx_wdev = dev->ieee80211_ptr; | 
|---|
| 6024 | } | 
|---|
| 6025 | } else if (!config->index) { | 
|---|
| 6026 | if (tx_link_id >= 0 && tx_link_id != link_id) | 
|---|
| 6027 | return -EINVAL; | 
|---|
| 6028 |  | 
|---|
| 6029 | config->tx_wdev = dev->ieee80211_ptr; | 
|---|
| 6030 | } else { | 
|---|
| 6031 | return -EINVAL; | 
|---|
| 6032 | } | 
|---|
| 6033 |  | 
|---|
| 6034 | return 0; | 
|---|
| 6035 | } | 
|---|
| 6036 |  | 
|---|
| 6037 | static struct cfg80211_mbssid_elems * | 
|---|
| 6038 | nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs) | 
|---|
| 6039 | { | 
|---|
| 6040 | struct nlattr *nl_elems; | 
|---|
| 6041 | struct cfg80211_mbssid_elems *elems; | 
|---|
| 6042 | int rem_elems; | 
|---|
| 6043 | u8 i = 0, num_elems = 0; | 
|---|
| 6044 |  | 
|---|
| 6045 | if (!wiphy->mbssid_max_interfaces) | 
|---|
| 6046 | return ERR_PTR(error: -EINVAL); | 
|---|
| 6047 |  | 
|---|
| 6048 | nla_for_each_nested(nl_elems, attrs, rem_elems) { | 
|---|
| 6049 | if (num_elems >= 255) | 
|---|
| 6050 | return ERR_PTR(error: -EINVAL); | 
|---|
| 6051 | num_elems++; | 
|---|
| 6052 | } | 
|---|
| 6053 |  | 
|---|
| 6054 | elems = kzalloc(struct_size(elems, elem, num_elems), GFP_KERNEL); | 
|---|
| 6055 | if (!elems) | 
|---|
| 6056 | return ERR_PTR(error: -ENOMEM); | 
|---|
| 6057 | elems->cnt = num_elems; | 
|---|
| 6058 |  | 
|---|
| 6059 | nla_for_each_nested(nl_elems, attrs, rem_elems) { | 
|---|
| 6060 | elems->elem[i].data = nla_data(nla: nl_elems); | 
|---|
| 6061 | elems->elem[i].len = nla_len(nla: nl_elems); | 
|---|
| 6062 | i++; | 
|---|
| 6063 | } | 
|---|
| 6064 | return elems; | 
|---|
| 6065 | } | 
|---|
| 6066 |  | 
|---|
| 6067 | static struct cfg80211_rnr_elems * | 
|---|
| 6068 | nl80211_parse_rnr_elems(struct wiphy *wiphy, struct nlattr *attrs, | 
|---|
| 6069 | struct netlink_ext_ack *extack) | 
|---|
| 6070 | { | 
|---|
| 6071 | struct nlattr *nl_elems; | 
|---|
| 6072 | struct cfg80211_rnr_elems *elems; | 
|---|
| 6073 | int rem_elems; | 
|---|
| 6074 | u8 i = 0, num_elems = 0; | 
|---|
| 6075 |  | 
|---|
| 6076 | nla_for_each_nested(nl_elems, attrs, rem_elems) { | 
|---|
| 6077 | int ret; | 
|---|
| 6078 |  | 
|---|
| 6079 | ret = validate_ie_attr(attr: nl_elems, extack); | 
|---|
| 6080 | if (ret) | 
|---|
| 6081 | return ERR_PTR(error: ret); | 
|---|
| 6082 |  | 
|---|
| 6083 | num_elems++; | 
|---|
| 6084 | } | 
|---|
| 6085 |  | 
|---|
| 6086 | elems = kzalloc(struct_size(elems, elem, num_elems), GFP_KERNEL); | 
|---|
| 6087 | if (!elems) | 
|---|
| 6088 | return ERR_PTR(error: -ENOMEM); | 
|---|
| 6089 | elems->cnt = num_elems; | 
|---|
| 6090 |  | 
|---|
| 6091 | nla_for_each_nested(nl_elems, attrs, rem_elems) { | 
|---|
| 6092 | elems->elem[i].data = nla_data(nla: nl_elems); | 
|---|
| 6093 | elems->elem[i].len = nla_len(nla: nl_elems); | 
|---|
| 6094 | i++; | 
|---|
| 6095 | } | 
|---|
| 6096 | return elems; | 
|---|
| 6097 | } | 
|---|
| 6098 |  | 
|---|
| 6099 | static int nl80211_parse_he_bss_color(struct nlattr *attrs, | 
|---|
| 6100 | struct cfg80211_he_bss_color *he_bss_color) | 
|---|
| 6101 | { | 
|---|
| 6102 | struct nlattr *tb[NL80211_HE_BSS_COLOR_ATTR_MAX + 1]; | 
|---|
| 6103 | int err; | 
|---|
| 6104 |  | 
|---|
| 6105 | err = nla_parse_nested(tb, maxtype: NL80211_HE_BSS_COLOR_ATTR_MAX, nla: attrs, | 
|---|
| 6106 | policy: he_bss_color_policy, NULL); | 
|---|
| 6107 | if (err) | 
|---|
| 6108 | return err; | 
|---|
| 6109 |  | 
|---|
| 6110 | if (!tb[NL80211_HE_BSS_COLOR_ATTR_COLOR]) | 
|---|
| 6111 | return -EINVAL; | 
|---|
| 6112 |  | 
|---|
| 6113 | he_bss_color->color = | 
|---|
| 6114 | nla_get_u8(nla: tb[NL80211_HE_BSS_COLOR_ATTR_COLOR]); | 
|---|
| 6115 | he_bss_color->enabled = | 
|---|
| 6116 | !nla_get_flag(nla: tb[NL80211_HE_BSS_COLOR_ATTR_DISABLED]); | 
|---|
| 6117 | he_bss_color->partial = | 
|---|
| 6118 | nla_get_flag(nla: tb[NL80211_HE_BSS_COLOR_ATTR_PARTIAL]); | 
|---|
| 6119 |  | 
|---|
| 6120 | return 0; | 
|---|
| 6121 | } | 
|---|
| 6122 |  | 
|---|
| 6123 | static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev, | 
|---|
| 6124 | struct nlattr *attrs[], | 
|---|
| 6125 | struct cfg80211_beacon_data *bcn, | 
|---|
| 6126 | struct netlink_ext_ack *extack) | 
|---|
| 6127 | { | 
|---|
| 6128 | bool haveinfo = false; | 
|---|
| 6129 | int err; | 
|---|
| 6130 |  | 
|---|
| 6131 | memset(s: bcn, c: 0, n: sizeof(*bcn)); | 
|---|
| 6132 |  | 
|---|
| 6133 | bcn->link_id = nl80211_link_id(attrs); | 
|---|
| 6134 |  | 
|---|
| 6135 | if (attrs[NL80211_ATTR_BEACON_HEAD]) { | 
|---|
| 6136 | bcn->head = nla_data(nla: attrs[NL80211_ATTR_BEACON_HEAD]); | 
|---|
| 6137 | bcn->head_len = nla_len(nla: attrs[NL80211_ATTR_BEACON_HEAD]); | 
|---|
| 6138 | if (!bcn->head_len) | 
|---|
| 6139 | return -EINVAL; | 
|---|
| 6140 | haveinfo = true; | 
|---|
| 6141 | } | 
|---|
| 6142 |  | 
|---|
| 6143 | if (attrs[NL80211_ATTR_BEACON_TAIL]) { | 
|---|
| 6144 | bcn->tail = nla_data(nla: attrs[NL80211_ATTR_BEACON_TAIL]); | 
|---|
| 6145 | bcn->tail_len = nla_len(nla: attrs[NL80211_ATTR_BEACON_TAIL]); | 
|---|
| 6146 | haveinfo = true; | 
|---|
| 6147 | } | 
|---|
| 6148 |  | 
|---|
| 6149 | if (!haveinfo) | 
|---|
| 6150 | return -EINVAL; | 
|---|
| 6151 |  | 
|---|
| 6152 | if (attrs[NL80211_ATTR_IE]) { | 
|---|
| 6153 | bcn->beacon_ies = nla_data(nla: attrs[NL80211_ATTR_IE]); | 
|---|
| 6154 | bcn->beacon_ies_len = nla_len(nla: attrs[NL80211_ATTR_IE]); | 
|---|
| 6155 | } | 
|---|
| 6156 |  | 
|---|
| 6157 | if (attrs[NL80211_ATTR_IE_PROBE_RESP]) { | 
|---|
| 6158 | bcn->proberesp_ies = | 
|---|
| 6159 | nla_data(nla: attrs[NL80211_ATTR_IE_PROBE_RESP]); | 
|---|
| 6160 | bcn->proberesp_ies_len = | 
|---|
| 6161 | nla_len(nla: attrs[NL80211_ATTR_IE_PROBE_RESP]); | 
|---|
| 6162 | } | 
|---|
| 6163 |  | 
|---|
| 6164 | if (attrs[NL80211_ATTR_IE_ASSOC_RESP]) { | 
|---|
| 6165 | bcn->assocresp_ies = | 
|---|
| 6166 | nla_data(nla: attrs[NL80211_ATTR_IE_ASSOC_RESP]); | 
|---|
| 6167 | bcn->assocresp_ies_len = | 
|---|
| 6168 | nla_len(nla: attrs[NL80211_ATTR_IE_ASSOC_RESP]); | 
|---|
| 6169 | } | 
|---|
| 6170 |  | 
|---|
| 6171 | if (attrs[NL80211_ATTR_PROBE_RESP]) { | 
|---|
| 6172 | bcn->probe_resp = nla_data(nla: attrs[NL80211_ATTR_PROBE_RESP]); | 
|---|
| 6173 | bcn->probe_resp_len = nla_len(nla: attrs[NL80211_ATTR_PROBE_RESP]); | 
|---|
| 6174 | } | 
|---|
| 6175 |  | 
|---|
| 6176 | if (attrs[NL80211_ATTR_FTM_RESPONDER]) { | 
|---|
| 6177 | struct nlattr *tb[NL80211_FTM_RESP_ATTR_MAX + 1]; | 
|---|
| 6178 |  | 
|---|
| 6179 | err = nla_parse_nested_deprecated(tb, | 
|---|
| 6180 | maxtype: NL80211_FTM_RESP_ATTR_MAX, | 
|---|
| 6181 | nla: attrs[NL80211_ATTR_FTM_RESPONDER], | 
|---|
| 6182 | NULL, NULL); | 
|---|
| 6183 | if (err) | 
|---|
| 6184 | return err; | 
|---|
| 6185 |  | 
|---|
| 6186 | if (tb[NL80211_FTM_RESP_ATTR_ENABLED] && | 
|---|
| 6187 | wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 6188 | ftidx: NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER)) | 
|---|
| 6189 | bcn->ftm_responder = 1; | 
|---|
| 6190 | else | 
|---|
| 6191 | return -EOPNOTSUPP; | 
|---|
| 6192 |  | 
|---|
| 6193 | if (tb[NL80211_FTM_RESP_ATTR_LCI]) { | 
|---|
| 6194 | bcn->lci = nla_data(nla: tb[NL80211_FTM_RESP_ATTR_LCI]); | 
|---|
| 6195 | bcn->lci_len = nla_len(nla: tb[NL80211_FTM_RESP_ATTR_LCI]); | 
|---|
| 6196 | } | 
|---|
| 6197 |  | 
|---|
| 6198 | if (tb[NL80211_FTM_RESP_ATTR_CIVICLOC]) { | 
|---|
| 6199 | bcn->civicloc = nla_data(nla: tb[NL80211_FTM_RESP_ATTR_CIVICLOC]); | 
|---|
| 6200 | bcn->civicloc_len = nla_len(nla: tb[NL80211_FTM_RESP_ATTR_CIVICLOC]); | 
|---|
| 6201 | } | 
|---|
| 6202 | } else { | 
|---|
| 6203 | bcn->ftm_responder = -1; | 
|---|
| 6204 | } | 
|---|
| 6205 |  | 
|---|
| 6206 | if (attrs[NL80211_ATTR_HE_BSS_COLOR]) { | 
|---|
| 6207 | err = nl80211_parse_he_bss_color(attrs: attrs[NL80211_ATTR_HE_BSS_COLOR], | 
|---|
| 6208 | he_bss_color: &bcn->he_bss_color); | 
|---|
| 6209 | if (err) | 
|---|
| 6210 | return err; | 
|---|
| 6211 | bcn->he_bss_color_valid = true; | 
|---|
| 6212 | } | 
|---|
| 6213 |  | 
|---|
| 6214 | if (attrs[NL80211_ATTR_MBSSID_ELEMS]) { | 
|---|
| 6215 | struct cfg80211_mbssid_elems *mbssid = | 
|---|
| 6216 | nl80211_parse_mbssid_elems(wiphy: &rdev->wiphy, | 
|---|
| 6217 | attrs: attrs[NL80211_ATTR_MBSSID_ELEMS]); | 
|---|
| 6218 |  | 
|---|
| 6219 | if (IS_ERR(ptr: mbssid)) | 
|---|
| 6220 | return PTR_ERR(ptr: mbssid); | 
|---|
| 6221 |  | 
|---|
| 6222 | bcn->mbssid_ies = mbssid; | 
|---|
| 6223 |  | 
|---|
| 6224 | if (bcn->mbssid_ies && attrs[NL80211_ATTR_EMA_RNR_ELEMS]) { | 
|---|
| 6225 | struct cfg80211_rnr_elems *rnr = | 
|---|
| 6226 | nl80211_parse_rnr_elems(wiphy: &rdev->wiphy, | 
|---|
| 6227 | attrs: attrs[NL80211_ATTR_EMA_RNR_ELEMS], | 
|---|
| 6228 | extack); | 
|---|
| 6229 |  | 
|---|
| 6230 | if (IS_ERR(ptr: rnr)) | 
|---|
| 6231 | return PTR_ERR(ptr: rnr); | 
|---|
| 6232 |  | 
|---|
| 6233 | if (rnr && rnr->cnt < bcn->mbssid_ies->cnt) | 
|---|
| 6234 | return -EINVAL; | 
|---|
| 6235 |  | 
|---|
| 6236 | bcn->rnr_ies = rnr; | 
|---|
| 6237 | } | 
|---|
| 6238 | } | 
|---|
| 6239 |  | 
|---|
| 6240 | return 0; | 
|---|
| 6241 | } | 
|---|
| 6242 |  | 
|---|
| 6243 | static int nl80211_parse_he_obss_pd(struct nlattr *attrs, | 
|---|
| 6244 | struct ieee80211_he_obss_pd *he_obss_pd) | 
|---|
| 6245 | { | 
|---|
| 6246 | struct nlattr *tb[NL80211_HE_OBSS_PD_ATTR_MAX + 1]; | 
|---|
| 6247 | int err; | 
|---|
| 6248 |  | 
|---|
| 6249 | err = nla_parse_nested(tb, maxtype: NL80211_HE_OBSS_PD_ATTR_MAX, nla: attrs, | 
|---|
| 6250 | policy: he_obss_pd_policy, NULL); | 
|---|
| 6251 | if (err) | 
|---|
| 6252 | return err; | 
|---|
| 6253 |  | 
|---|
| 6254 | if (!tb[NL80211_HE_OBSS_PD_ATTR_SR_CTRL]) | 
|---|
| 6255 | return -EINVAL; | 
|---|
| 6256 |  | 
|---|
| 6257 | he_obss_pd->sr_ctrl = nla_get_u8(nla: tb[NL80211_HE_OBSS_PD_ATTR_SR_CTRL]); | 
|---|
| 6258 |  | 
|---|
| 6259 | if (tb[NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET]) | 
|---|
| 6260 | he_obss_pd->min_offset = | 
|---|
| 6261 | nla_get_u8(nla: tb[NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET]); | 
|---|
| 6262 | if (tb[NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET]) | 
|---|
| 6263 | he_obss_pd->max_offset = | 
|---|
| 6264 | nla_get_u8(nla: tb[NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET]); | 
|---|
| 6265 | if (tb[NL80211_HE_OBSS_PD_ATTR_NON_SRG_MAX_OFFSET]) | 
|---|
| 6266 | he_obss_pd->non_srg_max_offset = | 
|---|
| 6267 | nla_get_u8(nla: tb[NL80211_HE_OBSS_PD_ATTR_NON_SRG_MAX_OFFSET]); | 
|---|
| 6268 |  | 
|---|
| 6269 | if (he_obss_pd->min_offset > he_obss_pd->max_offset) | 
|---|
| 6270 | return -EINVAL; | 
|---|
| 6271 |  | 
|---|
| 6272 | if (tb[NL80211_HE_OBSS_PD_ATTR_BSS_COLOR_BITMAP]) | 
|---|
| 6273 | memcpy(to: he_obss_pd->bss_color_bitmap, | 
|---|
| 6274 | from: nla_data(nla: tb[NL80211_HE_OBSS_PD_ATTR_BSS_COLOR_BITMAP]), | 
|---|
| 6275 | len: sizeof(he_obss_pd->bss_color_bitmap)); | 
|---|
| 6276 |  | 
|---|
| 6277 | if (tb[NL80211_HE_OBSS_PD_ATTR_PARTIAL_BSSID_BITMAP]) | 
|---|
| 6278 | memcpy(to: he_obss_pd->partial_bssid_bitmap, | 
|---|
| 6279 | from: nla_data(nla: tb[NL80211_HE_OBSS_PD_ATTR_PARTIAL_BSSID_BITMAP]), | 
|---|
| 6280 | len: sizeof(he_obss_pd->partial_bssid_bitmap)); | 
|---|
| 6281 |  | 
|---|
| 6282 | he_obss_pd->enable = true; | 
|---|
| 6283 |  | 
|---|
| 6284 | return 0; | 
|---|
| 6285 | } | 
|---|
| 6286 |  | 
|---|
| 6287 | static int nl80211_parse_fils_discovery(struct cfg80211_registered_device *rdev, | 
|---|
| 6288 | struct nlattr *attrs, | 
|---|
| 6289 | struct cfg80211_fils_discovery *fd) | 
|---|
| 6290 | { | 
|---|
| 6291 | struct nlattr *tb[NL80211_FILS_DISCOVERY_ATTR_MAX + 1]; | 
|---|
| 6292 | int ret; | 
|---|
| 6293 |  | 
|---|
| 6294 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 6295 | ftidx: NL80211_EXT_FEATURE_FILS_DISCOVERY)) | 
|---|
| 6296 | return -EINVAL; | 
|---|
| 6297 |  | 
|---|
| 6298 | ret = nla_parse_nested(tb, maxtype: NL80211_FILS_DISCOVERY_ATTR_MAX, nla: attrs, | 
|---|
| 6299 | NULL, NULL); | 
|---|
| 6300 | if (ret) | 
|---|
| 6301 | return ret; | 
|---|
| 6302 |  | 
|---|
| 6303 | if (!tb[NL80211_FILS_DISCOVERY_ATTR_INT_MIN] && | 
|---|
| 6304 | !tb[NL80211_FILS_DISCOVERY_ATTR_INT_MAX] && | 
|---|
| 6305 | !tb[NL80211_FILS_DISCOVERY_ATTR_TMPL]) { | 
|---|
| 6306 | fd->update = true; | 
|---|
| 6307 | return 0; | 
|---|
| 6308 | } | 
|---|
| 6309 |  | 
|---|
| 6310 | if (!tb[NL80211_FILS_DISCOVERY_ATTR_INT_MIN] || | 
|---|
| 6311 | !tb[NL80211_FILS_DISCOVERY_ATTR_INT_MAX] || | 
|---|
| 6312 | !tb[NL80211_FILS_DISCOVERY_ATTR_TMPL]) | 
|---|
| 6313 | return -EINVAL; | 
|---|
| 6314 |  | 
|---|
| 6315 | fd->tmpl_len = nla_len(nla: tb[NL80211_FILS_DISCOVERY_ATTR_TMPL]); | 
|---|
| 6316 | fd->tmpl = nla_data(nla: tb[NL80211_FILS_DISCOVERY_ATTR_TMPL]); | 
|---|
| 6317 | fd->min_interval = nla_get_u32(nla: tb[NL80211_FILS_DISCOVERY_ATTR_INT_MIN]); | 
|---|
| 6318 | fd->max_interval = nla_get_u32(nla: tb[NL80211_FILS_DISCOVERY_ATTR_INT_MAX]); | 
|---|
| 6319 | fd->update = true; | 
|---|
| 6320 | return 0; | 
|---|
| 6321 | } | 
|---|
| 6322 |  | 
|---|
| 6323 | static int | 
|---|
| 6324 | nl80211_parse_unsol_bcast_probe_resp(struct cfg80211_registered_device *rdev, | 
|---|
| 6325 | struct nlattr *attrs, | 
|---|
| 6326 | struct cfg80211_unsol_bcast_probe_resp *presp) | 
|---|
| 6327 | { | 
|---|
| 6328 | struct nlattr *tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_MAX + 1]; | 
|---|
| 6329 | int ret; | 
|---|
| 6330 |  | 
|---|
| 6331 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 6332 | ftidx: NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP)) | 
|---|
| 6333 | return -EINVAL; | 
|---|
| 6334 |  | 
|---|
| 6335 | ret = nla_parse_nested(tb, maxtype: NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_MAX, | 
|---|
| 6336 | nla: attrs, NULL, NULL); | 
|---|
| 6337 | if (ret) | 
|---|
| 6338 | return ret; | 
|---|
| 6339 |  | 
|---|
| 6340 | if (!tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT] && | 
|---|
| 6341 | !tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL]) { | 
|---|
| 6342 | presp->update = true; | 
|---|
| 6343 | return 0; | 
|---|
| 6344 | } | 
|---|
| 6345 |  | 
|---|
| 6346 | if (!tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT] || | 
|---|
| 6347 | !tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL]) | 
|---|
| 6348 | return -EINVAL; | 
|---|
| 6349 |  | 
|---|
| 6350 | presp->tmpl = nla_data(nla: tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL]); | 
|---|
| 6351 | presp->tmpl_len = nla_len(nla: tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL]); | 
|---|
| 6352 | presp->interval = nla_get_u32(nla: tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT]); | 
|---|
| 6353 | presp->update = true; | 
|---|
| 6354 | return 0; | 
|---|
| 6355 | } | 
|---|
| 6356 |  | 
|---|
| 6357 | static void nl80211_check_ap_rate_selectors(struct cfg80211_ap_settings *params, | 
|---|
| 6358 | const struct element *rates) | 
|---|
| 6359 | { | 
|---|
| 6360 | int i; | 
|---|
| 6361 |  | 
|---|
| 6362 | if (!rates) | 
|---|
| 6363 | return; | 
|---|
| 6364 |  | 
|---|
| 6365 | for (i = 0; i < rates->datalen; i++) { | 
|---|
| 6366 | if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_HT_PHY) | 
|---|
| 6367 | params->ht_required = true; | 
|---|
| 6368 | if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_VHT_PHY) | 
|---|
| 6369 | params->vht_required = true; | 
|---|
| 6370 | if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_HE_PHY) | 
|---|
| 6371 | params->he_required = true; | 
|---|
| 6372 | if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_SAE_H2E) | 
|---|
| 6373 | params->sae_h2e_required = true; | 
|---|
| 6374 | } | 
|---|
| 6375 | } | 
|---|
| 6376 |  | 
|---|
| 6377 | /* | 
|---|
| 6378 | * Since the nl80211 API didn't include, from the beginning, attributes about | 
|---|
| 6379 | * HT/VHT requirements/capabilities, we parse them out of the IEs for the | 
|---|
| 6380 | * benefit of drivers that rebuild IEs in the firmware. | 
|---|
| 6381 | */ | 
|---|
| 6382 | static int nl80211_calculate_ap_params(struct cfg80211_ap_settings *params) | 
|---|
| 6383 | { | 
|---|
| 6384 | const struct cfg80211_beacon_data *bcn = ¶ms->beacon; | 
|---|
| 6385 | size_t ies_len = bcn->tail_len; | 
|---|
| 6386 | const u8 *ies = bcn->tail; | 
|---|
| 6387 | const struct element *rates; | 
|---|
| 6388 | const struct element *cap; | 
|---|
| 6389 |  | 
|---|
| 6390 | rates = cfg80211_find_elem(eid: WLAN_EID_SUPP_RATES, ies, len: ies_len); | 
|---|
| 6391 | nl80211_check_ap_rate_selectors(params, rates); | 
|---|
| 6392 |  | 
|---|
| 6393 | rates = cfg80211_find_elem(eid: WLAN_EID_EXT_SUPP_RATES, ies, len: ies_len); | 
|---|
| 6394 | nl80211_check_ap_rate_selectors(params, rates); | 
|---|
| 6395 |  | 
|---|
| 6396 | cap = cfg80211_find_elem(eid: WLAN_EID_HT_CAPABILITY, ies, len: ies_len); | 
|---|
| 6397 | if (cap && cap->datalen >= sizeof(*params->ht_cap)) | 
|---|
| 6398 | params->ht_cap = (void *)cap->data; | 
|---|
| 6399 | cap = cfg80211_find_elem(eid: WLAN_EID_VHT_CAPABILITY, ies, len: ies_len); | 
|---|
| 6400 | if (cap && cap->datalen >= sizeof(*params->vht_cap)) | 
|---|
| 6401 | params->vht_cap = (void *)cap->data; | 
|---|
| 6402 | cap = cfg80211_find_ext_elem(ext_eid: WLAN_EID_EXT_HE_CAPABILITY, ies, len: ies_len); | 
|---|
| 6403 | if (cap && cap->datalen >= sizeof(*params->he_cap) + 1) | 
|---|
| 6404 | params->he_cap = (void *)(cap->data + 1); | 
|---|
| 6405 | cap = cfg80211_find_ext_elem(ext_eid: WLAN_EID_EXT_HE_OPERATION, ies, len: ies_len); | 
|---|
| 6406 | if (cap && cap->datalen >= sizeof(*params->he_oper) + 1) | 
|---|
| 6407 | params->he_oper = (void *)(cap->data + 1); | 
|---|
| 6408 | cap = cfg80211_find_ext_elem(ext_eid: WLAN_EID_EXT_EHT_CAPABILITY, ies, len: ies_len); | 
|---|
| 6409 | if (cap) { | 
|---|
| 6410 | if (!cap->datalen) | 
|---|
| 6411 | return -EINVAL; | 
|---|
| 6412 | params->eht_cap = (void *)(cap->data + 1); | 
|---|
| 6413 | if (!ieee80211_eht_capa_size_ok(he_capa: (const u8 *)params->he_cap, | 
|---|
| 6414 | data: (const u8 *)params->eht_cap, | 
|---|
| 6415 | len: cap->datalen - 1, from_ap: true)) | 
|---|
| 6416 | return -EINVAL; | 
|---|
| 6417 | } | 
|---|
| 6418 | cap = cfg80211_find_ext_elem(ext_eid: WLAN_EID_EXT_EHT_OPERATION, ies, len: ies_len); | 
|---|
| 6419 | if (cap) { | 
|---|
| 6420 | if (!cap->datalen) | 
|---|
| 6421 | return -EINVAL; | 
|---|
| 6422 | params->eht_oper = (void *)(cap->data + 1); | 
|---|
| 6423 | if (!ieee80211_eht_oper_size_ok(data: (const u8 *)params->eht_oper, | 
|---|
| 6424 | len: cap->datalen - 1)) | 
|---|
| 6425 | return -EINVAL; | 
|---|
| 6426 | } | 
|---|
| 6427 | return 0; | 
|---|
| 6428 | } | 
|---|
| 6429 |  | 
|---|
| 6430 | static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev, | 
|---|
| 6431 | struct cfg80211_ap_settings *params) | 
|---|
| 6432 | { | 
|---|
| 6433 | struct wireless_dev *wdev; | 
|---|
| 6434 |  | 
|---|
| 6435 | list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { | 
|---|
| 6436 | if (wdev->iftype != NL80211_IFTYPE_AP && | 
|---|
| 6437 | wdev->iftype != NL80211_IFTYPE_P2P_GO) | 
|---|
| 6438 | continue; | 
|---|
| 6439 |  | 
|---|
| 6440 | if (!wdev->u.ap.preset_chandef.chan) | 
|---|
| 6441 | continue; | 
|---|
| 6442 |  | 
|---|
| 6443 | params->chandef = wdev->u.ap.preset_chandef; | 
|---|
| 6444 | return true; | 
|---|
| 6445 | } | 
|---|
| 6446 |  | 
|---|
| 6447 | return false; | 
|---|
| 6448 | } | 
|---|
| 6449 |  | 
|---|
| 6450 | static bool nl80211_valid_auth_type(struct cfg80211_registered_device *rdev, | 
|---|
| 6451 | enum nl80211_auth_type auth_type, | 
|---|
| 6452 | enum nl80211_commands cmd) | 
|---|
| 6453 | { | 
|---|
| 6454 | if (auth_type > NL80211_AUTHTYPE_MAX) | 
|---|
| 6455 | return false; | 
|---|
| 6456 |  | 
|---|
| 6457 | switch (cmd) { | 
|---|
| 6458 | case NL80211_CMD_AUTHENTICATE: | 
|---|
| 6459 | if (!(rdev->wiphy.features & NL80211_FEATURE_SAE) && | 
|---|
| 6460 | auth_type == NL80211_AUTHTYPE_SAE) | 
|---|
| 6461 | return false; | 
|---|
| 6462 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 6463 | ftidx: NL80211_EXT_FEATURE_FILS_STA) && | 
|---|
| 6464 | (auth_type == NL80211_AUTHTYPE_FILS_SK || | 
|---|
| 6465 | auth_type == NL80211_AUTHTYPE_FILS_SK_PFS || | 
|---|
| 6466 | auth_type == NL80211_AUTHTYPE_FILS_PK)) | 
|---|
| 6467 | return false; | 
|---|
| 6468 | return true; | 
|---|
| 6469 | case NL80211_CMD_CONNECT: | 
|---|
| 6470 | if (!(rdev->wiphy.features & NL80211_FEATURE_SAE) && | 
|---|
| 6471 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 6472 | ftidx: NL80211_EXT_FEATURE_SAE_OFFLOAD) && | 
|---|
| 6473 | auth_type == NL80211_AUTHTYPE_SAE) | 
|---|
| 6474 | return false; | 
|---|
| 6475 |  | 
|---|
| 6476 | /* FILS with SK PFS or PK not supported yet */ | 
|---|
| 6477 | if (auth_type == NL80211_AUTHTYPE_FILS_SK_PFS || | 
|---|
| 6478 | auth_type == NL80211_AUTHTYPE_FILS_PK) | 
|---|
| 6479 | return false; | 
|---|
| 6480 | if (!wiphy_ext_feature_isset( | 
|---|
| 6481 | wiphy: &rdev->wiphy, | 
|---|
| 6482 | ftidx: NL80211_EXT_FEATURE_FILS_SK_OFFLOAD) && | 
|---|
| 6483 | auth_type == NL80211_AUTHTYPE_FILS_SK) | 
|---|
| 6484 | return false; | 
|---|
| 6485 | return true; | 
|---|
| 6486 | case NL80211_CMD_START_AP: | 
|---|
| 6487 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 6488 | ftidx: NL80211_EXT_FEATURE_SAE_OFFLOAD_AP) && | 
|---|
| 6489 | auth_type == NL80211_AUTHTYPE_SAE) | 
|---|
| 6490 | return false; | 
|---|
| 6491 | /* FILS not supported yet */ | 
|---|
| 6492 | if (auth_type == NL80211_AUTHTYPE_FILS_SK || | 
|---|
| 6493 | auth_type == NL80211_AUTHTYPE_FILS_SK_PFS || | 
|---|
| 6494 | auth_type == NL80211_AUTHTYPE_FILS_PK) | 
|---|
| 6495 | return false; | 
|---|
| 6496 | return true; | 
|---|
| 6497 | default: | 
|---|
| 6498 | return false; | 
|---|
| 6499 | } | 
|---|
| 6500 | } | 
|---|
| 6501 |  | 
|---|
| 6502 | static void nl80211_send_ap_started(struct wireless_dev *wdev, | 
|---|
| 6503 | unsigned int link_id) | 
|---|
| 6504 | { | 
|---|
| 6505 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 6506 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 6507 | struct sk_buff *msg; | 
|---|
| 6508 | void *hdr; | 
|---|
| 6509 |  | 
|---|
| 6510 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 6511 | if (!msg) | 
|---|
| 6512 | return; | 
|---|
| 6513 |  | 
|---|
| 6514 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_START_AP); | 
|---|
| 6515 | if (!hdr) | 
|---|
| 6516 | goto out; | 
|---|
| 6517 |  | 
|---|
| 6518 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 6519 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: wdev->netdev->ifindex) || | 
|---|
| 6520 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), | 
|---|
| 6521 | padattr: NL80211_ATTR_PAD) || | 
|---|
| 6522 | (wdev->u.ap.ssid_len && | 
|---|
| 6523 | nla_put(skb: msg, NL80211_ATTR_SSID, attrlen: wdev->u.ap.ssid_len, | 
|---|
| 6524 | data: wdev->u.ap.ssid)) || | 
|---|
| 6525 | (wdev->valid_links && | 
|---|
| 6526 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: link_id))) | 
|---|
| 6527 | goto out; | 
|---|
| 6528 |  | 
|---|
| 6529 | genlmsg_end(skb: msg, hdr); | 
|---|
| 6530 |  | 
|---|
| 6531 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy), skb: msg, portid: 0, | 
|---|
| 6532 | group: NL80211_MCGRP_MLME, GFP_KERNEL); | 
|---|
| 6533 | return; | 
|---|
| 6534 | out: | 
|---|
| 6535 | nlmsg_free(skb: msg); | 
|---|
| 6536 | } | 
|---|
| 6537 |  | 
|---|
| 6538 | static int nl80211_validate_ap_phy_operation(struct cfg80211_ap_settings *params) | 
|---|
| 6539 | { | 
|---|
| 6540 | struct ieee80211_channel *channel = params->chandef.chan; | 
|---|
| 6541 |  | 
|---|
| 6542 | if ((params->he_cap ||  params->he_oper) && | 
|---|
| 6543 | (channel->flags & IEEE80211_CHAN_NO_HE)) | 
|---|
| 6544 | return -EOPNOTSUPP; | 
|---|
| 6545 |  | 
|---|
| 6546 | if ((params->eht_cap || params->eht_oper) && | 
|---|
| 6547 | (channel->flags & IEEE80211_CHAN_NO_EHT)) | 
|---|
| 6548 | return -EOPNOTSUPP; | 
|---|
| 6549 |  | 
|---|
| 6550 | return 0; | 
|---|
| 6551 | } | 
|---|
| 6552 |  | 
|---|
| 6553 | static int | 
|---|
| 6554 | nl80211_parse_s1g_short_beacon(struct cfg80211_registered_device *rdev, | 
|---|
| 6555 | struct nlattr *attrs, | 
|---|
| 6556 | struct cfg80211_s1g_short_beacon *sb) | 
|---|
| 6557 | { | 
|---|
| 6558 | struct nlattr *tb[NL80211_S1G_SHORT_BEACON_ATTR_MAX + 1]; | 
|---|
| 6559 | int ret; | 
|---|
| 6560 |  | 
|---|
| 6561 | if (!rdev->wiphy.bands[NL80211_BAND_S1GHZ]) | 
|---|
| 6562 | return -EINVAL; | 
|---|
| 6563 |  | 
|---|
| 6564 | ret = nla_parse_nested(tb, maxtype: NL80211_S1G_SHORT_BEACON_ATTR_MAX, nla: attrs, | 
|---|
| 6565 | NULL, NULL); | 
|---|
| 6566 | if (ret) | 
|---|
| 6567 | return ret; | 
|---|
| 6568 |  | 
|---|
| 6569 | /* Short beacon tail is optional (i.e might only include the TIM) */ | 
|---|
| 6570 | if (!tb[NL80211_S1G_SHORT_BEACON_ATTR_HEAD]) | 
|---|
| 6571 | return -EINVAL; | 
|---|
| 6572 |  | 
|---|
| 6573 | sb->short_head = nla_data(nla: tb[NL80211_S1G_SHORT_BEACON_ATTR_HEAD]); | 
|---|
| 6574 | sb->short_head_len = nla_len(nla: tb[NL80211_S1G_SHORT_BEACON_ATTR_HEAD]); | 
|---|
| 6575 | sb->short_tail_len = 0; | 
|---|
| 6576 |  | 
|---|
| 6577 | if (tb[NL80211_S1G_SHORT_BEACON_ATTR_TAIL]) { | 
|---|
| 6578 | sb->short_tail = | 
|---|
| 6579 | nla_data(nla: tb[NL80211_S1G_SHORT_BEACON_ATTR_TAIL]); | 
|---|
| 6580 | sb->short_tail_len = | 
|---|
| 6581 | nla_len(nla: tb[NL80211_S1G_SHORT_BEACON_ATTR_TAIL]); | 
|---|
| 6582 | } | 
|---|
| 6583 |  | 
|---|
| 6584 | sb->update = true; | 
|---|
| 6585 | return 0; | 
|---|
| 6586 | } | 
|---|
| 6587 |  | 
|---|
| 6588 | static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 6589 | { | 
|---|
| 6590 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 6591 | struct cfg80211_beaconing_check_config beacon_check = {}; | 
|---|
| 6592 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); | 
|---|
| 6593 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 6594 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 6595 | struct cfg80211_ap_settings *params; | 
|---|
| 6596 | int err; | 
|---|
| 6597 |  | 
|---|
| 6598 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | 
|---|
| 6599 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) | 
|---|
| 6600 | return -EOPNOTSUPP; | 
|---|
| 6601 |  | 
|---|
| 6602 | if (!rdev->ops->start_ap) | 
|---|
| 6603 | return -EOPNOTSUPP; | 
|---|
| 6604 |  | 
|---|
| 6605 | if (wdev->links[link_id].cac_started) | 
|---|
| 6606 | return -EBUSY; | 
|---|
| 6607 |  | 
|---|
| 6608 | if (wdev->links[link_id].ap.beacon_interval) | 
|---|
| 6609 | return -EALREADY; | 
|---|
| 6610 |  | 
|---|
| 6611 | /* these are required for START_AP */ | 
|---|
| 6612 | if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] || | 
|---|
| 6613 | !info->attrs[NL80211_ATTR_DTIM_PERIOD] || | 
|---|
| 6614 | !info->attrs[NL80211_ATTR_BEACON_HEAD]) | 
|---|
| 6615 | return -EINVAL; | 
|---|
| 6616 |  | 
|---|
| 6617 | if (info->attrs[NL80211_ATTR_SMPS_MODE] && | 
|---|
| 6618 | nla_get_u8(nla: info->attrs[NL80211_ATTR_SMPS_MODE]) != NL80211_SMPS_OFF) | 
|---|
| 6619 | return -EOPNOTSUPP; | 
|---|
| 6620 |  | 
|---|
| 6621 | params = kzalloc(sizeof(*params), GFP_KERNEL); | 
|---|
| 6622 | if (!params) | 
|---|
| 6623 | return -ENOMEM; | 
|---|
| 6624 |  | 
|---|
| 6625 | err = nl80211_parse_beacon(rdev, attrs: info->attrs, bcn: ¶ms->beacon, | 
|---|
| 6626 | extack: info->extack); | 
|---|
| 6627 | if (err) | 
|---|
| 6628 | goto out; | 
|---|
| 6629 |  | 
|---|
| 6630 | params->beacon_interval = | 
|---|
| 6631 | nla_get_u32(nla: info->attrs[NL80211_ATTR_BEACON_INTERVAL]); | 
|---|
| 6632 | params->dtim_period = | 
|---|
| 6633 | nla_get_u32(nla: info->attrs[NL80211_ATTR_DTIM_PERIOD]); | 
|---|
| 6634 |  | 
|---|
| 6635 | err = cfg80211_validate_beacon_int(rdev, iftype: dev->ieee80211_ptr->iftype, | 
|---|
| 6636 | beacon_int: params->beacon_interval); | 
|---|
| 6637 | if (err) | 
|---|
| 6638 | goto out; | 
|---|
| 6639 |  | 
|---|
| 6640 | /* | 
|---|
| 6641 | * In theory, some of these attributes should be required here | 
|---|
| 6642 | * but since they were not used when the command was originally | 
|---|
| 6643 | * added, keep them optional for old user space programs to let | 
|---|
| 6644 | * them continue to work with drivers that do not need the | 
|---|
| 6645 | * additional information -- drivers must check! | 
|---|
| 6646 | */ | 
|---|
| 6647 | if (info->attrs[NL80211_ATTR_SSID]) { | 
|---|
| 6648 | params->ssid = nla_data(nla: info->attrs[NL80211_ATTR_SSID]); | 
|---|
| 6649 | params->ssid_len = | 
|---|
| 6650 | nla_len(nla: info->attrs[NL80211_ATTR_SSID]); | 
|---|
| 6651 | if (params->ssid_len == 0) { | 
|---|
| 6652 | err = -EINVAL; | 
|---|
| 6653 | goto out; | 
|---|
| 6654 | } | 
|---|
| 6655 |  | 
|---|
| 6656 | if (wdev->u.ap.ssid_len && | 
|---|
| 6657 | (wdev->u.ap.ssid_len != params->ssid_len || | 
|---|
| 6658 | memcmp(wdev->u.ap.ssid, params->ssid, params->ssid_len))) { | 
|---|
| 6659 | /* require identical SSID for MLO */ | 
|---|
| 6660 | err = -EINVAL; | 
|---|
| 6661 | goto out; | 
|---|
| 6662 | } | 
|---|
| 6663 | } else if (wdev->valid_links) { | 
|---|
| 6664 | /* require SSID for MLO */ | 
|---|
| 6665 | err = -EINVAL; | 
|---|
| 6666 | goto out; | 
|---|
| 6667 | } | 
|---|
| 6668 |  | 
|---|
| 6669 | if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) | 
|---|
| 6670 | params->hidden_ssid = nla_get_u32( | 
|---|
| 6671 | nla: info->attrs[NL80211_ATTR_HIDDEN_SSID]); | 
|---|
| 6672 |  | 
|---|
| 6673 | params->privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; | 
|---|
| 6674 |  | 
|---|
| 6675 | if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { | 
|---|
| 6676 | params->auth_type = nla_get_u32( | 
|---|
| 6677 | nla: info->attrs[NL80211_ATTR_AUTH_TYPE]); | 
|---|
| 6678 | if (!nl80211_valid_auth_type(rdev, auth_type: params->auth_type, | 
|---|
| 6679 | cmd: NL80211_CMD_START_AP)) { | 
|---|
| 6680 | err = -EINVAL; | 
|---|
| 6681 | goto out; | 
|---|
| 6682 | } | 
|---|
| 6683 | } else | 
|---|
| 6684 | params->auth_type = NL80211_AUTHTYPE_AUTOMATIC; | 
|---|
| 6685 |  | 
|---|
| 6686 | err = nl80211_crypto_settings(rdev, info, settings: ¶ms->crypto, | 
|---|
| 6687 | NL80211_MAX_NR_CIPHER_SUITES); | 
|---|
| 6688 | if (err) | 
|---|
| 6689 | goto out; | 
|---|
| 6690 |  | 
|---|
| 6691 | if (info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]) { | 
|---|
| 6692 | if (!(rdev->wiphy.features & NL80211_FEATURE_INACTIVITY_TIMER)) { | 
|---|
| 6693 | err = -EOPNOTSUPP; | 
|---|
| 6694 | goto out; | 
|---|
| 6695 | } | 
|---|
| 6696 | params->inactivity_timeout = nla_get_u16( | 
|---|
| 6697 | nla: info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]); | 
|---|
| 6698 | } | 
|---|
| 6699 |  | 
|---|
| 6700 | if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) { | 
|---|
| 6701 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) { | 
|---|
| 6702 | err = -EINVAL; | 
|---|
| 6703 | goto out; | 
|---|
| 6704 | } | 
|---|
| 6705 | params->p2p_ctwindow = | 
|---|
| 6706 | nla_get_u8(nla: info->attrs[NL80211_ATTR_P2P_CTWINDOW]); | 
|---|
| 6707 | if (params->p2p_ctwindow != 0 && | 
|---|
| 6708 | !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN)) { | 
|---|
| 6709 | err = -EINVAL; | 
|---|
| 6710 | goto out; | 
|---|
| 6711 | } | 
|---|
| 6712 | } | 
|---|
| 6713 |  | 
|---|
| 6714 | if (info->attrs[NL80211_ATTR_P2P_OPPPS]) { | 
|---|
| 6715 | u8 tmp; | 
|---|
| 6716 |  | 
|---|
| 6717 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) { | 
|---|
| 6718 | err = -EINVAL; | 
|---|
| 6719 | goto out; | 
|---|
| 6720 | } | 
|---|
| 6721 | tmp = nla_get_u8(nla: info->attrs[NL80211_ATTR_P2P_OPPPS]); | 
|---|
| 6722 | params->p2p_opp_ps = tmp; | 
|---|
| 6723 | if (params->p2p_opp_ps != 0 && | 
|---|
| 6724 | !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS)) { | 
|---|
| 6725 | err = -EINVAL; | 
|---|
| 6726 | goto out; | 
|---|
| 6727 | } | 
|---|
| 6728 | } | 
|---|
| 6729 |  | 
|---|
| 6730 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { | 
|---|
| 6731 | err = nl80211_parse_chandef(rdev, info, chandef: ¶ms->chandef); | 
|---|
| 6732 | if (err) | 
|---|
| 6733 | goto out; | 
|---|
| 6734 | } else if (wdev->valid_links) { | 
|---|
| 6735 | /* with MLD need to specify the channel configuration */ | 
|---|
| 6736 | err = -EINVAL; | 
|---|
| 6737 | goto out; | 
|---|
| 6738 | } else if (wdev->u.ap.preset_chandef.chan) { | 
|---|
| 6739 | params->chandef = wdev->u.ap.preset_chandef; | 
|---|
| 6740 | } else if (!nl80211_get_ap_channel(rdev, params)) { | 
|---|
| 6741 | err = -EINVAL; | 
|---|
| 6742 | goto out; | 
|---|
| 6743 | } | 
|---|
| 6744 |  | 
|---|
| 6745 | beacon_check.iftype = wdev->iftype; | 
|---|
| 6746 | beacon_check.relax = true; | 
|---|
| 6747 | beacon_check.reg_power = | 
|---|
| 6748 | cfg80211_get_6ghz_power_type(elems: params->beacon.tail, | 
|---|
| 6749 | elems_len: params->beacon.tail_len); | 
|---|
| 6750 | if (!cfg80211_reg_check_beaconing(wiphy: &rdev->wiphy, chandef: ¶ms->chandef, | 
|---|
| 6751 | cfg: &beacon_check)) { | 
|---|
| 6752 | err = -EINVAL; | 
|---|
| 6753 | goto out; | 
|---|
| 6754 | } | 
|---|
| 6755 |  | 
|---|
| 6756 | if (info->attrs[NL80211_ATTR_TX_RATES]) { | 
|---|
| 6757 | err = nl80211_parse_tx_bitrate_mask(info, attrs: info->attrs, | 
|---|
| 6758 | attr: NL80211_ATTR_TX_RATES, | 
|---|
| 6759 | mask: ¶ms->beacon_rate, | 
|---|
| 6760 | dev, default_all_enabled: false, link_id); | 
|---|
| 6761 | if (err) | 
|---|
| 6762 | goto out; | 
|---|
| 6763 |  | 
|---|
| 6764 | err = validate_beacon_tx_rate(rdev, band: params->chandef.chan->band, | 
|---|
| 6765 | beacon_rate: ¶ms->beacon_rate); | 
|---|
| 6766 | if (err) | 
|---|
| 6767 | goto out; | 
|---|
| 6768 | } | 
|---|
| 6769 |  | 
|---|
| 6770 | params->pbss = nla_get_flag(nla: info->attrs[NL80211_ATTR_PBSS]); | 
|---|
| 6771 | if (params->pbss && !rdev->wiphy.bands[NL80211_BAND_60GHZ]) { | 
|---|
| 6772 | err = -EOPNOTSUPP; | 
|---|
| 6773 | goto out; | 
|---|
| 6774 | } | 
|---|
| 6775 |  | 
|---|
| 6776 | if (info->attrs[NL80211_ATTR_ACL_POLICY]) { | 
|---|
| 6777 | params->acl = parse_acl_data(wiphy: &rdev->wiphy, info); | 
|---|
| 6778 | if (IS_ERR(ptr: params->acl)) { | 
|---|
| 6779 | err = PTR_ERR(ptr: params->acl); | 
|---|
| 6780 | params->acl = NULL; | 
|---|
| 6781 | goto out; | 
|---|
| 6782 | } | 
|---|
| 6783 | } | 
|---|
| 6784 |  | 
|---|
| 6785 | params->twt_responder = | 
|---|
| 6786 | nla_get_flag(nla: info->attrs[NL80211_ATTR_TWT_RESPONDER]); | 
|---|
| 6787 |  | 
|---|
| 6788 | if (info->attrs[NL80211_ATTR_HE_OBSS_PD]) { | 
|---|
| 6789 | err = nl80211_parse_he_obss_pd( | 
|---|
| 6790 | attrs: info->attrs[NL80211_ATTR_HE_OBSS_PD], | 
|---|
| 6791 | he_obss_pd: ¶ms->he_obss_pd); | 
|---|
| 6792 | if (err) | 
|---|
| 6793 | goto out; | 
|---|
| 6794 | } | 
|---|
| 6795 |  | 
|---|
| 6796 | if (info->attrs[NL80211_ATTR_FILS_DISCOVERY]) { | 
|---|
| 6797 | err = nl80211_parse_fils_discovery(rdev, | 
|---|
| 6798 | attrs: info->attrs[NL80211_ATTR_FILS_DISCOVERY], | 
|---|
| 6799 | fd: ¶ms->fils_discovery); | 
|---|
| 6800 | if (err) | 
|---|
| 6801 | goto out; | 
|---|
| 6802 | } | 
|---|
| 6803 |  | 
|---|
| 6804 | if (info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP]) { | 
|---|
| 6805 | err = nl80211_parse_unsol_bcast_probe_resp( | 
|---|
| 6806 | rdev, attrs: info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP], | 
|---|
| 6807 | presp: ¶ms->unsol_bcast_probe_resp); | 
|---|
| 6808 | if (err) | 
|---|
| 6809 | goto out; | 
|---|
| 6810 | } | 
|---|
| 6811 |  | 
|---|
| 6812 | if (info->attrs[NL80211_ATTR_MBSSID_CONFIG]) { | 
|---|
| 6813 | err = nl80211_parse_mbssid_config(wiphy: &rdev->wiphy, dev, link_id, | 
|---|
| 6814 | attrs: info->attrs[NL80211_ATTR_MBSSID_CONFIG], | 
|---|
| 6815 | config: ¶ms->mbssid_config, | 
|---|
| 6816 | num_elems: params->beacon.mbssid_ies ? | 
|---|
| 6817 | params->beacon.mbssid_ies->cnt : | 
|---|
| 6818 | 0); | 
|---|
| 6819 | if (err) | 
|---|
| 6820 | goto out; | 
|---|
| 6821 | } | 
|---|
| 6822 |  | 
|---|
| 6823 | if (!params->mbssid_config.ema && params->beacon.rnr_ies) { | 
|---|
| 6824 | err = -EINVAL; | 
|---|
| 6825 | goto out; | 
|---|
| 6826 | } | 
|---|
| 6827 |  | 
|---|
| 6828 | if (info->attrs[NL80211_ATTR_S1G_SHORT_BEACON]) { | 
|---|
| 6829 | if (!info->attrs[NL80211_ATTR_S1G_LONG_BEACON_PERIOD]) { | 
|---|
| 6830 | err = -EINVAL; | 
|---|
| 6831 | goto out; | 
|---|
| 6832 | } | 
|---|
| 6833 |  | 
|---|
| 6834 | params->s1g_long_beacon_period = nla_get_u8( | 
|---|
| 6835 | nla: info->attrs[NL80211_ATTR_S1G_LONG_BEACON_PERIOD]); | 
|---|
| 6836 |  | 
|---|
| 6837 | err = nl80211_parse_s1g_short_beacon( | 
|---|
| 6838 | rdev, attrs: info->attrs[NL80211_ATTR_S1G_SHORT_BEACON], | 
|---|
| 6839 | sb: ¶ms->s1g_short_beacon); | 
|---|
| 6840 | if (err) | 
|---|
| 6841 | goto out; | 
|---|
| 6842 | } | 
|---|
| 6843 |  | 
|---|
| 6844 | err = nl80211_calculate_ap_params(params); | 
|---|
| 6845 | if (err) | 
|---|
| 6846 | goto out; | 
|---|
| 6847 |  | 
|---|
| 6848 | err = nl80211_validate_ap_phy_operation(params); | 
|---|
| 6849 | if (err) | 
|---|
| 6850 | goto out; | 
|---|
| 6851 |  | 
|---|
| 6852 | if (info->attrs[NL80211_ATTR_AP_SETTINGS_FLAGS]) | 
|---|
| 6853 | params->flags = nla_get_u32( | 
|---|
| 6854 | nla: info->attrs[NL80211_ATTR_AP_SETTINGS_FLAGS]); | 
|---|
| 6855 | else if (info->attrs[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT]) | 
|---|
| 6856 | params->flags |= NL80211_AP_SETTINGS_EXTERNAL_AUTH_SUPPORT; | 
|---|
| 6857 |  | 
|---|
| 6858 | if (wdev->conn_owner_nlportid && | 
|---|
| 6859 | info->attrs[NL80211_ATTR_SOCKET_OWNER] && | 
|---|
| 6860 | wdev->conn_owner_nlportid != info->snd_portid) { | 
|---|
| 6861 | err = -EINVAL; | 
|---|
| 6862 | goto out; | 
|---|
| 6863 | } | 
|---|
| 6864 |  | 
|---|
| 6865 | /* FIXME: validate MLO/link-id against driver capabilities */ | 
|---|
| 6866 |  | 
|---|
| 6867 | err = rdev_start_ap(rdev, dev, settings: params); | 
|---|
| 6868 | if (!err) { | 
|---|
| 6869 | wdev->links[link_id].ap.beacon_interval = params->beacon_interval; | 
|---|
| 6870 | wdev->links[link_id].ap.chandef = params->chandef; | 
|---|
| 6871 | wdev->u.ap.ssid_len = params->ssid_len; | 
|---|
| 6872 | memcpy(to: wdev->u.ap.ssid, from: params->ssid, | 
|---|
| 6873 | len: params->ssid_len); | 
|---|
| 6874 |  | 
|---|
| 6875 | if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) | 
|---|
| 6876 | wdev->conn_owner_nlportid = info->snd_portid; | 
|---|
| 6877 |  | 
|---|
| 6878 | nl80211_send_ap_started(wdev, link_id); | 
|---|
| 6879 | } | 
|---|
| 6880 | out: | 
|---|
| 6881 | kfree(objp: params->acl); | 
|---|
| 6882 | kfree(objp: params->beacon.mbssid_ies); | 
|---|
| 6883 | if (params->mbssid_config.tx_wdev && | 
|---|
| 6884 | params->mbssid_config.tx_wdev->netdev && | 
|---|
| 6885 | params->mbssid_config.tx_wdev->netdev != dev) | 
|---|
| 6886 | dev_put(dev: params->mbssid_config.tx_wdev->netdev); | 
|---|
| 6887 | kfree(objp: params->beacon.rnr_ies); | 
|---|
| 6888 | kfree(objp: params); | 
|---|
| 6889 |  | 
|---|
| 6890 | return err; | 
|---|
| 6891 | } | 
|---|
| 6892 |  | 
|---|
| 6893 | static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 6894 | { | 
|---|
| 6895 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 6896 | struct cfg80211_beaconing_check_config beacon_check = {}; | 
|---|
| 6897 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); | 
|---|
| 6898 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 6899 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 6900 | struct cfg80211_ap_update *params; | 
|---|
| 6901 | struct nlattr *attr; | 
|---|
| 6902 | int err; | 
|---|
| 6903 |  | 
|---|
| 6904 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | 
|---|
| 6905 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) | 
|---|
| 6906 | return -EOPNOTSUPP; | 
|---|
| 6907 |  | 
|---|
| 6908 | if (!rdev->ops->change_beacon) | 
|---|
| 6909 | return -EOPNOTSUPP; | 
|---|
| 6910 |  | 
|---|
| 6911 | if (!wdev->links[link_id].ap.beacon_interval) | 
|---|
| 6912 | return -EINVAL; | 
|---|
| 6913 |  | 
|---|
| 6914 | params = kzalloc(sizeof(*params), GFP_KERNEL); | 
|---|
| 6915 | if (!params) | 
|---|
| 6916 | return -ENOMEM; | 
|---|
| 6917 |  | 
|---|
| 6918 | err = nl80211_parse_beacon(rdev, attrs: info->attrs, bcn: ¶ms->beacon, | 
|---|
| 6919 | extack: info->extack); | 
|---|
| 6920 | if (err) | 
|---|
| 6921 | goto out; | 
|---|
| 6922 |  | 
|---|
| 6923 | /* recheck beaconing is permitted with possibly changed power type */ | 
|---|
| 6924 | beacon_check.iftype = wdev->iftype; | 
|---|
| 6925 | beacon_check.relax = true; | 
|---|
| 6926 | beacon_check.reg_power = | 
|---|
| 6927 | cfg80211_get_6ghz_power_type(elems: params->beacon.tail, | 
|---|
| 6928 | elems_len: params->beacon.tail_len); | 
|---|
| 6929 | if (!cfg80211_reg_check_beaconing(wiphy: &rdev->wiphy, | 
|---|
| 6930 | chandef: &wdev->links[link_id].ap.chandef, | 
|---|
| 6931 | cfg: &beacon_check)) { | 
|---|
| 6932 | err = -EINVAL; | 
|---|
| 6933 | goto out; | 
|---|
| 6934 | } | 
|---|
| 6935 |  | 
|---|
| 6936 | attr = info->attrs[NL80211_ATTR_FILS_DISCOVERY]; | 
|---|
| 6937 | if (attr) { | 
|---|
| 6938 | err = nl80211_parse_fils_discovery(rdev, attrs: attr, | 
|---|
| 6939 | fd: ¶ms->fils_discovery); | 
|---|
| 6940 | if (err) | 
|---|
| 6941 | goto out; | 
|---|
| 6942 | } | 
|---|
| 6943 |  | 
|---|
| 6944 | attr = info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP]; | 
|---|
| 6945 | if (attr) { | 
|---|
| 6946 | err = nl80211_parse_unsol_bcast_probe_resp(rdev, attrs: attr, | 
|---|
| 6947 | presp: ¶ms->unsol_bcast_probe_resp); | 
|---|
| 6948 | if (err) | 
|---|
| 6949 | goto out; | 
|---|
| 6950 | } | 
|---|
| 6951 |  | 
|---|
| 6952 | attr = info->attrs[NL80211_ATTR_S1G_SHORT_BEACON]; | 
|---|
| 6953 | if (attr) { | 
|---|
| 6954 | err = nl80211_parse_s1g_short_beacon(rdev, attrs: attr, | 
|---|
| 6955 | sb: ¶ms->s1g_short_beacon); | 
|---|
| 6956 | if (err) | 
|---|
| 6957 | goto out; | 
|---|
| 6958 | } | 
|---|
| 6959 |  | 
|---|
| 6960 | err = rdev_change_beacon(rdev, dev, info: params); | 
|---|
| 6961 |  | 
|---|
| 6962 | out: | 
|---|
| 6963 | kfree(objp: params->beacon.mbssid_ies); | 
|---|
| 6964 | kfree(objp: params->beacon.rnr_ies); | 
|---|
| 6965 | kfree(objp: params); | 
|---|
| 6966 | return err; | 
|---|
| 6967 | } | 
|---|
| 6968 |  | 
|---|
| 6969 | static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 6970 | { | 
|---|
| 6971 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 6972 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); | 
|---|
| 6973 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 6974 |  | 
|---|
| 6975 | return cfg80211_stop_ap(rdev, dev, link: link_id, notify: false); | 
|---|
| 6976 | } | 
|---|
| 6977 |  | 
|---|
| 6978 | static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = { | 
|---|
| 6979 | [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG }, | 
|---|
| 6980 | [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG }, | 
|---|
| 6981 | [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG }, | 
|---|
| 6982 | [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG }, | 
|---|
| 6983 | [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG }, | 
|---|
| 6984 | [NL80211_STA_FLAG_TDLS_PEER] = { .type = NLA_FLAG }, | 
|---|
| 6985 | }; | 
|---|
| 6986 |  | 
|---|
| 6987 | static int parse_station_flags(struct genl_info *info, | 
|---|
| 6988 | enum nl80211_iftype iftype, | 
|---|
| 6989 | struct station_parameters *params) | 
|---|
| 6990 | { | 
|---|
| 6991 | struct nlattr *flags[NL80211_STA_FLAG_MAX + 1]; | 
|---|
| 6992 | struct nlattr *nla; | 
|---|
| 6993 | int flag; | 
|---|
| 6994 |  | 
|---|
| 6995 | /* | 
|---|
| 6996 | * Try parsing the new attribute first so userspace | 
|---|
| 6997 | * can specify both for older kernels. | 
|---|
| 6998 | */ | 
|---|
| 6999 | nla = info->attrs[NL80211_ATTR_STA_FLAGS2]; | 
|---|
| 7000 | if (nla) { | 
|---|
| 7001 | struct nl80211_sta_flag_update *sta_flags; | 
|---|
| 7002 |  | 
|---|
| 7003 | sta_flags = nla_data(nla); | 
|---|
| 7004 | params->sta_flags_mask = sta_flags->mask; | 
|---|
| 7005 | params->sta_flags_set = sta_flags->set; | 
|---|
| 7006 | params->sta_flags_set &= params->sta_flags_mask; | 
|---|
| 7007 | if ((params->sta_flags_mask | | 
|---|
| 7008 | params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID)) | 
|---|
| 7009 | return -EINVAL; | 
|---|
| 7010 | return 0; | 
|---|
| 7011 | } | 
|---|
| 7012 |  | 
|---|
| 7013 | /* if present, parse the old attribute */ | 
|---|
| 7014 |  | 
|---|
| 7015 | nla = info->attrs[NL80211_ATTR_STA_FLAGS]; | 
|---|
| 7016 | if (!nla) | 
|---|
| 7017 | return 0; | 
|---|
| 7018 |  | 
|---|
| 7019 | if (nla_parse_nested_deprecated(tb: flags, maxtype: NL80211_STA_FLAG_MAX, nla, policy: sta_flags_policy, extack: info->extack)) | 
|---|
| 7020 | return -EINVAL; | 
|---|
| 7021 |  | 
|---|
| 7022 | /* | 
|---|
| 7023 | * Only allow certain flags for interface types so that | 
|---|
| 7024 | * other attributes are silently ignored. Remember that | 
|---|
| 7025 | * this is backward compatibility code with old userspace | 
|---|
| 7026 | * and shouldn't be hit in other cases anyway. | 
|---|
| 7027 | */ | 
|---|
| 7028 | switch (iftype) { | 
|---|
| 7029 | case NL80211_IFTYPE_AP: | 
|---|
| 7030 | case NL80211_IFTYPE_AP_VLAN: | 
|---|
| 7031 | case NL80211_IFTYPE_P2P_GO: | 
|---|
| 7032 | params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) | | 
|---|
| 7033 | BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | | 
|---|
| 7034 | BIT(NL80211_STA_FLAG_WME) | | 
|---|
| 7035 | BIT(NL80211_STA_FLAG_MFP); | 
|---|
| 7036 | break; | 
|---|
| 7037 | case NL80211_IFTYPE_P2P_CLIENT: | 
|---|
| 7038 | case NL80211_IFTYPE_STATION: | 
|---|
| 7039 | params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) | | 
|---|
| 7040 | BIT(NL80211_STA_FLAG_TDLS_PEER); | 
|---|
| 7041 | break; | 
|---|
| 7042 | case NL80211_IFTYPE_MESH_POINT: | 
|---|
| 7043 | params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHENTICATED) | | 
|---|
| 7044 | BIT(NL80211_STA_FLAG_MFP) | | 
|---|
| 7045 | BIT(NL80211_STA_FLAG_AUTHORIZED); | 
|---|
| 7046 | break; | 
|---|
| 7047 | default: | 
|---|
| 7048 | return -EINVAL; | 
|---|
| 7049 | } | 
|---|
| 7050 |  | 
|---|
| 7051 | for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++) { | 
|---|
| 7052 | if (flags[flag]) { | 
|---|
| 7053 | params->sta_flags_set |= (1<<flag); | 
|---|
| 7054 |  | 
|---|
| 7055 | /* no longer support new API additions in old API */ | 
|---|
| 7056 | if (flag > NL80211_STA_FLAG_MAX_OLD_API) | 
|---|
| 7057 | return -EINVAL; | 
|---|
| 7058 | } | 
|---|
| 7059 | } | 
|---|
| 7060 |  | 
|---|
| 7061 | return 0; | 
|---|
| 7062 | } | 
|---|
| 7063 |  | 
|---|
| 7064 | bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, int attr) | 
|---|
| 7065 | { | 
|---|
| 7066 | struct nlattr *rate; | 
|---|
| 7067 | u32 bitrate; | 
|---|
| 7068 | u16 bitrate_compat; | 
|---|
| 7069 | enum nl80211_rate_info rate_flg; | 
|---|
| 7070 |  | 
|---|
| 7071 | rate = nla_nest_start_noflag(skb: msg, attrtype: attr); | 
|---|
| 7072 | if (!rate) | 
|---|
| 7073 | return false; | 
|---|
| 7074 |  | 
|---|
| 7075 | /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ | 
|---|
| 7076 | bitrate = cfg80211_calculate_bitrate(rate: info); | 
|---|
| 7077 | /* report 16-bit bitrate only if we can */ | 
|---|
| 7078 | bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0; | 
|---|
| 7079 | if (bitrate > 0 && | 
|---|
| 7080 | nla_put_u32(skb: msg, attrtype: NL80211_RATE_INFO_BITRATE32, value: bitrate)) | 
|---|
| 7081 | return false; | 
|---|
| 7082 | if (bitrate_compat > 0 && | 
|---|
| 7083 | nla_put_u16(skb: msg, attrtype: NL80211_RATE_INFO_BITRATE, value: bitrate_compat)) | 
|---|
| 7084 | return false; | 
|---|
| 7085 |  | 
|---|
| 7086 | switch (info->bw) { | 
|---|
| 7087 | case RATE_INFO_BW_1: | 
|---|
| 7088 | rate_flg = NL80211_RATE_INFO_1_MHZ_WIDTH; | 
|---|
| 7089 | break; | 
|---|
| 7090 | case RATE_INFO_BW_2: | 
|---|
| 7091 | rate_flg = NL80211_RATE_INFO_2_MHZ_WIDTH; | 
|---|
| 7092 | break; | 
|---|
| 7093 | case RATE_INFO_BW_4: | 
|---|
| 7094 | rate_flg = NL80211_RATE_INFO_4_MHZ_WIDTH; | 
|---|
| 7095 | break; | 
|---|
| 7096 | case RATE_INFO_BW_5: | 
|---|
| 7097 | rate_flg = NL80211_RATE_INFO_5_MHZ_WIDTH; | 
|---|
| 7098 | break; | 
|---|
| 7099 | case RATE_INFO_BW_8: | 
|---|
| 7100 | rate_flg = NL80211_RATE_INFO_8_MHZ_WIDTH; | 
|---|
| 7101 | break; | 
|---|
| 7102 | case RATE_INFO_BW_10: | 
|---|
| 7103 | rate_flg = NL80211_RATE_INFO_10_MHZ_WIDTH; | 
|---|
| 7104 | break; | 
|---|
| 7105 | case RATE_INFO_BW_16: | 
|---|
| 7106 | rate_flg = NL80211_RATE_INFO_16_MHZ_WIDTH; | 
|---|
| 7107 | break; | 
|---|
| 7108 | default: | 
|---|
| 7109 | WARN_ON(1); | 
|---|
| 7110 | fallthrough; | 
|---|
| 7111 | case RATE_INFO_BW_20: | 
|---|
| 7112 | rate_flg = 0; | 
|---|
| 7113 | break; | 
|---|
| 7114 | case RATE_INFO_BW_40: | 
|---|
| 7115 | rate_flg = NL80211_RATE_INFO_40_MHZ_WIDTH; | 
|---|
| 7116 | break; | 
|---|
| 7117 | case RATE_INFO_BW_80: | 
|---|
| 7118 | rate_flg = NL80211_RATE_INFO_80_MHZ_WIDTH; | 
|---|
| 7119 | break; | 
|---|
| 7120 | case RATE_INFO_BW_160: | 
|---|
| 7121 | rate_flg = NL80211_RATE_INFO_160_MHZ_WIDTH; | 
|---|
| 7122 | break; | 
|---|
| 7123 | case RATE_INFO_BW_HE_RU: | 
|---|
| 7124 | rate_flg = 0; | 
|---|
| 7125 | WARN_ON(!(info->flags & RATE_INFO_FLAGS_HE_MCS)); | 
|---|
| 7126 | break; | 
|---|
| 7127 | case RATE_INFO_BW_320: | 
|---|
| 7128 | rate_flg = NL80211_RATE_INFO_320_MHZ_WIDTH; | 
|---|
| 7129 | break; | 
|---|
| 7130 | case RATE_INFO_BW_EHT_RU: | 
|---|
| 7131 | rate_flg = 0; | 
|---|
| 7132 | WARN_ON(!(info->flags & RATE_INFO_FLAGS_EHT_MCS)); | 
|---|
| 7133 | break; | 
|---|
| 7134 | } | 
|---|
| 7135 |  | 
|---|
| 7136 | if (rate_flg && nla_put_flag(skb: msg, attrtype: rate_flg)) | 
|---|
| 7137 | return false; | 
|---|
| 7138 |  | 
|---|
| 7139 | if (info->flags & RATE_INFO_FLAGS_MCS) { | 
|---|
| 7140 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_MCS, value: info->mcs)) | 
|---|
| 7141 | return false; | 
|---|
| 7142 | if (info->flags & RATE_INFO_FLAGS_SHORT_GI && | 
|---|
| 7143 | nla_put_flag(skb: msg, attrtype: NL80211_RATE_INFO_SHORT_GI)) | 
|---|
| 7144 | return false; | 
|---|
| 7145 | } else if (info->flags & RATE_INFO_FLAGS_VHT_MCS) { | 
|---|
| 7146 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_VHT_MCS, value: info->mcs)) | 
|---|
| 7147 | return false; | 
|---|
| 7148 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_VHT_NSS, value: info->nss)) | 
|---|
| 7149 | return false; | 
|---|
| 7150 | if (info->flags & RATE_INFO_FLAGS_SHORT_GI && | 
|---|
| 7151 | nla_put_flag(skb: msg, attrtype: NL80211_RATE_INFO_SHORT_GI)) | 
|---|
| 7152 | return false; | 
|---|
| 7153 | } else if (info->flags & RATE_INFO_FLAGS_HE_MCS) { | 
|---|
| 7154 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_HE_MCS, value: info->mcs)) | 
|---|
| 7155 | return false; | 
|---|
| 7156 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_HE_NSS, value: info->nss)) | 
|---|
| 7157 | return false; | 
|---|
| 7158 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_HE_GI, value: info->he_gi)) | 
|---|
| 7159 | return false; | 
|---|
| 7160 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_HE_DCM, value: info->he_dcm)) | 
|---|
| 7161 | return false; | 
|---|
| 7162 | if (info->bw == RATE_INFO_BW_HE_RU && | 
|---|
| 7163 | nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_HE_RU_ALLOC, | 
|---|
| 7164 | value: info->he_ru_alloc)) | 
|---|
| 7165 | return false; | 
|---|
| 7166 | } else if (info->flags & RATE_INFO_FLAGS_S1G_MCS) { | 
|---|
| 7167 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_S1G_MCS, value: info->mcs)) | 
|---|
| 7168 | return false; | 
|---|
| 7169 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_S1G_NSS, value: info->nss)) | 
|---|
| 7170 | return false; | 
|---|
| 7171 | if (info->flags & RATE_INFO_FLAGS_SHORT_GI && | 
|---|
| 7172 | nla_put_flag(skb: msg, attrtype: NL80211_RATE_INFO_SHORT_GI)) | 
|---|
| 7173 | return false; | 
|---|
| 7174 | } else if (info->flags & RATE_INFO_FLAGS_EHT_MCS) { | 
|---|
| 7175 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_EHT_MCS, value: info->mcs)) | 
|---|
| 7176 | return false; | 
|---|
| 7177 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_EHT_NSS, value: info->nss)) | 
|---|
| 7178 | return false; | 
|---|
| 7179 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_EHT_GI, value: info->eht_gi)) | 
|---|
| 7180 | return false; | 
|---|
| 7181 | if (info->bw == RATE_INFO_BW_EHT_RU && | 
|---|
| 7182 | nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_EHT_RU_ALLOC, | 
|---|
| 7183 | value: info->eht_ru_alloc)) | 
|---|
| 7184 | return false; | 
|---|
| 7185 | } | 
|---|
| 7186 |  | 
|---|
| 7187 | nla_nest_end(skb: msg, start: rate); | 
|---|
| 7188 | return true; | 
|---|
| 7189 | } | 
|---|
| 7190 |  | 
|---|
| 7191 | static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal, | 
|---|
| 7192 | int id) | 
|---|
| 7193 | { | 
|---|
| 7194 | void *attr; | 
|---|
| 7195 | int i = 0; | 
|---|
| 7196 |  | 
|---|
| 7197 | if (!mask) | 
|---|
| 7198 | return true; | 
|---|
| 7199 |  | 
|---|
| 7200 | attr = nla_nest_start_noflag(skb: msg, attrtype: id); | 
|---|
| 7201 | if (!attr) | 
|---|
| 7202 | return false; | 
|---|
| 7203 |  | 
|---|
| 7204 | for (i = 0; i < IEEE80211_MAX_CHAINS; i++) { | 
|---|
| 7205 | if (!(mask & BIT(i))) | 
|---|
| 7206 | continue; | 
|---|
| 7207 |  | 
|---|
| 7208 | if (nla_put_u8(skb: msg, attrtype: i, value: signal[i])) | 
|---|
| 7209 | return false; | 
|---|
| 7210 | } | 
|---|
| 7211 |  | 
|---|
| 7212 | nla_nest_end(skb: msg, start: attr); | 
|---|
| 7213 |  | 
|---|
| 7214 | return true; | 
|---|
| 7215 | } | 
|---|
| 7216 |  | 
|---|
| 7217 | static int nl80211_fill_link_station(struct sk_buff *msg, | 
|---|
| 7218 | struct cfg80211_registered_device *rdev, | 
|---|
| 7219 | struct link_station_info *link_sinfo) | 
|---|
| 7220 | { | 
|---|
| 7221 | struct nlattr *bss_param, *link_sinfoattr; | 
|---|
| 7222 |  | 
|---|
| 7223 | #define PUT_LINK_SINFO(attr, memb, type) do {				\ | 
|---|
| 7224 | BUILD_BUG_ON(sizeof(type) == sizeof(u64));			\ | 
|---|
| 7225 | if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_ ## attr) &&	\ | 
|---|
| 7226 | nla_put_ ## type(msg, NL80211_STA_INFO_ ## attr,		\ | 
|---|
| 7227 | link_sinfo->memb))				\ | 
|---|
| 7228 | goto nla_put_failure;					\ | 
|---|
| 7229 | } while (0) | 
|---|
| 7230 | #define PUT_LINK_SINFO_U64(attr, memb) do {				\ | 
|---|
| 7231 | if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_ ## attr) &&	\ | 
|---|
| 7232 | nla_put_u64_64bit(msg, NL80211_STA_INFO_ ## attr,		\ | 
|---|
| 7233 | link_sinfo->memb, NL80211_STA_INFO_PAD))	\ | 
|---|
| 7234 | goto nla_put_failure;					\ | 
|---|
| 7235 | } while (0) | 
|---|
| 7236 |  | 
|---|
| 7237 | link_sinfoattr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_STA_INFO); | 
|---|
| 7238 | if (!link_sinfoattr) | 
|---|
| 7239 | goto nla_put_failure; | 
|---|
| 7240 |  | 
|---|
| 7241 | PUT_LINK_SINFO(INACTIVE_TIME, inactive_time, u32); | 
|---|
| 7242 |  | 
|---|
| 7243 | if (link_sinfo->filled & (BIT_ULL(NL80211_STA_INFO_RX_BYTES) | | 
|---|
| 7244 | BIT_ULL(NL80211_STA_INFO_RX_BYTES64)) && | 
|---|
| 7245 | nla_put_u32(skb: msg, attrtype: NL80211_STA_INFO_RX_BYTES, | 
|---|
| 7246 | value: (u32)link_sinfo->rx_bytes)) | 
|---|
| 7247 | goto nla_put_failure; | 
|---|
| 7248 |  | 
|---|
| 7249 | if (link_sinfo->filled & (BIT_ULL(NL80211_STA_INFO_TX_BYTES) | | 
|---|
| 7250 | BIT_ULL(NL80211_STA_INFO_TX_BYTES64)) && | 
|---|
| 7251 | nla_put_u32(skb: msg, attrtype: NL80211_STA_INFO_TX_BYTES, | 
|---|
| 7252 | value: (u32)link_sinfo->tx_bytes)) | 
|---|
| 7253 | goto nla_put_failure; | 
|---|
| 7254 |  | 
|---|
| 7255 | PUT_LINK_SINFO_U64(RX_BYTES64, rx_bytes); | 
|---|
| 7256 | PUT_LINK_SINFO_U64(TX_BYTES64, tx_bytes); | 
|---|
| 7257 | PUT_LINK_SINFO_U64(RX_DURATION, rx_duration); | 
|---|
| 7258 | PUT_LINK_SINFO_U64(TX_DURATION, tx_duration); | 
|---|
| 7259 |  | 
|---|
| 7260 | if (wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 7261 | ftidx: NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) | 
|---|
| 7262 | PUT_LINK_SINFO(AIRTIME_WEIGHT, airtime_weight, u16); | 
|---|
| 7263 |  | 
|---|
| 7264 | switch (rdev->wiphy.signal_type) { | 
|---|
| 7265 | case CFG80211_SIGNAL_TYPE_MBM: | 
|---|
| 7266 | PUT_LINK_SINFO(SIGNAL, signal, u8); | 
|---|
| 7267 | PUT_LINK_SINFO(SIGNAL_AVG, signal_avg, u8); | 
|---|
| 7268 | break; | 
|---|
| 7269 | default: | 
|---|
| 7270 | break; | 
|---|
| 7271 | } | 
|---|
| 7272 | if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) { | 
|---|
| 7273 | if (!nl80211_put_signal(msg, mask: link_sinfo->chains, | 
|---|
| 7274 | signal: link_sinfo->chain_signal, | 
|---|
| 7275 | id: NL80211_STA_INFO_CHAIN_SIGNAL)) | 
|---|
| 7276 | goto nla_put_failure; | 
|---|
| 7277 | } | 
|---|
| 7278 | if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) { | 
|---|
| 7279 | if (!nl80211_put_signal(msg, mask: link_sinfo->chains, | 
|---|
| 7280 | signal: link_sinfo->chain_signal_avg, | 
|---|
| 7281 | id: NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) | 
|---|
| 7282 | goto nla_put_failure; | 
|---|
| 7283 | } | 
|---|
| 7284 | if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_TX_BITRATE)) { | 
|---|
| 7285 | if (!nl80211_put_sta_rate(msg, info: &link_sinfo->txrate, | 
|---|
| 7286 | attr: NL80211_STA_INFO_TX_BITRATE)) | 
|---|
| 7287 | goto nla_put_failure; | 
|---|
| 7288 | } | 
|---|
| 7289 | if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_RX_BITRATE)) { | 
|---|
| 7290 | if (!nl80211_put_sta_rate(msg, info: &link_sinfo->rxrate, | 
|---|
| 7291 | attr: NL80211_STA_INFO_RX_BITRATE)) | 
|---|
| 7292 | goto nla_put_failure; | 
|---|
| 7293 | } | 
|---|
| 7294 |  | 
|---|
| 7295 | PUT_LINK_SINFO(RX_PACKETS, rx_packets, u32); | 
|---|
| 7296 | PUT_LINK_SINFO(TX_PACKETS, tx_packets, u32); | 
|---|
| 7297 | PUT_LINK_SINFO(TX_RETRIES, tx_retries, u32); | 
|---|
| 7298 | PUT_LINK_SINFO(TX_FAILED, tx_failed, u32); | 
|---|
| 7299 | PUT_LINK_SINFO(EXPECTED_THROUGHPUT, expected_throughput, u32); | 
|---|
| 7300 | PUT_LINK_SINFO(BEACON_LOSS, beacon_loss_count, u32); | 
|---|
| 7301 |  | 
|---|
| 7302 | if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM)) { | 
|---|
| 7303 | bss_param = nla_nest_start_noflag(skb: msg, | 
|---|
| 7304 | attrtype: NL80211_STA_INFO_BSS_PARAM); | 
|---|
| 7305 | if (!bss_param) | 
|---|
| 7306 | goto nla_put_failure; | 
|---|
| 7307 |  | 
|---|
| 7308 | if (((link_sinfo->bss_param.flags & | 
|---|
| 7309 | BSS_PARAM_FLAGS_CTS_PROT) && | 
|---|
| 7310 | nla_put_flag(skb: msg, attrtype: NL80211_STA_BSS_PARAM_CTS_PROT)) || | 
|---|
| 7311 | ((link_sinfo->bss_param.flags & | 
|---|
| 7312 | BSS_PARAM_FLAGS_SHORT_PREAMBLE) && | 
|---|
| 7313 | nla_put_flag(skb: msg, | 
|---|
| 7314 | attrtype: NL80211_STA_BSS_PARAM_SHORT_PREAMBLE)) || | 
|---|
| 7315 | ((link_sinfo->bss_param.flags & | 
|---|
| 7316 | BSS_PARAM_FLAGS_SHORT_SLOT_TIME) && | 
|---|
| 7317 | nla_put_flag(skb: msg, | 
|---|
| 7318 | attrtype: NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME)) || | 
|---|
| 7319 | nla_put_u8(skb: msg, attrtype: NL80211_STA_BSS_PARAM_DTIM_PERIOD, | 
|---|
| 7320 | value: link_sinfo->bss_param.dtim_period) || | 
|---|
| 7321 | nla_put_u16(skb: msg, attrtype: NL80211_STA_BSS_PARAM_BEACON_INTERVAL, | 
|---|
| 7322 | value: link_sinfo->bss_param.beacon_interval)) | 
|---|
| 7323 | goto nla_put_failure; | 
|---|
| 7324 |  | 
|---|
| 7325 | nla_nest_end(skb: msg, start: bss_param); | 
|---|
| 7326 | } | 
|---|
| 7327 |  | 
|---|
| 7328 | PUT_LINK_SINFO_U64(RX_DROP_MISC, rx_dropped_misc); | 
|---|
| 7329 | PUT_LINK_SINFO_U64(BEACON_RX, rx_beacon); | 
|---|
| 7330 | PUT_LINK_SINFO(BEACON_SIGNAL_AVG, rx_beacon_signal_avg, u8); | 
|---|
| 7331 | PUT_LINK_SINFO(RX_MPDUS, rx_mpdu_count, u32); | 
|---|
| 7332 | PUT_LINK_SINFO(FCS_ERROR_COUNT, fcs_err_count, u32); | 
|---|
| 7333 | if (wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 7334 | ftidx: NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT)) { | 
|---|
| 7335 | PUT_LINK_SINFO(ACK_SIGNAL, ack_signal, u8); | 
|---|
| 7336 | PUT_LINK_SINFO(ACK_SIGNAL_AVG, avg_ack_signal, s8); | 
|---|
| 7337 | } | 
|---|
| 7338 |  | 
|---|
| 7339 | #undef PUT_LINK_SINFO | 
|---|
| 7340 | #undef PUT_LINK_SINFO_U64 | 
|---|
| 7341 |  | 
|---|
| 7342 | if (link_sinfo->pertid) { | 
|---|
| 7343 | struct nlattr *tidsattr; | 
|---|
| 7344 | int tid; | 
|---|
| 7345 |  | 
|---|
| 7346 | tidsattr = nla_nest_start_noflag(skb: msg, | 
|---|
| 7347 | attrtype: NL80211_STA_INFO_TID_STATS); | 
|---|
| 7348 | if (!tidsattr) | 
|---|
| 7349 | goto nla_put_failure; | 
|---|
| 7350 |  | 
|---|
| 7351 | for (tid = 0; tid < IEEE80211_NUM_TIDS + 1; tid++) { | 
|---|
| 7352 | struct cfg80211_tid_stats *tidstats; | 
|---|
| 7353 | struct nlattr *tidattr; | 
|---|
| 7354 |  | 
|---|
| 7355 | tidstats = &link_sinfo->pertid[tid]; | 
|---|
| 7356 |  | 
|---|
| 7357 | if (!tidstats->filled) | 
|---|
| 7358 | continue; | 
|---|
| 7359 |  | 
|---|
| 7360 | tidattr = nla_nest_start_noflag(skb: msg, attrtype: tid + 1); | 
|---|
| 7361 | if (!tidattr) | 
|---|
| 7362 | goto nla_put_failure; | 
|---|
| 7363 |  | 
|---|
| 7364 | #define PUT_TIDVAL_U64(attr, memb) do {					\ | 
|---|
| 7365 | if (tidstats->filled & BIT(NL80211_TID_STATS_ ## attr) &&	\ | 
|---|
| 7366 | nla_put_u64_64bit(msg, NL80211_TID_STATS_ ## attr,		\ | 
|---|
| 7367 | tidstats->memb, NL80211_TID_STATS_PAD))	\ | 
|---|
| 7368 | goto nla_put_failure;					\ | 
|---|
| 7369 | } while (0) | 
|---|
| 7370 |  | 
|---|
| 7371 | PUT_TIDVAL_U64(RX_MSDU, rx_msdu); | 
|---|
| 7372 | PUT_TIDVAL_U64(TX_MSDU, tx_msdu); | 
|---|
| 7373 | PUT_TIDVAL_U64(TX_MSDU_RETRIES, tx_msdu_retries); | 
|---|
| 7374 | PUT_TIDVAL_U64(TX_MSDU_FAILED, tx_msdu_failed); | 
|---|
| 7375 |  | 
|---|
| 7376 | #undef PUT_TIDVAL_U64 | 
|---|
| 7377 | if ((tidstats->filled & | 
|---|
| 7378 | BIT(NL80211_TID_STATS_TXQ_STATS)) && | 
|---|
| 7379 | !nl80211_put_txq_stats(msg, txqstats: &tidstats->txq_stats, | 
|---|
| 7380 | attrtype: NL80211_TID_STATS_TXQ_STATS)) | 
|---|
| 7381 | goto nla_put_failure; | 
|---|
| 7382 |  | 
|---|
| 7383 | nla_nest_end(skb: msg, start: tidattr); | 
|---|
| 7384 | } | 
|---|
| 7385 |  | 
|---|
| 7386 | nla_nest_end(skb: msg, start: tidsattr); | 
|---|
| 7387 | } | 
|---|
| 7388 |  | 
|---|
| 7389 | nla_nest_end(skb: msg, start: link_sinfoattr); | 
|---|
| 7390 | return 0; | 
|---|
| 7391 |  | 
|---|
| 7392 | nla_put_failure: | 
|---|
| 7393 | return -EMSGSIZE; | 
|---|
| 7394 | } | 
|---|
| 7395 |  | 
|---|
| 7396 | static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, | 
|---|
| 7397 | u32 seq, int flags, | 
|---|
| 7398 | struct cfg80211_registered_device *rdev, | 
|---|
| 7399 | struct net_device *dev, | 
|---|
| 7400 | const u8 *mac_addr, struct station_info *sinfo, | 
|---|
| 7401 | bool link_stats) | 
|---|
| 7402 | { | 
|---|
| 7403 | void *hdr; | 
|---|
| 7404 | struct nlattr *sinfoattr, *bss_param; | 
|---|
| 7405 | struct link_station_info *link_sinfo; | 
|---|
| 7406 | struct nlattr *links, *link; | 
|---|
| 7407 | int link_id; | 
|---|
| 7408 |  | 
|---|
| 7409 | hdr = nl80211hdr_put(skb: msg, portid, seq, flags, cmd); | 
|---|
| 7410 | if (!hdr) { | 
|---|
| 7411 | cfg80211_sinfo_release_content(sinfo); | 
|---|
| 7412 | return -1; | 
|---|
| 7413 | } | 
|---|
| 7414 |  | 
|---|
| 7415 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || | 
|---|
| 7416 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: mac_addr) || | 
|---|
| 7417 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_GENERATION, value: sinfo->generation)) | 
|---|
| 7418 | goto nla_put_failure; | 
|---|
| 7419 |  | 
|---|
| 7420 | sinfoattr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_STA_INFO); | 
|---|
| 7421 | if (!sinfoattr) | 
|---|
| 7422 | goto nla_put_failure; | 
|---|
| 7423 |  | 
|---|
| 7424 | #define PUT_SINFO(attr, memb, type) do {				\ | 
|---|
| 7425 | BUILD_BUG_ON(sizeof(type) == sizeof(u64));			\ | 
|---|
| 7426 | if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_ ## attr) &&	\ | 
|---|
| 7427 | nla_put_ ## type(msg, NL80211_STA_INFO_ ## attr,		\ | 
|---|
| 7428 | sinfo->memb))				\ | 
|---|
| 7429 | goto nla_put_failure;					\ | 
|---|
| 7430 | } while (0) | 
|---|
| 7431 | #define PUT_SINFO_U64(attr, memb) do {					\ | 
|---|
| 7432 | if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_ ## attr) &&	\ | 
|---|
| 7433 | nla_put_u64_64bit(msg, NL80211_STA_INFO_ ## attr,		\ | 
|---|
| 7434 | sinfo->memb, NL80211_STA_INFO_PAD))	\ | 
|---|
| 7435 | goto nla_put_failure;					\ | 
|---|
| 7436 | } while (0) | 
|---|
| 7437 |  | 
|---|
| 7438 | PUT_SINFO(CONNECTED_TIME, connected_time, u32); | 
|---|
| 7439 | PUT_SINFO(INACTIVE_TIME, inactive_time, u32); | 
|---|
| 7440 | PUT_SINFO_U64(ASSOC_AT_BOOTTIME, assoc_at); | 
|---|
| 7441 |  | 
|---|
| 7442 | if (sinfo->filled & (BIT_ULL(NL80211_STA_INFO_RX_BYTES) | | 
|---|
| 7443 | BIT_ULL(NL80211_STA_INFO_RX_BYTES64)) && | 
|---|
| 7444 | nla_put_u32(skb: msg, attrtype: NL80211_STA_INFO_RX_BYTES, | 
|---|
| 7445 | value: (u32)sinfo->rx_bytes)) | 
|---|
| 7446 | goto nla_put_failure; | 
|---|
| 7447 |  | 
|---|
| 7448 | if (sinfo->filled & (BIT_ULL(NL80211_STA_INFO_TX_BYTES) | | 
|---|
| 7449 | BIT_ULL(NL80211_STA_INFO_TX_BYTES64)) && | 
|---|
| 7450 | nla_put_u32(skb: msg, attrtype: NL80211_STA_INFO_TX_BYTES, | 
|---|
| 7451 | value: (u32)sinfo->tx_bytes)) | 
|---|
| 7452 | goto nla_put_failure; | 
|---|
| 7453 |  | 
|---|
| 7454 | PUT_SINFO_U64(RX_BYTES64, rx_bytes); | 
|---|
| 7455 | PUT_SINFO_U64(TX_BYTES64, tx_bytes); | 
|---|
| 7456 | PUT_SINFO_U64(RX_DURATION, rx_duration); | 
|---|
| 7457 | PUT_SINFO_U64(TX_DURATION, tx_duration); | 
|---|
| 7458 |  | 
|---|
| 7459 | if (wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 7460 | ftidx: NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) | 
|---|
| 7461 | PUT_SINFO(AIRTIME_WEIGHT, airtime_weight, u16); | 
|---|
| 7462 |  | 
|---|
| 7463 | switch (rdev->wiphy.signal_type) { | 
|---|
| 7464 | case CFG80211_SIGNAL_TYPE_MBM: | 
|---|
| 7465 | PUT_SINFO(SIGNAL, signal, u8); | 
|---|
| 7466 | PUT_SINFO(SIGNAL_AVG, signal_avg, u8); | 
|---|
| 7467 | break; | 
|---|
| 7468 | default: | 
|---|
| 7469 | break; | 
|---|
| 7470 | } | 
|---|
| 7471 | if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) { | 
|---|
| 7472 | if (!nl80211_put_signal(msg, mask: sinfo->chains, | 
|---|
| 7473 | signal: sinfo->chain_signal, | 
|---|
| 7474 | id: NL80211_STA_INFO_CHAIN_SIGNAL)) | 
|---|
| 7475 | goto nla_put_failure; | 
|---|
| 7476 | } | 
|---|
| 7477 | if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) { | 
|---|
| 7478 | if (!nl80211_put_signal(msg, mask: sinfo->chains, | 
|---|
| 7479 | signal: sinfo->chain_signal_avg, | 
|---|
| 7480 | id: NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) | 
|---|
| 7481 | goto nla_put_failure; | 
|---|
| 7482 | } | 
|---|
| 7483 | if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_TX_BITRATE)) { | 
|---|
| 7484 | if (!nl80211_put_sta_rate(msg, info: &sinfo->txrate, | 
|---|
| 7485 | attr: NL80211_STA_INFO_TX_BITRATE)) | 
|---|
| 7486 | goto nla_put_failure; | 
|---|
| 7487 | } | 
|---|
| 7488 | if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_RX_BITRATE)) { | 
|---|
| 7489 | if (!nl80211_put_sta_rate(msg, info: &sinfo->rxrate, | 
|---|
| 7490 | attr: NL80211_STA_INFO_RX_BITRATE)) | 
|---|
| 7491 | goto nla_put_failure; | 
|---|
| 7492 | } | 
|---|
| 7493 |  | 
|---|
| 7494 | PUT_SINFO(RX_PACKETS, rx_packets, u32); | 
|---|
| 7495 | PUT_SINFO(TX_PACKETS, tx_packets, u32); | 
|---|
| 7496 | PUT_SINFO(TX_RETRIES, tx_retries, u32); | 
|---|
| 7497 | PUT_SINFO(TX_FAILED, tx_failed, u32); | 
|---|
| 7498 | PUT_SINFO(EXPECTED_THROUGHPUT, expected_throughput, u32); | 
|---|
| 7499 | PUT_SINFO(BEACON_LOSS, beacon_loss_count, u32); | 
|---|
| 7500 |  | 
|---|
| 7501 | PUT_SINFO(LLID, llid, u16); | 
|---|
| 7502 | PUT_SINFO(PLID, plid, u16); | 
|---|
| 7503 | PUT_SINFO(PLINK_STATE, plink_state, u8); | 
|---|
| 7504 | PUT_SINFO(AIRTIME_LINK_METRIC, airtime_link_metric, u32); | 
|---|
| 7505 | PUT_SINFO(LOCAL_PM, local_pm, u32); | 
|---|
| 7506 | PUT_SINFO(PEER_PM, peer_pm, u32); | 
|---|
| 7507 | PUT_SINFO(NONPEER_PM, nonpeer_pm, u32); | 
|---|
| 7508 | PUT_SINFO(CONNECTED_TO_GATE, connected_to_gate, u8); | 
|---|
| 7509 | PUT_SINFO(CONNECTED_TO_AS, connected_to_as, u8); | 
|---|
| 7510 | PUT_SINFO_U64(T_OFFSET, t_offset); | 
|---|
| 7511 |  | 
|---|
| 7512 | if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM)) { | 
|---|
| 7513 | bss_param = nla_nest_start_noflag(skb: msg, | 
|---|
| 7514 | attrtype: NL80211_STA_INFO_BSS_PARAM); | 
|---|
| 7515 | if (!bss_param) | 
|---|
| 7516 | goto nla_put_failure; | 
|---|
| 7517 |  | 
|---|
| 7518 | if (((sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT) && | 
|---|
| 7519 | nla_put_flag(skb: msg, attrtype: NL80211_STA_BSS_PARAM_CTS_PROT)) || | 
|---|
| 7520 | ((sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE) && | 
|---|
| 7521 | nla_put_flag(skb: msg, attrtype: NL80211_STA_BSS_PARAM_SHORT_PREAMBLE)) || | 
|---|
| 7522 | ((sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME) && | 
|---|
| 7523 | nla_put_flag(skb: msg, attrtype: NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME)) || | 
|---|
| 7524 | nla_put_u8(skb: msg, attrtype: NL80211_STA_BSS_PARAM_DTIM_PERIOD, | 
|---|
| 7525 | value: sinfo->bss_param.dtim_period) || | 
|---|
| 7526 | nla_put_u16(skb: msg, attrtype: NL80211_STA_BSS_PARAM_BEACON_INTERVAL, | 
|---|
| 7527 | value: sinfo->bss_param.beacon_interval)) | 
|---|
| 7528 | goto nla_put_failure; | 
|---|
| 7529 |  | 
|---|
| 7530 | nla_nest_end(skb: msg, start: bss_param); | 
|---|
| 7531 | } | 
|---|
| 7532 | if ((sinfo->filled & BIT_ULL(NL80211_STA_INFO_STA_FLAGS)) && | 
|---|
| 7533 | nla_put(skb: msg, attrtype: NL80211_STA_INFO_STA_FLAGS, | 
|---|
| 7534 | attrlen: sizeof(struct nl80211_sta_flag_update), | 
|---|
| 7535 | data: &sinfo->sta_flags)) | 
|---|
| 7536 | goto nla_put_failure; | 
|---|
| 7537 |  | 
|---|
| 7538 | PUT_SINFO_U64(RX_DROP_MISC, rx_dropped_misc); | 
|---|
| 7539 | PUT_SINFO_U64(BEACON_RX, rx_beacon); | 
|---|
| 7540 | PUT_SINFO(BEACON_SIGNAL_AVG, rx_beacon_signal_avg, u8); | 
|---|
| 7541 | PUT_SINFO(RX_MPDUS, rx_mpdu_count, u32); | 
|---|
| 7542 | PUT_SINFO(FCS_ERROR_COUNT, fcs_err_count, u32); | 
|---|
| 7543 | if (wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 7544 | ftidx: NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT)) { | 
|---|
| 7545 | PUT_SINFO(ACK_SIGNAL, ack_signal, u8); | 
|---|
| 7546 | PUT_SINFO(ACK_SIGNAL_AVG, avg_ack_signal, s8); | 
|---|
| 7547 | } | 
|---|
| 7548 |  | 
|---|
| 7549 | #undef PUT_SINFO | 
|---|
| 7550 | #undef PUT_SINFO_U64 | 
|---|
| 7551 |  | 
|---|
| 7552 | if (sinfo->pertid) { | 
|---|
| 7553 | struct nlattr *tidsattr; | 
|---|
| 7554 | int tid; | 
|---|
| 7555 |  | 
|---|
| 7556 | tidsattr = nla_nest_start_noflag(skb: msg, | 
|---|
| 7557 | attrtype: NL80211_STA_INFO_TID_STATS); | 
|---|
| 7558 | if (!tidsattr) | 
|---|
| 7559 | goto nla_put_failure; | 
|---|
| 7560 |  | 
|---|
| 7561 | for (tid = 0; tid < IEEE80211_NUM_TIDS + 1; tid++) { | 
|---|
| 7562 | struct cfg80211_tid_stats *tidstats; | 
|---|
| 7563 | struct nlattr *tidattr; | 
|---|
| 7564 |  | 
|---|
| 7565 | tidstats = &sinfo->pertid[tid]; | 
|---|
| 7566 |  | 
|---|
| 7567 | if (!tidstats->filled) | 
|---|
| 7568 | continue; | 
|---|
| 7569 |  | 
|---|
| 7570 | tidattr = nla_nest_start_noflag(skb: msg, attrtype: tid + 1); | 
|---|
| 7571 | if (!tidattr) | 
|---|
| 7572 | goto nla_put_failure; | 
|---|
| 7573 |  | 
|---|
| 7574 | #define PUT_TIDVAL_U64(attr, memb) do {					\ | 
|---|
| 7575 | if (tidstats->filled & BIT(NL80211_TID_STATS_ ## attr) &&	\ | 
|---|
| 7576 | nla_put_u64_64bit(msg, NL80211_TID_STATS_ ## attr,		\ | 
|---|
| 7577 | tidstats->memb, NL80211_TID_STATS_PAD))	\ | 
|---|
| 7578 | goto nla_put_failure;					\ | 
|---|
| 7579 | } while (0) | 
|---|
| 7580 |  | 
|---|
| 7581 | PUT_TIDVAL_U64(RX_MSDU, rx_msdu); | 
|---|
| 7582 | PUT_TIDVAL_U64(TX_MSDU, tx_msdu); | 
|---|
| 7583 | PUT_TIDVAL_U64(TX_MSDU_RETRIES, tx_msdu_retries); | 
|---|
| 7584 | PUT_TIDVAL_U64(TX_MSDU_FAILED, tx_msdu_failed); | 
|---|
| 7585 |  | 
|---|
| 7586 | #undef PUT_TIDVAL_U64 | 
|---|
| 7587 | if ((tidstats->filled & | 
|---|
| 7588 | BIT(NL80211_TID_STATS_TXQ_STATS)) && | 
|---|
| 7589 | !nl80211_put_txq_stats(msg, txqstats: &tidstats->txq_stats, | 
|---|
| 7590 | attrtype: NL80211_TID_STATS_TXQ_STATS)) | 
|---|
| 7591 | goto nla_put_failure; | 
|---|
| 7592 |  | 
|---|
| 7593 | nla_nest_end(skb: msg, start: tidattr); | 
|---|
| 7594 | } | 
|---|
| 7595 |  | 
|---|
| 7596 | nla_nest_end(skb: msg, start: tidsattr); | 
|---|
| 7597 | } | 
|---|
| 7598 |  | 
|---|
| 7599 | nla_nest_end(skb: msg, start: sinfoattr); | 
|---|
| 7600 |  | 
|---|
| 7601 | if (sinfo->assoc_req_ies_len && | 
|---|
| 7602 | nla_put(skb: msg, NL80211_ATTR_IE, attrlen: sinfo->assoc_req_ies_len, | 
|---|
| 7603 | data: sinfo->assoc_req_ies)) | 
|---|
| 7604 | goto nla_put_failure; | 
|---|
| 7605 |  | 
|---|
| 7606 | if (sinfo->assoc_resp_ies_len && | 
|---|
| 7607 | nla_put(skb: msg, attrtype: NL80211_ATTR_RESP_IE, attrlen: sinfo->assoc_resp_ies_len, | 
|---|
| 7608 | data: sinfo->assoc_resp_ies)) | 
|---|
| 7609 | goto nla_put_failure; | 
|---|
| 7610 |  | 
|---|
| 7611 | if (sinfo->mlo_params_valid) { | 
|---|
| 7612 | if (nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, | 
|---|
| 7613 | value: sinfo->assoc_link_id)) | 
|---|
| 7614 | goto nla_put_failure; | 
|---|
| 7615 |  | 
|---|
| 7616 | if (!is_zero_ether_addr(addr: sinfo->mld_addr) && | 
|---|
| 7617 | nla_put(skb: msg, attrtype: NL80211_ATTR_MLD_ADDR, ETH_ALEN, | 
|---|
| 7618 | data: sinfo->mld_addr)) | 
|---|
| 7619 | goto nla_put_failure; | 
|---|
| 7620 | } | 
|---|
| 7621 |  | 
|---|
| 7622 | if (link_stats && sinfo->valid_links) { | 
|---|
| 7623 | links = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_MLO_LINKS); | 
|---|
| 7624 | if (!links) | 
|---|
| 7625 | goto nla_put_failure; | 
|---|
| 7626 |  | 
|---|
| 7627 | for_each_valid_link(sinfo, link_id) { | 
|---|
| 7628 | link_sinfo = sinfo->links[link_id]; | 
|---|
| 7629 |  | 
|---|
| 7630 | if (WARN_ON_ONCE(!link_sinfo)) | 
|---|
| 7631 | continue; | 
|---|
| 7632 |  | 
|---|
| 7633 | if (!is_valid_ether_addr(addr: link_sinfo->addr)) | 
|---|
| 7634 | continue; | 
|---|
| 7635 |  | 
|---|
| 7636 | link = nla_nest_start(skb: msg, attrtype: link_id + 1); | 
|---|
| 7637 | if (!link) | 
|---|
| 7638 | goto nla_put_failure; | 
|---|
| 7639 |  | 
|---|
| 7640 | if (nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, | 
|---|
| 7641 | value: link_id)) | 
|---|
| 7642 | goto nla_put_failure; | 
|---|
| 7643 |  | 
|---|
| 7644 | if (nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, | 
|---|
| 7645 | data: link_sinfo->addr)) | 
|---|
| 7646 | goto nla_put_failure; | 
|---|
| 7647 |  | 
|---|
| 7648 | if (nl80211_fill_link_station(msg, rdev, link_sinfo)) | 
|---|
| 7649 | goto nla_put_failure; | 
|---|
| 7650 |  | 
|---|
| 7651 | nla_nest_end(skb: msg, start: link); | 
|---|
| 7652 | } | 
|---|
| 7653 | nla_nest_end(skb: msg, start: links); | 
|---|
| 7654 | } | 
|---|
| 7655 |  | 
|---|
| 7656 | cfg80211_sinfo_release_content(sinfo); | 
|---|
| 7657 | genlmsg_end(skb: msg, hdr); | 
|---|
| 7658 | return 0; | 
|---|
| 7659 |  | 
|---|
| 7660 | nla_put_failure: | 
|---|
| 7661 | cfg80211_sinfo_release_content(sinfo); | 
|---|
| 7662 | genlmsg_cancel(skb: msg, hdr); | 
|---|
| 7663 | return -EMSGSIZE; | 
|---|
| 7664 | } | 
|---|
| 7665 |  | 
|---|
| 7666 | static void cfg80211_sta_set_mld_sinfo(struct station_info *sinfo) | 
|---|
| 7667 | { | 
|---|
| 7668 | struct link_station_info *link_sinfo; | 
|---|
| 7669 | int link_id, init = 0; | 
|---|
| 7670 | u32 link_inactive_time; | 
|---|
| 7671 |  | 
|---|
| 7672 | sinfo->signal = -99; | 
|---|
| 7673 |  | 
|---|
| 7674 | for_each_valid_link(sinfo, link_id) { | 
|---|
| 7675 | link_sinfo = sinfo->links[link_id]; | 
|---|
| 7676 | if (!link_sinfo) | 
|---|
| 7677 | continue; | 
|---|
| 7678 |  | 
|---|
| 7679 | if ((link_sinfo->filled & | 
|---|
| 7680 | BIT_ULL(NL80211_STA_INFO_TX_PACKETS))) { | 
|---|
| 7681 | sinfo->tx_packets += link_sinfo->tx_packets; | 
|---|
| 7682 | sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS); | 
|---|
| 7683 | } | 
|---|
| 7684 |  | 
|---|
| 7685 | if ((link_sinfo->filled & | 
|---|
| 7686 | BIT_ULL(NL80211_STA_INFO_RX_PACKETS))) { | 
|---|
| 7687 | sinfo->rx_packets += link_sinfo->rx_packets; | 
|---|
| 7688 | sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS); | 
|---|
| 7689 | } | 
|---|
| 7690 |  | 
|---|
| 7691 | if (link_sinfo->filled & | 
|---|
| 7692 | (BIT_ULL(NL80211_STA_INFO_TX_BYTES) | | 
|---|
| 7693 | BIT_ULL(NL80211_STA_INFO_TX_BYTES64))) { | 
|---|
| 7694 | sinfo->tx_bytes += link_sinfo->tx_bytes; | 
|---|
| 7695 | sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES); | 
|---|
| 7696 | } | 
|---|
| 7697 |  | 
|---|
| 7698 | if (link_sinfo->filled & | 
|---|
| 7699 | (BIT_ULL(NL80211_STA_INFO_RX_BYTES) | | 
|---|
| 7700 | BIT_ULL(NL80211_STA_INFO_TX_BYTES64))) { | 
|---|
| 7701 | sinfo->rx_bytes += link_sinfo->rx_bytes; | 
|---|
| 7702 | sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES); | 
|---|
| 7703 | } | 
|---|
| 7704 |  | 
|---|
| 7705 | if (link_sinfo->filled & | 
|---|
| 7706 | BIT_ULL(NL80211_STA_INFO_TX_RETRIES)) { | 
|---|
| 7707 | sinfo->tx_retries += link_sinfo->tx_retries; | 
|---|
| 7708 | sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES); | 
|---|
| 7709 | } | 
|---|
| 7710 |  | 
|---|
| 7711 | if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_TX_FAILED)) { | 
|---|
| 7712 | sinfo->tx_failed += link_sinfo->tx_failed; | 
|---|
| 7713 | sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED); | 
|---|
| 7714 | } | 
|---|
| 7715 |  | 
|---|
| 7716 | if (link_sinfo->filled & | 
|---|
| 7717 | BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC)) { | 
|---|
| 7718 | sinfo->rx_dropped_misc += link_sinfo->rx_dropped_misc; | 
|---|
| 7719 | sinfo->filled |= | 
|---|
| 7720 | BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC); | 
|---|
| 7721 | } | 
|---|
| 7722 |  | 
|---|
| 7723 | if (link_sinfo->filled & | 
|---|
| 7724 | BIT_ULL(NL80211_STA_INFO_BEACON_LOSS)) { | 
|---|
| 7725 | sinfo->beacon_loss_count += | 
|---|
| 7726 | link_sinfo->beacon_loss_count; | 
|---|
| 7727 | sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BEACON_LOSS); | 
|---|
| 7728 | } | 
|---|
| 7729 |  | 
|---|
| 7730 | if (link_sinfo->filled & | 
|---|
| 7731 | BIT_ULL(NL80211_STA_INFO_EXPECTED_THROUGHPUT)) { | 
|---|
| 7732 | sinfo->expected_throughput += | 
|---|
| 7733 | link_sinfo->expected_throughput; | 
|---|
| 7734 | sinfo->filled |= | 
|---|
| 7735 | BIT_ULL(NL80211_STA_INFO_EXPECTED_THROUGHPUT); | 
|---|
| 7736 | } | 
|---|
| 7737 |  | 
|---|
| 7738 | if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_RX_MPDUS)) { | 
|---|
| 7739 | sinfo->rx_mpdu_count += link_sinfo->rx_mpdu_count; | 
|---|
| 7740 | sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_MPDUS); | 
|---|
| 7741 | } | 
|---|
| 7742 |  | 
|---|
| 7743 | if (link_sinfo->filled & | 
|---|
| 7744 | BIT_ULL(NL80211_STA_INFO_FCS_ERROR_COUNT)) { | 
|---|
| 7745 | sinfo->fcs_err_count += link_sinfo->fcs_err_count; | 
|---|
| 7746 | sinfo->filled |= | 
|---|
| 7747 | BIT_ULL(NL80211_STA_INFO_FCS_ERROR_COUNT); | 
|---|
| 7748 | } | 
|---|
| 7749 |  | 
|---|
| 7750 | if (link_sinfo->filled & | 
|---|
| 7751 | BIT_ULL(NL80211_STA_INFO_BEACON_RX)) { | 
|---|
| 7752 | sinfo->rx_beacon += link_sinfo->rx_beacon; | 
|---|
| 7753 | sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BEACON_RX); | 
|---|
| 7754 | } | 
|---|
| 7755 |  | 
|---|
| 7756 | /* Update MLO signal, signal_avg as best among links */ | 
|---|
| 7757 | if ((link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_SIGNAL)) && | 
|---|
| 7758 | link_sinfo->signal > sinfo->signal) { | 
|---|
| 7759 | sinfo->signal = link_sinfo->signal; | 
|---|
| 7760 | sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); | 
|---|
| 7761 | } | 
|---|
| 7762 |  | 
|---|
| 7763 | if ((link_sinfo->filled & | 
|---|
| 7764 | BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG)) && | 
|---|
| 7765 | link_sinfo->signal_avg > sinfo->signal_avg) { | 
|---|
| 7766 | sinfo->signal_avg = link_sinfo->signal_avg; | 
|---|
| 7767 | sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); | 
|---|
| 7768 | } | 
|---|
| 7769 |  | 
|---|
| 7770 | /* Update MLO inactive_time, bss_param based on least | 
|---|
| 7771 | * value for corresponding field of link. | 
|---|
| 7772 | */ | 
|---|
| 7773 | if ((link_sinfo->filled & | 
|---|
| 7774 | BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME)) && | 
|---|
| 7775 | (!init || | 
|---|
| 7776 | link_inactive_time > link_sinfo->inactive_time)) { | 
|---|
| 7777 | link_inactive_time = link_sinfo->inactive_time; | 
|---|
| 7778 | sinfo->inactive_time = link_sinfo->inactive_time; | 
|---|
| 7779 | sinfo->filled |= NL80211_STA_INFO_INACTIVE_TIME; | 
|---|
| 7780 | } | 
|---|
| 7781 |  | 
|---|
| 7782 | if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM) && | 
|---|
| 7783 | (!init || | 
|---|
| 7784 | sinfo->bss_param.dtim_period > | 
|---|
| 7785 | link_sinfo->bss_param.dtim_period)) { | 
|---|
| 7786 | sinfo->bss_param.dtim_period = | 
|---|
| 7787 | link_sinfo->bss_param.dtim_period; | 
|---|
| 7788 | sinfo->filled |= NL80211_STA_BSS_PARAM_DTIM_PERIOD; | 
|---|
| 7789 | sinfo->bss_param.beacon_interval = | 
|---|
| 7790 | link_sinfo->bss_param.beacon_interval; | 
|---|
| 7791 | sinfo->filled |= NL80211_STA_BSS_PARAM_BEACON_INTERVAL; | 
|---|
| 7792 | } | 
|---|
| 7793 |  | 
|---|
| 7794 | /* Update MLO rates as per last updated link rate */ | 
|---|
| 7795 | if ((link_sinfo->filled & | 
|---|
| 7796 | BIT_ULL(NL80211_STA_INFO_TX_BITRATE)) && | 
|---|
| 7797 | (!init || | 
|---|
| 7798 | link_inactive_time > link_sinfo->inactive_time)) { | 
|---|
| 7799 | sinfo->txrate = link_sinfo->txrate; | 
|---|
| 7800 | sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); | 
|---|
| 7801 | } | 
|---|
| 7802 | if ((link_sinfo->filled & | 
|---|
| 7803 | BIT_ULL(NL80211_STA_INFO_RX_BITRATE)) && | 
|---|
| 7804 | (!init || | 
|---|
| 7805 | link_inactive_time > link_sinfo->inactive_time)) { | 
|---|
| 7806 | sinfo->rxrate = link_sinfo->rxrate; | 
|---|
| 7807 | sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE); | 
|---|
| 7808 | } | 
|---|
| 7809 |  | 
|---|
| 7810 | if (link_sinfo->filled & | 
|---|
| 7811 | BIT_ULL(NL80211_STA_INFO_TX_DURATION) && | 
|---|
| 7812 | (!init || | 
|---|
| 7813 | link_inactive_time > link_sinfo->inactive_time)) { | 
|---|
| 7814 | sinfo->tx_duration += link_sinfo->tx_duration; | 
|---|
| 7815 | sinfo->filled |= | 
|---|
| 7816 | BIT_ULL(NL80211_STA_INFO_TX_DURATION); | 
|---|
| 7817 | } | 
|---|
| 7818 | if (link_sinfo->filled & | 
|---|
| 7819 | BIT_ULL(NL80211_STA_INFO_RX_DURATION) && | 
|---|
| 7820 | (!init || | 
|---|
| 7821 | link_inactive_time > link_sinfo->inactive_time)) { | 
|---|
| 7822 | sinfo->rx_duration += link_sinfo->rx_duration; | 
|---|
| 7823 | sinfo->filled |= | 
|---|
| 7824 | BIT_ULL(NL80211_STA_INFO_RX_DURATION); | 
|---|
| 7825 | } | 
|---|
| 7826 | init++; | 
|---|
| 7827 |  | 
|---|
| 7828 | /* pertid stats accumulate for rx/tx fields */ | 
|---|
| 7829 | if (sinfo->pertid) { | 
|---|
| 7830 | sinfo->pertid->rx_msdu += | 
|---|
| 7831 | link_sinfo->pertid->rx_msdu; | 
|---|
| 7832 | sinfo->pertid->tx_msdu += | 
|---|
| 7833 | link_sinfo->pertid->tx_msdu; | 
|---|
| 7834 | sinfo->pertid->tx_msdu_retries += | 
|---|
| 7835 | link_sinfo->pertid->tx_msdu_retries; | 
|---|
| 7836 | sinfo->pertid->tx_msdu_failed += | 
|---|
| 7837 | link_sinfo->pertid->tx_msdu_failed; | 
|---|
| 7838 |  | 
|---|
| 7839 | sinfo->pertid->filled |= | 
|---|
| 7840 | BIT(NL80211_TID_STATS_RX_MSDU) | | 
|---|
| 7841 | BIT(NL80211_TID_STATS_TX_MSDU) | | 
|---|
| 7842 | BIT(NL80211_TID_STATS_TX_MSDU_RETRIES) | | 
|---|
| 7843 | BIT(NL80211_TID_STATS_TX_MSDU_FAILED); | 
|---|
| 7844 | } | 
|---|
| 7845 | } | 
|---|
| 7846 |  | 
|---|
| 7847 | /* Reset sinfo->filled bits to exclude fields which don't make | 
|---|
| 7848 | * much sense at the MLO level. | 
|---|
| 7849 | */ | 
|---|
| 7850 | sinfo->filled &= ~BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL); | 
|---|
| 7851 | sinfo->filled &= ~BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG); | 
|---|
| 7852 | } | 
|---|
| 7853 |  | 
|---|
| 7854 | static int nl80211_dump_station(struct sk_buff *skb, | 
|---|
| 7855 | struct netlink_callback *cb) | 
|---|
| 7856 | { | 
|---|
| 7857 | struct station_info sinfo; | 
|---|
| 7858 | struct cfg80211_registered_device *rdev; | 
|---|
| 7859 | struct wireless_dev *wdev; | 
|---|
| 7860 | u8 mac_addr[ETH_ALEN]; | 
|---|
| 7861 | int sta_idx = cb->args[2]; | 
|---|
| 7862 | bool sinfo_alloc = false; | 
|---|
| 7863 | int err, i; | 
|---|
| 7864 |  | 
|---|
| 7865 | err = nl80211_prepare_wdev_dump(cb, rdev: &rdev, wdev: &wdev, NULL); | 
|---|
| 7866 | if (err) | 
|---|
| 7867 | return err; | 
|---|
| 7868 | /* nl80211_prepare_wdev_dump acquired it in the successful case */ | 
|---|
| 7869 | __acquire(&rdev->wiphy.mtx); | 
|---|
| 7870 |  | 
|---|
| 7871 | if (!wdev->netdev) { | 
|---|
| 7872 | err = -EINVAL; | 
|---|
| 7873 | goto out_err; | 
|---|
| 7874 | } | 
|---|
| 7875 |  | 
|---|
| 7876 | if (!rdev->ops->dump_station) { | 
|---|
| 7877 | err = -EOPNOTSUPP; | 
|---|
| 7878 | goto out_err; | 
|---|
| 7879 | } | 
|---|
| 7880 |  | 
|---|
| 7881 | while (1) { | 
|---|
| 7882 | memset(s: &sinfo, c: 0, n: sizeof(sinfo)); | 
|---|
| 7883 |  | 
|---|
| 7884 | for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { | 
|---|
| 7885 | sinfo.links[i] = | 
|---|
| 7886 | kzalloc(sizeof(*sinfo.links[0]), GFP_KERNEL); | 
|---|
| 7887 | if (!sinfo.links[i]) { | 
|---|
| 7888 | err = -ENOMEM; | 
|---|
| 7889 | goto out_err; | 
|---|
| 7890 | } | 
|---|
| 7891 | sinfo_alloc = true; | 
|---|
| 7892 | } | 
|---|
| 7893 |  | 
|---|
| 7894 | err = rdev_dump_station(rdev, dev: wdev->netdev, idx: sta_idx, | 
|---|
| 7895 | mac: mac_addr, sinfo: &sinfo); | 
|---|
| 7896 | if (err == -ENOENT) | 
|---|
| 7897 | break; | 
|---|
| 7898 | if (err) | 
|---|
| 7899 | goto out_err; | 
|---|
| 7900 |  | 
|---|
| 7901 | if (sinfo.valid_links) | 
|---|
| 7902 | cfg80211_sta_set_mld_sinfo(sinfo: &sinfo); | 
|---|
| 7903 |  | 
|---|
| 7904 | /* reset the sinfo_alloc flag as nl80211_send_station() | 
|---|
| 7905 | * always releases sinfo | 
|---|
| 7906 | */ | 
|---|
| 7907 | sinfo_alloc = false; | 
|---|
| 7908 |  | 
|---|
| 7909 | if (nl80211_send_station(msg: skb, cmd: NL80211_CMD_NEW_STATION, | 
|---|
| 7910 | NETLINK_CB(cb->skb).portid, | 
|---|
| 7911 | seq: cb->nlh->nlmsg_seq, NLM_F_MULTI, | 
|---|
| 7912 | rdev, dev: wdev->netdev, mac_addr, | 
|---|
| 7913 | sinfo: &sinfo, link_stats: false) < 0) | 
|---|
| 7914 | goto out; | 
|---|
| 7915 |  | 
|---|
| 7916 | sta_idx++; | 
|---|
| 7917 | } | 
|---|
| 7918 |  | 
|---|
| 7919 | out: | 
|---|
| 7920 | cb->args[2] = sta_idx; | 
|---|
| 7921 | err = skb->len; | 
|---|
| 7922 | out_err: | 
|---|
| 7923 | if (sinfo_alloc) | 
|---|
| 7924 | cfg80211_sinfo_release_content(sinfo: &sinfo); | 
|---|
| 7925 | wiphy_unlock(wiphy: &rdev->wiphy); | 
|---|
| 7926 |  | 
|---|
| 7927 | return err; | 
|---|
| 7928 | } | 
|---|
| 7929 |  | 
|---|
| 7930 | static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 7931 | { | 
|---|
| 7932 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 7933 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 7934 | struct station_info sinfo; | 
|---|
| 7935 | struct sk_buff *msg; | 
|---|
| 7936 | u8 *mac_addr = NULL; | 
|---|
| 7937 | int err, i; | 
|---|
| 7938 |  | 
|---|
| 7939 | memset(s: &sinfo, c: 0, n: sizeof(sinfo)); | 
|---|
| 7940 |  | 
|---|
| 7941 | if (!info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 7942 | return -EINVAL; | 
|---|
| 7943 |  | 
|---|
| 7944 | mac_addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 7945 |  | 
|---|
| 7946 | if (!rdev->ops->get_station) | 
|---|
| 7947 | return -EOPNOTSUPP; | 
|---|
| 7948 |  | 
|---|
| 7949 | for (i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { | 
|---|
| 7950 | sinfo.links[i] = kzalloc(sizeof(*sinfo.links[0]), GFP_KERNEL); | 
|---|
| 7951 | if (!sinfo.links[i]) { | 
|---|
| 7952 | cfg80211_sinfo_release_content(sinfo: &sinfo); | 
|---|
| 7953 | return -ENOMEM; | 
|---|
| 7954 | } | 
|---|
| 7955 | } | 
|---|
| 7956 |  | 
|---|
| 7957 | err = rdev_get_station(rdev, dev, mac: mac_addr, sinfo: &sinfo); | 
|---|
| 7958 | if (err) { | 
|---|
| 7959 | cfg80211_sinfo_release_content(sinfo: &sinfo); | 
|---|
| 7960 | return err; | 
|---|
| 7961 | } | 
|---|
| 7962 |  | 
|---|
| 7963 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 7964 | if (!msg) { | 
|---|
| 7965 | cfg80211_sinfo_release_content(sinfo: &sinfo); | 
|---|
| 7966 | return -ENOMEM; | 
|---|
| 7967 | } | 
|---|
| 7968 |  | 
|---|
| 7969 | if (sinfo.valid_links) | 
|---|
| 7970 | cfg80211_sta_set_mld_sinfo(sinfo: &sinfo); | 
|---|
| 7971 |  | 
|---|
| 7972 | if (nl80211_send_station(msg, cmd: NL80211_CMD_NEW_STATION, | 
|---|
| 7973 | portid: info->snd_portid, seq: info->snd_seq, flags: 0, | 
|---|
| 7974 | rdev, dev, mac_addr, sinfo: &sinfo, link_stats: false) < 0) { | 
|---|
| 7975 | nlmsg_free(skb: msg); | 
|---|
| 7976 | return -ENOBUFS; | 
|---|
| 7977 | } | 
|---|
| 7978 |  | 
|---|
| 7979 | return genlmsg_reply(skb: msg, info); | 
|---|
| 7980 | } | 
|---|
| 7981 |  | 
|---|
| 7982 | int cfg80211_check_station_change(struct wiphy *wiphy, | 
|---|
| 7983 | struct station_parameters *params, | 
|---|
| 7984 | enum cfg80211_station_type statype) | 
|---|
| 7985 | { | 
|---|
| 7986 | if (params->listen_interval != -1 && | 
|---|
| 7987 | statype != CFG80211_STA_AP_CLIENT_UNASSOC) | 
|---|
| 7988 | return -EINVAL; | 
|---|
| 7989 |  | 
|---|
| 7990 | if (params->support_p2p_ps != -1 && | 
|---|
| 7991 | statype != CFG80211_STA_AP_CLIENT_UNASSOC) | 
|---|
| 7992 | return -EINVAL; | 
|---|
| 7993 |  | 
|---|
| 7994 | if (params->aid && | 
|---|
| 7995 | !(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && | 
|---|
| 7996 | statype != CFG80211_STA_AP_CLIENT_UNASSOC) | 
|---|
| 7997 | return -EINVAL; | 
|---|
| 7998 |  | 
|---|
| 7999 | /* When you run into this, adjust the code below for the new flag */ | 
|---|
| 8000 | BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 8); | 
|---|
| 8001 |  | 
|---|
| 8002 | switch (statype) { | 
|---|
| 8003 | case CFG80211_STA_MESH_PEER_KERNEL: | 
|---|
| 8004 | case CFG80211_STA_MESH_PEER_USER: | 
|---|
| 8005 | /* | 
|---|
| 8006 | * No ignoring the TDLS flag here -- the userspace mesh | 
|---|
| 8007 | * code doesn't have the bug of including TDLS in the | 
|---|
| 8008 | * mask everywhere. | 
|---|
| 8009 | */ | 
|---|
| 8010 | if (params->sta_flags_mask & | 
|---|
| 8011 | ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) | | 
|---|
| 8012 | BIT(NL80211_STA_FLAG_MFP) | | 
|---|
| 8013 | BIT(NL80211_STA_FLAG_AUTHORIZED))) | 
|---|
| 8014 | return -EINVAL; | 
|---|
| 8015 | break; | 
|---|
| 8016 | case CFG80211_STA_TDLS_PEER_SETUP: | 
|---|
| 8017 | case CFG80211_STA_TDLS_PEER_ACTIVE: | 
|---|
| 8018 | if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) | 
|---|
| 8019 | return -EINVAL; | 
|---|
| 8020 | /* ignore since it can't change */ | 
|---|
| 8021 | params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); | 
|---|
| 8022 | break; | 
|---|
| 8023 | default: | 
|---|
| 8024 | /* disallow mesh-specific things */ | 
|---|
| 8025 | if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION) | 
|---|
| 8026 | return -EINVAL; | 
|---|
| 8027 | if (params->local_pm) | 
|---|
| 8028 | return -EINVAL; | 
|---|
| 8029 | if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) | 
|---|
| 8030 | return -EINVAL; | 
|---|
| 8031 | } | 
|---|
| 8032 |  | 
|---|
| 8033 | if (statype != CFG80211_STA_TDLS_PEER_SETUP && | 
|---|
| 8034 | statype != CFG80211_STA_TDLS_PEER_ACTIVE) { | 
|---|
| 8035 | /* TDLS can't be set, ... */ | 
|---|
| 8036 | if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) | 
|---|
| 8037 | return -EINVAL; | 
|---|
| 8038 | /* | 
|---|
| 8039 | * ... but don't bother the driver with it. This works around | 
|---|
| 8040 | * a hostapd/wpa_supplicant issue -- it always includes the | 
|---|
| 8041 | * TLDS_PEER flag in the mask even for AP mode. | 
|---|
| 8042 | */ | 
|---|
| 8043 | params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); | 
|---|
| 8044 | } | 
|---|
| 8045 |  | 
|---|
| 8046 | if (statype != CFG80211_STA_TDLS_PEER_SETUP && | 
|---|
| 8047 | statype != CFG80211_STA_AP_CLIENT_UNASSOC) { | 
|---|
| 8048 | /* reject other things that can't change */ | 
|---|
| 8049 | if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) | 
|---|
| 8050 | return -EINVAL; | 
|---|
| 8051 | if (params->sta_modify_mask & STATION_PARAM_APPLY_CAPABILITY) | 
|---|
| 8052 | return -EINVAL; | 
|---|
| 8053 | if (params->link_sta_params.supported_rates) | 
|---|
| 8054 | return -EINVAL; | 
|---|
| 8055 | if (params->ext_capab || params->link_sta_params.ht_capa || | 
|---|
| 8056 | params->link_sta_params.vht_capa || | 
|---|
| 8057 | params->link_sta_params.he_capa || | 
|---|
| 8058 | params->link_sta_params.eht_capa) | 
|---|
| 8059 | return -EINVAL; | 
|---|
| 8060 | if (params->sta_flags_mask & BIT(NL80211_STA_FLAG_SPP_AMSDU)) | 
|---|
| 8061 | return -EINVAL; | 
|---|
| 8062 | } | 
|---|
| 8063 |  | 
|---|
| 8064 | if (statype != CFG80211_STA_AP_CLIENT && | 
|---|
| 8065 | statype != CFG80211_STA_AP_CLIENT_UNASSOC) { | 
|---|
| 8066 | if (params->vlan) | 
|---|
| 8067 | return -EINVAL; | 
|---|
| 8068 | } | 
|---|
| 8069 |  | 
|---|
| 8070 | /* Accept EMLSR capabilities only for AP client before association */ | 
|---|
| 8071 | if (statype != CFG80211_STA_AP_CLIENT_UNASSOC && | 
|---|
| 8072 | params->eml_cap_present) | 
|---|
| 8073 | return -EINVAL; | 
|---|
| 8074 |  | 
|---|
| 8075 | switch (statype) { | 
|---|
| 8076 | case CFG80211_STA_AP_MLME_CLIENT: | 
|---|
| 8077 | /* Use this only for authorizing/unauthorizing a station */ | 
|---|
| 8078 | if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))) | 
|---|
| 8079 | return -EOPNOTSUPP; | 
|---|
| 8080 | break; | 
|---|
| 8081 | case CFG80211_STA_AP_CLIENT: | 
|---|
| 8082 | case CFG80211_STA_AP_CLIENT_UNASSOC: | 
|---|
| 8083 | /* accept only the listed bits */ | 
|---|
| 8084 | if (params->sta_flags_mask & | 
|---|
| 8085 | ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | | 
|---|
| 8086 | BIT(NL80211_STA_FLAG_AUTHENTICATED) | | 
|---|
| 8087 | BIT(NL80211_STA_FLAG_ASSOCIATED) | | 
|---|
| 8088 | BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | | 
|---|
| 8089 | BIT(NL80211_STA_FLAG_WME) | | 
|---|
| 8090 | BIT(NL80211_STA_FLAG_MFP) | | 
|---|
| 8091 | BIT(NL80211_STA_FLAG_SPP_AMSDU))) | 
|---|
| 8092 | return -EINVAL; | 
|---|
| 8093 |  | 
|---|
| 8094 | /* but authenticated/associated only if driver handles it */ | 
|---|
| 8095 | if (!(wiphy->features & NL80211_FEATURE_FULL_AP_CLIENT_STATE) && | 
|---|
| 8096 | params->sta_flags_mask & | 
|---|
| 8097 | (BIT(NL80211_STA_FLAG_AUTHENTICATED) | | 
|---|
| 8098 | BIT(NL80211_STA_FLAG_ASSOCIATED))) | 
|---|
| 8099 | return -EINVAL; | 
|---|
| 8100 | break; | 
|---|
| 8101 | case CFG80211_STA_IBSS: | 
|---|
| 8102 | case CFG80211_STA_AP_STA: | 
|---|
| 8103 | /* reject any changes other than AUTHORIZED */ | 
|---|
| 8104 | if (params->sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)) | 
|---|
| 8105 | return -EINVAL; | 
|---|
| 8106 | break; | 
|---|
| 8107 | case CFG80211_STA_TDLS_PEER_SETUP: | 
|---|
| 8108 | /* reject any changes other than AUTHORIZED or WME */ | 
|---|
| 8109 | if (params->sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | | 
|---|
| 8110 | BIT(NL80211_STA_FLAG_WME))) | 
|---|
| 8111 | return -EINVAL; | 
|---|
| 8112 | /* force (at least) rates when authorizing */ | 
|---|
| 8113 | if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED) && | 
|---|
| 8114 | !params->link_sta_params.supported_rates) | 
|---|
| 8115 | return -EINVAL; | 
|---|
| 8116 | break; | 
|---|
| 8117 | case CFG80211_STA_TDLS_PEER_ACTIVE: | 
|---|
| 8118 | /* reject any changes */ | 
|---|
| 8119 | return -EINVAL; | 
|---|
| 8120 | case CFG80211_STA_MESH_PEER_KERNEL: | 
|---|
| 8121 | if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) | 
|---|
| 8122 | return -EINVAL; | 
|---|
| 8123 | break; | 
|---|
| 8124 | case CFG80211_STA_MESH_PEER_USER: | 
|---|
| 8125 | if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION && | 
|---|
| 8126 | params->plink_action != NL80211_PLINK_ACTION_BLOCK) | 
|---|
| 8127 | return -EINVAL; | 
|---|
| 8128 | break; | 
|---|
| 8129 | } | 
|---|
| 8130 |  | 
|---|
| 8131 | /* | 
|---|
| 8132 | * Older kernel versions ignored this attribute entirely, so don't | 
|---|
| 8133 | * reject attempts to update it but mark it as unused instead so the | 
|---|
| 8134 | * driver won't look at the data. | 
|---|
| 8135 | */ | 
|---|
| 8136 | if (statype != CFG80211_STA_AP_CLIENT_UNASSOC && | 
|---|
| 8137 | statype != CFG80211_STA_TDLS_PEER_SETUP) | 
|---|
| 8138 | params->link_sta_params.opmode_notif_used = false; | 
|---|
| 8139 |  | 
|---|
| 8140 | return 0; | 
|---|
| 8141 | } | 
|---|
| 8142 | EXPORT_SYMBOL(cfg80211_check_station_change); | 
|---|
| 8143 |  | 
|---|
| 8144 | /* | 
|---|
| 8145 | * Get vlan interface making sure it is running and on the right wiphy. | 
|---|
| 8146 | */ | 
|---|
| 8147 | static struct net_device *get_vlan(struct genl_info *info, | 
|---|
| 8148 | struct cfg80211_registered_device *rdev) | 
|---|
| 8149 | { | 
|---|
| 8150 | struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN]; | 
|---|
| 8151 | struct net_device *v; | 
|---|
| 8152 | int ret; | 
|---|
| 8153 |  | 
|---|
| 8154 | if (!vlanattr) | 
|---|
| 8155 | return NULL; | 
|---|
| 8156 |  | 
|---|
| 8157 | v = dev_get_by_index(net: genl_info_net(info), ifindex: nla_get_u32(nla: vlanattr)); | 
|---|
| 8158 | if (!v) | 
|---|
| 8159 | return ERR_PTR(error: -ENODEV); | 
|---|
| 8160 |  | 
|---|
| 8161 | if (!v->ieee80211_ptr || v->ieee80211_ptr->wiphy != &rdev->wiphy) { | 
|---|
| 8162 | ret = -EINVAL; | 
|---|
| 8163 | goto error; | 
|---|
| 8164 | } | 
|---|
| 8165 |  | 
|---|
| 8166 | if (v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && | 
|---|
| 8167 | v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | 
|---|
| 8168 | v->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) { | 
|---|
| 8169 | ret = -EINVAL; | 
|---|
| 8170 | goto error; | 
|---|
| 8171 | } | 
|---|
| 8172 |  | 
|---|
| 8173 | if (!netif_running(dev: v)) { | 
|---|
| 8174 | ret = -ENETDOWN; | 
|---|
| 8175 | goto error; | 
|---|
| 8176 | } | 
|---|
| 8177 |  | 
|---|
| 8178 | return v; | 
|---|
| 8179 | error: | 
|---|
| 8180 | dev_put(dev: v); | 
|---|
| 8181 | return ERR_PTR(error: ret); | 
|---|
| 8182 | } | 
|---|
| 8183 |  | 
|---|
| 8184 | static int nl80211_parse_sta_wme(struct genl_info *info, | 
|---|
| 8185 | struct station_parameters *params) | 
|---|
| 8186 | { | 
|---|
| 8187 | struct nlattr *tb[NL80211_STA_WME_MAX + 1]; | 
|---|
| 8188 | struct nlattr *nla; | 
|---|
| 8189 | int err; | 
|---|
| 8190 |  | 
|---|
| 8191 | /* parse WME attributes if present */ | 
|---|
| 8192 | if (!info->attrs[NL80211_ATTR_STA_WME]) | 
|---|
| 8193 | return 0; | 
|---|
| 8194 |  | 
|---|
| 8195 | nla = info->attrs[NL80211_ATTR_STA_WME]; | 
|---|
| 8196 | err = nla_parse_nested_deprecated(tb, maxtype: NL80211_STA_WME_MAX, nla, | 
|---|
| 8197 | policy: nl80211_sta_wme_policy, | 
|---|
| 8198 | extack: info->extack); | 
|---|
| 8199 | if (err) | 
|---|
| 8200 | return err; | 
|---|
| 8201 |  | 
|---|
| 8202 | if (tb[NL80211_STA_WME_UAPSD_QUEUES]) | 
|---|
| 8203 | params->uapsd_queues = nla_get_u8( | 
|---|
| 8204 | nla: tb[NL80211_STA_WME_UAPSD_QUEUES]); | 
|---|
| 8205 | if (params->uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) | 
|---|
| 8206 | return -EINVAL; | 
|---|
| 8207 |  | 
|---|
| 8208 | if (tb[NL80211_STA_WME_MAX_SP]) | 
|---|
| 8209 | params->max_sp = nla_get_u8(nla: tb[NL80211_STA_WME_MAX_SP]); | 
|---|
| 8210 |  | 
|---|
| 8211 | if (params->max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) | 
|---|
| 8212 | return -EINVAL; | 
|---|
| 8213 |  | 
|---|
| 8214 | params->sta_modify_mask |= STATION_PARAM_APPLY_UAPSD; | 
|---|
| 8215 |  | 
|---|
| 8216 | return 0; | 
|---|
| 8217 | } | 
|---|
| 8218 |  | 
|---|
| 8219 | static int nl80211_parse_sta_channel_info(struct genl_info *info, | 
|---|
| 8220 | struct station_parameters *params) | 
|---|
| 8221 | { | 
|---|
| 8222 | if (info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]) { | 
|---|
| 8223 | params->supported_channels = | 
|---|
| 8224 | nla_data(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]); | 
|---|
| 8225 | params->supported_channels_len = | 
|---|
| 8226 | nla_len(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]); | 
|---|
| 8227 | /* | 
|---|
| 8228 | * Need to include at least one (first channel, number of | 
|---|
| 8229 | * channels) tuple for each subband (checked in policy), | 
|---|
| 8230 | * and must have proper tuples for the rest of the data as well. | 
|---|
| 8231 | */ | 
|---|
| 8232 | if (params->supported_channels_len % 2) | 
|---|
| 8233 | return -EINVAL; | 
|---|
| 8234 | } | 
|---|
| 8235 |  | 
|---|
| 8236 | if (info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]) { | 
|---|
| 8237 | params->supported_oper_classes = | 
|---|
| 8238 | nla_data(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]); | 
|---|
| 8239 | params->supported_oper_classes_len = | 
|---|
| 8240 | nla_len(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]); | 
|---|
| 8241 | } | 
|---|
| 8242 | return 0; | 
|---|
| 8243 | } | 
|---|
| 8244 |  | 
|---|
| 8245 | static int nl80211_set_station_tdls(struct genl_info *info, | 
|---|
| 8246 | struct station_parameters *params) | 
|---|
| 8247 | { | 
|---|
| 8248 | int err; | 
|---|
| 8249 | /* Dummy STA entry gets updated once the peer capabilities are known */ | 
|---|
| 8250 | if (info->attrs[NL80211_ATTR_PEER_AID]) | 
|---|
| 8251 | params->aid = nla_get_u16(nla: info->attrs[NL80211_ATTR_PEER_AID]); | 
|---|
| 8252 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) | 
|---|
| 8253 | params->link_sta_params.ht_capa = | 
|---|
| 8254 | nla_data(nla: info->attrs[NL80211_ATTR_HT_CAPABILITY]); | 
|---|
| 8255 | if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) | 
|---|
| 8256 | params->link_sta_params.vht_capa = | 
|---|
| 8257 | nla_data(nla: info->attrs[NL80211_ATTR_VHT_CAPABILITY]); | 
|---|
| 8258 | if (info->attrs[NL80211_ATTR_HE_CAPABILITY]) { | 
|---|
| 8259 | params->link_sta_params.he_capa = | 
|---|
| 8260 | nla_data(nla: info->attrs[NL80211_ATTR_HE_CAPABILITY]); | 
|---|
| 8261 | params->link_sta_params.he_capa_len = | 
|---|
| 8262 | nla_len(nla: info->attrs[NL80211_ATTR_HE_CAPABILITY]); | 
|---|
| 8263 |  | 
|---|
| 8264 | if (info->attrs[NL80211_ATTR_EHT_CAPABILITY]) { | 
|---|
| 8265 | params->link_sta_params.eht_capa = | 
|---|
| 8266 | nla_data(nla: info->attrs[NL80211_ATTR_EHT_CAPABILITY]); | 
|---|
| 8267 | params->link_sta_params.eht_capa_len = | 
|---|
| 8268 | nla_len(nla: info->attrs[NL80211_ATTR_EHT_CAPABILITY]); | 
|---|
| 8269 |  | 
|---|
| 8270 | if (!ieee80211_eht_capa_size_ok(he_capa: (const u8 *)params->link_sta_params.he_capa, | 
|---|
| 8271 | data: (const u8 *)params->link_sta_params.eht_capa, | 
|---|
| 8272 | len: params->link_sta_params.eht_capa_len, | 
|---|
| 8273 | from_ap: false)) | 
|---|
| 8274 | return -EINVAL; | 
|---|
| 8275 | } | 
|---|
| 8276 | } | 
|---|
| 8277 |  | 
|---|
| 8278 | if (info->attrs[NL80211_ATTR_S1G_CAPABILITY]) | 
|---|
| 8279 | params->link_sta_params.s1g_capa = | 
|---|
| 8280 | nla_data(nla: info->attrs[NL80211_ATTR_S1G_CAPABILITY]); | 
|---|
| 8281 |  | 
|---|
| 8282 | err = nl80211_parse_sta_channel_info(info, params); | 
|---|
| 8283 | if (err) | 
|---|
| 8284 | return err; | 
|---|
| 8285 |  | 
|---|
| 8286 | return nl80211_parse_sta_wme(info, params); | 
|---|
| 8287 | } | 
|---|
| 8288 |  | 
|---|
| 8289 | static int nl80211_parse_sta_txpower_setting(struct genl_info *info, | 
|---|
| 8290 | struct sta_txpwr *txpwr, | 
|---|
| 8291 | bool *txpwr_set) | 
|---|
| 8292 | { | 
|---|
| 8293 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 8294 | int idx; | 
|---|
| 8295 |  | 
|---|
| 8296 | if (info->attrs[NL80211_ATTR_STA_TX_POWER_SETTING]) { | 
|---|
| 8297 | if (!rdev->ops->set_tx_power || | 
|---|
| 8298 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 8299 | ftidx: NL80211_EXT_FEATURE_STA_TX_PWR)) | 
|---|
| 8300 | return -EOPNOTSUPP; | 
|---|
| 8301 |  | 
|---|
| 8302 | idx = NL80211_ATTR_STA_TX_POWER_SETTING; | 
|---|
| 8303 | txpwr->type = nla_get_u8(nla: info->attrs[idx]); | 
|---|
| 8304 |  | 
|---|
| 8305 | if (txpwr->type == NL80211_TX_POWER_LIMITED) { | 
|---|
| 8306 | idx = NL80211_ATTR_STA_TX_POWER; | 
|---|
| 8307 |  | 
|---|
| 8308 | if (info->attrs[idx]) | 
|---|
| 8309 | txpwr->power = nla_get_s16(nla: info->attrs[idx]); | 
|---|
| 8310 | else | 
|---|
| 8311 | return -EINVAL; | 
|---|
| 8312 | } | 
|---|
| 8313 |  | 
|---|
| 8314 | *txpwr_set = true; | 
|---|
| 8315 | } else { | 
|---|
| 8316 | *txpwr_set = false; | 
|---|
| 8317 | } | 
|---|
| 8318 |  | 
|---|
| 8319 | return 0; | 
|---|
| 8320 | } | 
|---|
| 8321 |  | 
|---|
| 8322 | static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 8323 | { | 
|---|
| 8324 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 8325 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 8326 | struct station_parameters params; | 
|---|
| 8327 | u8 *mac_addr; | 
|---|
| 8328 | int err; | 
|---|
| 8329 |  | 
|---|
| 8330 | memset(s: ¶ms, c: 0, n: sizeof(params)); | 
|---|
| 8331 |  | 
|---|
| 8332 | if (!rdev->ops->change_station) | 
|---|
| 8333 | return -EOPNOTSUPP; | 
|---|
| 8334 |  | 
|---|
| 8335 | /* | 
|---|
| 8336 | * AID and listen_interval properties can be set only for unassociated | 
|---|
| 8337 | * station. Include these parameters here and will check them in | 
|---|
| 8338 | * cfg80211_check_station_change(). | 
|---|
| 8339 | */ | 
|---|
| 8340 | if (info->attrs[NL80211_ATTR_STA_AID]) | 
|---|
| 8341 | params.aid = nla_get_u16(nla: info->attrs[NL80211_ATTR_STA_AID]); | 
|---|
| 8342 |  | 
|---|
| 8343 | if (info->attrs[NL80211_ATTR_VLAN_ID]) | 
|---|
| 8344 | params.vlan_id = nla_get_u16(nla: info->attrs[NL80211_ATTR_VLAN_ID]); | 
|---|
| 8345 |  | 
|---|
| 8346 | if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) | 
|---|
| 8347 | params.listen_interval = | 
|---|
| 8348 | nla_get_u16(nla: info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); | 
|---|
| 8349 | else | 
|---|
| 8350 | params.listen_interval = -1; | 
|---|
| 8351 |  | 
|---|
| 8352 | if (info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]) | 
|---|
| 8353 | params.support_p2p_ps = | 
|---|
| 8354 | nla_get_u8(nla: info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]); | 
|---|
| 8355 | else | 
|---|
| 8356 | params.support_p2p_ps = -1; | 
|---|
| 8357 |  | 
|---|
| 8358 | if (!info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 8359 | return -EINVAL; | 
|---|
| 8360 |  | 
|---|
| 8361 | params.link_sta_params.link_id = | 
|---|
| 8362 | nl80211_link_id_or_invalid(attrs: info->attrs); | 
|---|
| 8363 |  | 
|---|
| 8364 | if (info->attrs[NL80211_ATTR_MLD_ADDR]) { | 
|---|
| 8365 | /* If MLD_ADDR attribute is set then this is an MLD station | 
|---|
| 8366 | * and the MLD_ADDR attribute holds the MLD address and the | 
|---|
| 8367 | * MAC attribute holds for the LINK address. | 
|---|
| 8368 | * In that case, the link_id is also expected to be valid. | 
|---|
| 8369 | */ | 
|---|
| 8370 | if (params.link_sta_params.link_id < 0) | 
|---|
| 8371 | return -EINVAL; | 
|---|
| 8372 |  | 
|---|
| 8373 | mac_addr = nla_data(nla: info->attrs[NL80211_ATTR_MLD_ADDR]); | 
|---|
| 8374 | params.link_sta_params.mld_mac = mac_addr; | 
|---|
| 8375 | params.link_sta_params.link_mac = | 
|---|
| 8376 | nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 8377 | if (!is_valid_ether_addr(addr: params.link_sta_params.link_mac)) | 
|---|
| 8378 | return -EINVAL; | 
|---|
| 8379 | } else { | 
|---|
| 8380 | mac_addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 8381 | } | 
|---|
| 8382 |  | 
|---|
| 8383 |  | 
|---|
| 8384 | if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) { | 
|---|
| 8385 | params.link_sta_params.supported_rates = | 
|---|
| 8386 | nla_data(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | 
|---|
| 8387 | params.link_sta_params.supported_rates_len = | 
|---|
| 8388 | nla_len(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | 
|---|
| 8389 | } | 
|---|
| 8390 |  | 
|---|
| 8391 | if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) { | 
|---|
| 8392 | params.capability = | 
|---|
| 8393 | nla_get_u16(nla: info->attrs[NL80211_ATTR_STA_CAPABILITY]); | 
|---|
| 8394 | params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY; | 
|---|
| 8395 | } | 
|---|
| 8396 |  | 
|---|
| 8397 | if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) { | 
|---|
| 8398 | params.ext_capab = | 
|---|
| 8399 | nla_data(nla: info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); | 
|---|
| 8400 | params.ext_capab_len = | 
|---|
| 8401 | nla_len(nla: info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); | 
|---|
| 8402 | } | 
|---|
| 8403 |  | 
|---|
| 8404 | if (parse_station_flags(info, iftype: dev->ieee80211_ptr->iftype, params: ¶ms)) | 
|---|
| 8405 | return -EINVAL; | 
|---|
| 8406 |  | 
|---|
| 8407 | if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) | 
|---|
| 8408 | params.plink_action = | 
|---|
| 8409 | nla_get_u8(nla: info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); | 
|---|
| 8410 |  | 
|---|
| 8411 | if (info->attrs[NL80211_ATTR_STA_PLINK_STATE]) { | 
|---|
| 8412 | params.plink_state = | 
|---|
| 8413 | nla_get_u8(nla: info->attrs[NL80211_ATTR_STA_PLINK_STATE]); | 
|---|
| 8414 | if (info->attrs[NL80211_ATTR_MESH_PEER_AID]) | 
|---|
| 8415 | params.peer_aid = nla_get_u16( | 
|---|
| 8416 | nla: info->attrs[NL80211_ATTR_MESH_PEER_AID]); | 
|---|
| 8417 | params.sta_modify_mask |= STATION_PARAM_APPLY_PLINK_STATE; | 
|---|
| 8418 | } | 
|---|
| 8419 |  | 
|---|
| 8420 | if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]) | 
|---|
| 8421 | params.local_pm = nla_get_u32( | 
|---|
| 8422 | nla: info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]); | 
|---|
| 8423 |  | 
|---|
| 8424 | if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) { | 
|---|
| 8425 | params.link_sta_params.opmode_notif_used = true; | 
|---|
| 8426 | params.link_sta_params.opmode_notif = | 
|---|
| 8427 | nla_get_u8(nla: info->attrs[NL80211_ATTR_OPMODE_NOTIF]); | 
|---|
| 8428 | } | 
|---|
| 8429 |  | 
|---|
| 8430 | if (info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]) | 
|---|
| 8431 | params.link_sta_params.he_6ghz_capa = | 
|---|
| 8432 | nla_data(nla: info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]); | 
|---|
| 8433 |  | 
|---|
| 8434 | if (info->attrs[NL80211_ATTR_EML_CAPABILITY]) { | 
|---|
| 8435 | params.eml_cap_present = true; | 
|---|
| 8436 | params.eml_cap = | 
|---|
| 8437 | nla_get_u16(nla: info->attrs[NL80211_ATTR_EML_CAPABILITY]); | 
|---|
| 8438 | } | 
|---|
| 8439 |  | 
|---|
| 8440 | if (info->attrs[NL80211_ATTR_AIRTIME_WEIGHT]) | 
|---|
| 8441 | params.airtime_weight = | 
|---|
| 8442 | nla_get_u16(nla: info->attrs[NL80211_ATTR_AIRTIME_WEIGHT]); | 
|---|
| 8443 |  | 
|---|
| 8444 | if (params.airtime_weight && | 
|---|
| 8445 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 8446 | ftidx: NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) | 
|---|
| 8447 | return -EOPNOTSUPP; | 
|---|
| 8448 |  | 
|---|
| 8449 | err = nl80211_parse_sta_txpower_setting(info, | 
|---|
| 8450 | txpwr: ¶ms.link_sta_params.txpwr, | 
|---|
| 8451 | txpwr_set: ¶ms.link_sta_params.txpwr_set); | 
|---|
| 8452 | if (err) | 
|---|
| 8453 | return err; | 
|---|
| 8454 |  | 
|---|
| 8455 | /* Include parameters for TDLS peer (will check later) */ | 
|---|
| 8456 | err = nl80211_set_station_tdls(info, params: ¶ms); | 
|---|
| 8457 | if (err) | 
|---|
| 8458 | return err; | 
|---|
| 8459 |  | 
|---|
| 8460 | params.vlan = get_vlan(info, rdev); | 
|---|
| 8461 | if (IS_ERR(ptr: params.vlan)) | 
|---|
| 8462 | return PTR_ERR(ptr: params.vlan); | 
|---|
| 8463 |  | 
|---|
| 8464 | switch (dev->ieee80211_ptr->iftype) { | 
|---|
| 8465 | case NL80211_IFTYPE_AP: | 
|---|
| 8466 | case NL80211_IFTYPE_AP_VLAN: | 
|---|
| 8467 | case NL80211_IFTYPE_P2P_GO: | 
|---|
| 8468 | case NL80211_IFTYPE_P2P_CLIENT: | 
|---|
| 8469 | case NL80211_IFTYPE_STATION: | 
|---|
| 8470 | case NL80211_IFTYPE_ADHOC: | 
|---|
| 8471 | case NL80211_IFTYPE_MESH_POINT: | 
|---|
| 8472 | break; | 
|---|
| 8473 | default: | 
|---|
| 8474 | err = -EOPNOTSUPP; | 
|---|
| 8475 | goto out_put_vlan; | 
|---|
| 8476 | } | 
|---|
| 8477 |  | 
|---|
| 8478 | /* driver will call cfg80211_check_station_change() */ | 
|---|
| 8479 | err = rdev_change_station(rdev, dev, mac: mac_addr, params: ¶ms); | 
|---|
| 8480 |  | 
|---|
| 8481 | out_put_vlan: | 
|---|
| 8482 | dev_put(dev: params.vlan); | 
|---|
| 8483 |  | 
|---|
| 8484 | return err; | 
|---|
| 8485 | } | 
|---|
| 8486 |  | 
|---|
| 8487 | static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 8488 | { | 
|---|
| 8489 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 8490 | int err; | 
|---|
| 8491 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 8492 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 8493 | struct station_parameters params; | 
|---|
| 8494 | u8 *mac_addr = NULL; | 
|---|
| 8495 | u32 auth_assoc = BIT(NL80211_STA_FLAG_AUTHENTICATED) | | 
|---|
| 8496 | BIT(NL80211_STA_FLAG_ASSOCIATED); | 
|---|
| 8497 |  | 
|---|
| 8498 | memset(s: ¶ms, c: 0, n: sizeof(params)); | 
|---|
| 8499 |  | 
|---|
| 8500 | if (!rdev->ops->add_station) | 
|---|
| 8501 | return -EOPNOTSUPP; | 
|---|
| 8502 |  | 
|---|
| 8503 | if (!info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 8504 | return -EINVAL; | 
|---|
| 8505 |  | 
|---|
| 8506 | if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) | 
|---|
| 8507 | return -EINVAL; | 
|---|
| 8508 |  | 
|---|
| 8509 | if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) | 
|---|
| 8510 | return -EINVAL; | 
|---|
| 8511 |  | 
|---|
| 8512 | if (!info->attrs[NL80211_ATTR_STA_AID] && | 
|---|
| 8513 | !info->attrs[NL80211_ATTR_PEER_AID]) | 
|---|
| 8514 | return -EINVAL; | 
|---|
| 8515 |  | 
|---|
| 8516 | params.link_sta_params.link_id = | 
|---|
| 8517 | nl80211_link_id_or_invalid(attrs: info->attrs); | 
|---|
| 8518 |  | 
|---|
| 8519 | if (info->attrs[NL80211_ATTR_MLD_ADDR]) { | 
|---|
| 8520 | mac_addr = nla_data(nla: info->attrs[NL80211_ATTR_MLD_ADDR]); | 
|---|
| 8521 | params.link_sta_params.mld_mac = mac_addr; | 
|---|
| 8522 | params.link_sta_params.link_mac = | 
|---|
| 8523 | nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 8524 | if (!is_valid_ether_addr(addr: params.link_sta_params.link_mac)) | 
|---|
| 8525 | return -EINVAL; | 
|---|
| 8526 | } else { | 
|---|
| 8527 | mac_addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 8528 | } | 
|---|
| 8529 |  | 
|---|
| 8530 | params.link_sta_params.supported_rates = | 
|---|
| 8531 | nla_data(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | 
|---|
| 8532 | params.link_sta_params.supported_rates_len = | 
|---|
| 8533 | nla_len(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | 
|---|
| 8534 | params.listen_interval = | 
|---|
| 8535 | nla_get_u16(nla: info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); | 
|---|
| 8536 |  | 
|---|
| 8537 | if (info->attrs[NL80211_ATTR_VLAN_ID]) | 
|---|
| 8538 | params.vlan_id = nla_get_u16(nla: info->attrs[NL80211_ATTR_VLAN_ID]); | 
|---|
| 8539 |  | 
|---|
| 8540 | if (info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]) { | 
|---|
| 8541 | params.support_p2p_ps = | 
|---|
| 8542 | nla_get_u8(nla: info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]); | 
|---|
| 8543 | } else { | 
|---|
| 8544 | /* | 
|---|
| 8545 | * if not specified, assume it's supported for P2P GO interface, | 
|---|
| 8546 | * and is NOT supported for AP interface | 
|---|
| 8547 | */ | 
|---|
| 8548 | params.support_p2p_ps = | 
|---|
| 8549 | dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO; | 
|---|
| 8550 | } | 
|---|
| 8551 |  | 
|---|
| 8552 | if (info->attrs[NL80211_ATTR_PEER_AID]) | 
|---|
| 8553 | params.aid = nla_get_u16(nla: info->attrs[NL80211_ATTR_PEER_AID]); | 
|---|
| 8554 | else | 
|---|
| 8555 | params.aid = nla_get_u16(nla: info->attrs[NL80211_ATTR_STA_AID]); | 
|---|
| 8556 |  | 
|---|
| 8557 | if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) { | 
|---|
| 8558 | params.capability = | 
|---|
| 8559 | nla_get_u16(nla: info->attrs[NL80211_ATTR_STA_CAPABILITY]); | 
|---|
| 8560 | params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY; | 
|---|
| 8561 | } | 
|---|
| 8562 |  | 
|---|
| 8563 | if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) { | 
|---|
| 8564 | params.ext_capab = | 
|---|
| 8565 | nla_data(nla: info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); | 
|---|
| 8566 | params.ext_capab_len = | 
|---|
| 8567 | nla_len(nla: info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); | 
|---|
| 8568 | } | 
|---|
| 8569 |  | 
|---|
| 8570 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) | 
|---|
| 8571 | params.link_sta_params.ht_capa = | 
|---|
| 8572 | nla_data(nla: info->attrs[NL80211_ATTR_HT_CAPABILITY]); | 
|---|
| 8573 |  | 
|---|
| 8574 | if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) | 
|---|
| 8575 | params.link_sta_params.vht_capa = | 
|---|
| 8576 | nla_data(nla: info->attrs[NL80211_ATTR_VHT_CAPABILITY]); | 
|---|
| 8577 |  | 
|---|
| 8578 | if (info->attrs[NL80211_ATTR_HE_CAPABILITY]) { | 
|---|
| 8579 | params.link_sta_params.he_capa = | 
|---|
| 8580 | nla_data(nla: info->attrs[NL80211_ATTR_HE_CAPABILITY]); | 
|---|
| 8581 | params.link_sta_params.he_capa_len = | 
|---|
| 8582 | nla_len(nla: info->attrs[NL80211_ATTR_HE_CAPABILITY]); | 
|---|
| 8583 |  | 
|---|
| 8584 | if (info->attrs[NL80211_ATTR_EHT_CAPABILITY]) { | 
|---|
| 8585 | params.link_sta_params.eht_capa = | 
|---|
| 8586 | nla_data(nla: info->attrs[NL80211_ATTR_EHT_CAPABILITY]); | 
|---|
| 8587 | params.link_sta_params.eht_capa_len = | 
|---|
| 8588 | nla_len(nla: info->attrs[NL80211_ATTR_EHT_CAPABILITY]); | 
|---|
| 8589 |  | 
|---|
| 8590 | if (!ieee80211_eht_capa_size_ok(he_capa: (const u8 *)params.link_sta_params.he_capa, | 
|---|
| 8591 | data: (const u8 *)params.link_sta_params.eht_capa, | 
|---|
| 8592 | len: params.link_sta_params.eht_capa_len, | 
|---|
| 8593 | from_ap: false)) | 
|---|
| 8594 | return -EINVAL; | 
|---|
| 8595 | } | 
|---|
| 8596 | } | 
|---|
| 8597 |  | 
|---|
| 8598 | if (info->attrs[NL80211_ATTR_EML_CAPABILITY]) { | 
|---|
| 8599 | params.eml_cap_present = true; | 
|---|
| 8600 | params.eml_cap = | 
|---|
| 8601 | nla_get_u16(nla: info->attrs[NL80211_ATTR_EML_CAPABILITY]); | 
|---|
| 8602 | } | 
|---|
| 8603 |  | 
|---|
| 8604 | if (info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]) | 
|---|
| 8605 | params.link_sta_params.he_6ghz_capa = | 
|---|
| 8606 | nla_data(nla: info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]); | 
|---|
| 8607 |  | 
|---|
| 8608 | if (info->attrs[NL80211_ATTR_S1G_CAPABILITY]) | 
|---|
| 8609 | params.link_sta_params.s1g_capa = | 
|---|
| 8610 | nla_data(nla: info->attrs[NL80211_ATTR_S1G_CAPABILITY]); | 
|---|
| 8611 |  | 
|---|
| 8612 | if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) { | 
|---|
| 8613 | params.link_sta_params.opmode_notif_used = true; | 
|---|
| 8614 | params.link_sta_params.opmode_notif = | 
|---|
| 8615 | nla_get_u8(nla: info->attrs[NL80211_ATTR_OPMODE_NOTIF]); | 
|---|
| 8616 | } | 
|---|
| 8617 |  | 
|---|
| 8618 | if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) | 
|---|
| 8619 | params.plink_action = | 
|---|
| 8620 | nla_get_u8(nla: info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); | 
|---|
| 8621 |  | 
|---|
| 8622 | if (info->attrs[NL80211_ATTR_AIRTIME_WEIGHT]) | 
|---|
| 8623 | params.airtime_weight = | 
|---|
| 8624 | nla_get_u16(nla: info->attrs[NL80211_ATTR_AIRTIME_WEIGHT]); | 
|---|
| 8625 |  | 
|---|
| 8626 | if (params.airtime_weight && | 
|---|
| 8627 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 8628 | ftidx: NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) | 
|---|
| 8629 | return -EOPNOTSUPP; | 
|---|
| 8630 |  | 
|---|
| 8631 | err = nl80211_parse_sta_txpower_setting(info, | 
|---|
| 8632 | txpwr: ¶ms.link_sta_params.txpwr, | 
|---|
| 8633 | txpwr_set: ¶ms.link_sta_params.txpwr_set); | 
|---|
| 8634 | if (err) | 
|---|
| 8635 | return err; | 
|---|
| 8636 |  | 
|---|
| 8637 | err = nl80211_parse_sta_channel_info(info, params: ¶ms); | 
|---|
| 8638 | if (err) | 
|---|
| 8639 | return err; | 
|---|
| 8640 |  | 
|---|
| 8641 | err = nl80211_parse_sta_wme(info, params: ¶ms); | 
|---|
| 8642 | if (err) | 
|---|
| 8643 | return err; | 
|---|
| 8644 |  | 
|---|
| 8645 | if (parse_station_flags(info, iftype: dev->ieee80211_ptr->iftype, params: ¶ms)) | 
|---|
| 8646 | return -EINVAL; | 
|---|
| 8647 |  | 
|---|
| 8648 | /* HT/VHT requires QoS, but if we don't have that just ignore HT/VHT | 
|---|
| 8649 | * as userspace might just pass through the capabilities from the IEs | 
|---|
| 8650 | * directly, rather than enforcing this restriction and returning an | 
|---|
| 8651 | * error in this case. | 
|---|
| 8652 | */ | 
|---|
| 8653 | if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME))) { | 
|---|
| 8654 | params.link_sta_params.ht_capa = NULL; | 
|---|
| 8655 | params.link_sta_params.vht_capa = NULL; | 
|---|
| 8656 |  | 
|---|
| 8657 | /* HE and EHT require WME */ | 
|---|
| 8658 | if (params.link_sta_params.he_capa_len || | 
|---|
| 8659 | params.link_sta_params.he_6ghz_capa || | 
|---|
| 8660 | params.link_sta_params.eht_capa_len) | 
|---|
| 8661 | return -EINVAL; | 
|---|
| 8662 | } | 
|---|
| 8663 |  | 
|---|
| 8664 | /* Ensure that HT/VHT capabilities are not set for 6 GHz HE STA */ | 
|---|
| 8665 | if (params.link_sta_params.he_6ghz_capa && | 
|---|
| 8666 | (params.link_sta_params.ht_capa || params.link_sta_params.vht_capa)) | 
|---|
| 8667 | return -EINVAL; | 
|---|
| 8668 |  | 
|---|
| 8669 | /* When you run into this, adjust the code below for the new flag */ | 
|---|
| 8670 | BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 8); | 
|---|
| 8671 |  | 
|---|
| 8672 | switch (dev->ieee80211_ptr->iftype) { | 
|---|
| 8673 | case NL80211_IFTYPE_AP: | 
|---|
| 8674 | case NL80211_IFTYPE_AP_VLAN: | 
|---|
| 8675 | case NL80211_IFTYPE_P2P_GO: | 
|---|
| 8676 | /* ignore WME attributes if iface/sta is not capable */ | 
|---|
| 8677 | if (!(rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) || | 
|---|
| 8678 | !(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME))) | 
|---|
| 8679 | params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD; | 
|---|
| 8680 |  | 
|---|
| 8681 | /* TDLS peers cannot be added */ | 
|---|
| 8682 | if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) || | 
|---|
| 8683 | info->attrs[NL80211_ATTR_PEER_AID]) | 
|---|
| 8684 | return -EINVAL; | 
|---|
| 8685 | /* but don't bother the driver with it */ | 
|---|
| 8686 | params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); | 
|---|
| 8687 |  | 
|---|
| 8688 | /* allow authenticated/associated only if driver handles it */ | 
|---|
| 8689 | if (!(rdev->wiphy.features & | 
|---|
| 8690 | NL80211_FEATURE_FULL_AP_CLIENT_STATE) && | 
|---|
| 8691 | params.sta_flags_mask & auth_assoc) | 
|---|
| 8692 | return -EINVAL; | 
|---|
| 8693 |  | 
|---|
| 8694 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 8695 | ftidx: NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT) && | 
|---|
| 8696 | params.sta_flags_mask & BIT(NL80211_STA_FLAG_SPP_AMSDU)) | 
|---|
| 8697 | return -EINVAL; | 
|---|
| 8698 |  | 
|---|
| 8699 | /* Older userspace, or userspace wanting to be compatible with | 
|---|
| 8700 | * !NL80211_FEATURE_FULL_AP_CLIENT_STATE, will not set the auth | 
|---|
| 8701 | * and assoc flags in the mask, but assumes the station will be | 
|---|
| 8702 | * added as associated anyway since this was the required driver | 
|---|
| 8703 | * behaviour before NL80211_FEATURE_FULL_AP_CLIENT_STATE was | 
|---|
| 8704 | * introduced. | 
|---|
| 8705 | * In order to not bother drivers with this quirk in the API | 
|---|
| 8706 | * set the flags in both the mask and set for new stations in | 
|---|
| 8707 | * this case. | 
|---|
| 8708 | */ | 
|---|
| 8709 | if (!(params.sta_flags_mask & auth_assoc)) { | 
|---|
| 8710 | params.sta_flags_mask |= auth_assoc; | 
|---|
| 8711 | params.sta_flags_set |= auth_assoc; | 
|---|
| 8712 | } | 
|---|
| 8713 |  | 
|---|
| 8714 | /* must be last in here for error handling */ | 
|---|
| 8715 | params.vlan = get_vlan(info, rdev); | 
|---|
| 8716 | if (IS_ERR(ptr: params.vlan)) | 
|---|
| 8717 | return PTR_ERR(ptr: params.vlan); | 
|---|
| 8718 | break; | 
|---|
| 8719 | case NL80211_IFTYPE_MESH_POINT: | 
|---|
| 8720 | /* ignore uAPSD data */ | 
|---|
| 8721 | params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD; | 
|---|
| 8722 |  | 
|---|
| 8723 | /* associated is disallowed */ | 
|---|
| 8724 | if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) | 
|---|
| 8725 | return -EINVAL; | 
|---|
| 8726 | /* TDLS peers cannot be added */ | 
|---|
| 8727 | if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) || | 
|---|
| 8728 | info->attrs[NL80211_ATTR_PEER_AID]) | 
|---|
| 8729 | return -EINVAL; | 
|---|
| 8730 | break; | 
|---|
| 8731 | case NL80211_IFTYPE_STATION: | 
|---|
| 8732 | case NL80211_IFTYPE_P2P_CLIENT: | 
|---|
| 8733 | /* ignore uAPSD data */ | 
|---|
| 8734 | params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD; | 
|---|
| 8735 |  | 
|---|
| 8736 | /* these are disallowed */ | 
|---|
| 8737 | if (params.sta_flags_mask & | 
|---|
| 8738 | (BIT(NL80211_STA_FLAG_ASSOCIATED) | | 
|---|
| 8739 | BIT(NL80211_STA_FLAG_AUTHENTICATED))) | 
|---|
| 8740 | return -EINVAL; | 
|---|
| 8741 | /* Only TDLS peers can be added */ | 
|---|
| 8742 | if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) | 
|---|
| 8743 | return -EINVAL; | 
|---|
| 8744 | /* Can only add if TDLS ... */ | 
|---|
| 8745 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS)) | 
|---|
| 8746 | return -EOPNOTSUPP; | 
|---|
| 8747 | /* ... with external setup is supported */ | 
|---|
| 8748 | if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)) | 
|---|
| 8749 | return -EOPNOTSUPP; | 
|---|
| 8750 | /* | 
|---|
| 8751 | * Older wpa_supplicant versions always mark the TDLS peer | 
|---|
| 8752 | * as authorized, but it shouldn't yet be. | 
|---|
| 8753 | */ | 
|---|
| 8754 | params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_AUTHORIZED); | 
|---|
| 8755 | break; | 
|---|
| 8756 | default: | 
|---|
| 8757 | return -EOPNOTSUPP; | 
|---|
| 8758 | } | 
|---|
| 8759 |  | 
|---|
| 8760 | /* be aware of params.vlan when changing code here */ | 
|---|
| 8761 |  | 
|---|
| 8762 | if (wdev->valid_links) { | 
|---|
| 8763 | if (params.link_sta_params.link_id < 0) { | 
|---|
| 8764 | err = -EINVAL; | 
|---|
| 8765 | goto out; | 
|---|
| 8766 | } | 
|---|
| 8767 | if (!(wdev->valid_links & BIT(params.link_sta_params.link_id))) { | 
|---|
| 8768 | err = -ENOLINK; | 
|---|
| 8769 | goto out; | 
|---|
| 8770 | } | 
|---|
| 8771 | } else { | 
|---|
| 8772 | if (params.link_sta_params.link_id >= 0) { | 
|---|
| 8773 | err = -EINVAL; | 
|---|
| 8774 | goto out; | 
|---|
| 8775 | } | 
|---|
| 8776 | } | 
|---|
| 8777 | err = rdev_add_station(rdev, dev, mac: mac_addr, params: ¶ms); | 
|---|
| 8778 | out: | 
|---|
| 8779 | dev_put(dev: params.vlan); | 
|---|
| 8780 | return err; | 
|---|
| 8781 | } | 
|---|
| 8782 |  | 
|---|
| 8783 | static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 8784 | { | 
|---|
| 8785 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 8786 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 8787 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 8788 | struct station_del_parameters params; | 
|---|
| 8789 | int link_id = nl80211_link_id_or_invalid(attrs: info->attrs); | 
|---|
| 8790 |  | 
|---|
| 8791 | memset(s: ¶ms, c: 0, n: sizeof(params)); | 
|---|
| 8792 |  | 
|---|
| 8793 | if (info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 8794 | params.mac = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 8795 |  | 
|---|
| 8796 | switch (wdev->iftype) { | 
|---|
| 8797 | case NL80211_IFTYPE_AP: | 
|---|
| 8798 | case NL80211_IFTYPE_AP_VLAN: | 
|---|
| 8799 | case NL80211_IFTYPE_MESH_POINT: | 
|---|
| 8800 | case NL80211_IFTYPE_P2P_GO: | 
|---|
| 8801 | /* always accept these */ | 
|---|
| 8802 | break; | 
|---|
| 8803 | case NL80211_IFTYPE_ADHOC: | 
|---|
| 8804 | /* conditionally accept */ | 
|---|
| 8805 | if (wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 8806 | ftidx: NL80211_EXT_FEATURE_DEL_IBSS_STA)) | 
|---|
| 8807 | break; | 
|---|
| 8808 | return -EINVAL; | 
|---|
| 8809 | default: | 
|---|
| 8810 | return -EINVAL; | 
|---|
| 8811 | } | 
|---|
| 8812 |  | 
|---|
| 8813 | if (!rdev->ops->del_station) | 
|---|
| 8814 | return -EOPNOTSUPP; | 
|---|
| 8815 |  | 
|---|
| 8816 | if (info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) { | 
|---|
| 8817 | params.subtype = | 
|---|
| 8818 | nla_get_u8(nla: info->attrs[NL80211_ATTR_MGMT_SUBTYPE]); | 
|---|
| 8819 | if (params.subtype != IEEE80211_STYPE_DISASSOC >> 4 && | 
|---|
| 8820 | params.subtype != IEEE80211_STYPE_DEAUTH >> 4) | 
|---|
| 8821 | return -EINVAL; | 
|---|
| 8822 | } else { | 
|---|
| 8823 | /* Default to Deauthentication frame */ | 
|---|
| 8824 | params.subtype = IEEE80211_STYPE_DEAUTH >> 4; | 
|---|
| 8825 | } | 
|---|
| 8826 |  | 
|---|
| 8827 | if (info->attrs[NL80211_ATTR_REASON_CODE]) { | 
|---|
| 8828 | params.reason_code = | 
|---|
| 8829 | nla_get_u16(nla: info->attrs[NL80211_ATTR_REASON_CODE]); | 
|---|
| 8830 | if (params.reason_code == 0) | 
|---|
| 8831 | return -EINVAL; /* 0 is reserved */ | 
|---|
| 8832 | } else { | 
|---|
| 8833 | /* Default to reason code 2 */ | 
|---|
| 8834 | params.reason_code = WLAN_REASON_PREV_AUTH_NOT_VALID; | 
|---|
| 8835 | } | 
|---|
| 8836 |  | 
|---|
| 8837 | /* Link ID not expected in case of non-ML operation */ | 
|---|
| 8838 | if (!wdev->valid_links && link_id != -1) | 
|---|
| 8839 | return -EINVAL; | 
|---|
| 8840 |  | 
|---|
| 8841 | /* If given, a valid link ID should be passed during MLO */ | 
|---|
| 8842 | if (wdev->valid_links && link_id >= 0 && | 
|---|
| 8843 | !(wdev->valid_links & BIT(link_id))) | 
|---|
| 8844 | return -EINVAL; | 
|---|
| 8845 |  | 
|---|
| 8846 | params.link_id = link_id; | 
|---|
| 8847 |  | 
|---|
| 8848 | return rdev_del_station(rdev, dev, params: ¶ms); | 
|---|
| 8849 | } | 
|---|
| 8850 |  | 
|---|
| 8851 | static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq, | 
|---|
| 8852 | int flags, struct net_device *dev, | 
|---|
| 8853 | u8 *dst, u8 *next_hop, | 
|---|
| 8854 | struct mpath_info *pinfo) | 
|---|
| 8855 | { | 
|---|
| 8856 | void *hdr; | 
|---|
| 8857 | struct nlattr *pinfoattr; | 
|---|
| 8858 |  | 
|---|
| 8859 | hdr = nl80211hdr_put(skb: msg, portid, seq, flags, cmd: NL80211_CMD_NEW_MPATH); | 
|---|
| 8860 | if (!hdr) | 
|---|
| 8861 | return -1; | 
|---|
| 8862 |  | 
|---|
| 8863 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || | 
|---|
| 8864 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: dst) || | 
|---|
| 8865 | nla_put(skb: msg, attrtype: NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, data: next_hop) || | 
|---|
| 8866 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_GENERATION, value: pinfo->generation)) | 
|---|
| 8867 | goto nla_put_failure; | 
|---|
| 8868 |  | 
|---|
| 8869 | pinfoattr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_MPATH_INFO); | 
|---|
| 8870 | if (!pinfoattr) | 
|---|
| 8871 | goto nla_put_failure; | 
|---|
| 8872 | if ((pinfo->filled & MPATH_INFO_FRAME_QLEN) && | 
|---|
| 8873 | nla_put_u32(skb: msg, attrtype: NL80211_MPATH_INFO_FRAME_QLEN, | 
|---|
| 8874 | value: pinfo->frame_qlen)) | 
|---|
| 8875 | goto nla_put_failure; | 
|---|
| 8876 | if (((pinfo->filled & MPATH_INFO_SN) && | 
|---|
| 8877 | nla_put_u32(skb: msg, attrtype: NL80211_MPATH_INFO_SN, value: pinfo->sn)) || | 
|---|
| 8878 | ((pinfo->filled & MPATH_INFO_METRIC) && | 
|---|
| 8879 | nla_put_u32(skb: msg, attrtype: NL80211_MPATH_INFO_METRIC, | 
|---|
| 8880 | value: pinfo->metric)) || | 
|---|
| 8881 | ((pinfo->filled & MPATH_INFO_EXPTIME) && | 
|---|
| 8882 | nla_put_u32(skb: msg, attrtype: NL80211_MPATH_INFO_EXPTIME, | 
|---|
| 8883 | value: pinfo->exptime)) || | 
|---|
| 8884 | ((pinfo->filled & MPATH_INFO_FLAGS) && | 
|---|
| 8885 | nla_put_u8(skb: msg, attrtype: NL80211_MPATH_INFO_FLAGS, | 
|---|
| 8886 | value: pinfo->flags)) || | 
|---|
| 8887 | ((pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT) && | 
|---|
| 8888 | nla_put_u32(skb: msg, attrtype: NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, | 
|---|
| 8889 | value: pinfo->discovery_timeout)) || | 
|---|
| 8890 | ((pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES) && | 
|---|
| 8891 | nla_put_u8(skb: msg, attrtype: NL80211_MPATH_INFO_DISCOVERY_RETRIES, | 
|---|
| 8892 | value: pinfo->discovery_retries)) || | 
|---|
| 8893 | ((pinfo->filled & MPATH_INFO_HOP_COUNT) && | 
|---|
| 8894 | nla_put_u8(skb: msg, attrtype: NL80211_MPATH_INFO_HOP_COUNT, | 
|---|
| 8895 | value: pinfo->hop_count)) || | 
|---|
| 8896 | ((pinfo->filled & MPATH_INFO_PATH_CHANGE) && | 
|---|
| 8897 | nla_put_u32(skb: msg, attrtype: NL80211_MPATH_INFO_PATH_CHANGE, | 
|---|
| 8898 | value: pinfo->path_change_count))) | 
|---|
| 8899 | goto nla_put_failure; | 
|---|
| 8900 |  | 
|---|
| 8901 | nla_nest_end(skb: msg, start: pinfoattr); | 
|---|
| 8902 |  | 
|---|
| 8903 | genlmsg_end(skb: msg, hdr); | 
|---|
| 8904 | return 0; | 
|---|
| 8905 |  | 
|---|
| 8906 | nla_put_failure: | 
|---|
| 8907 | genlmsg_cancel(skb: msg, hdr); | 
|---|
| 8908 | return -EMSGSIZE; | 
|---|
| 8909 | } | 
|---|
| 8910 |  | 
|---|
| 8911 | static int nl80211_dump_mpath(struct sk_buff *skb, | 
|---|
| 8912 | struct netlink_callback *cb) | 
|---|
| 8913 | { | 
|---|
| 8914 | struct mpath_info pinfo; | 
|---|
| 8915 | struct cfg80211_registered_device *rdev; | 
|---|
| 8916 | struct wireless_dev *wdev; | 
|---|
| 8917 | u8 dst[ETH_ALEN]; | 
|---|
| 8918 | u8 next_hop[ETH_ALEN]; | 
|---|
| 8919 | int path_idx = cb->args[2]; | 
|---|
| 8920 | int err; | 
|---|
| 8921 |  | 
|---|
| 8922 | err = nl80211_prepare_wdev_dump(cb, rdev: &rdev, wdev: &wdev, NULL); | 
|---|
| 8923 | if (err) | 
|---|
| 8924 | return err; | 
|---|
| 8925 | /* nl80211_prepare_wdev_dump acquired it in the successful case */ | 
|---|
| 8926 | __acquire(&rdev->wiphy.mtx); | 
|---|
| 8927 |  | 
|---|
| 8928 | if (!rdev->ops->dump_mpath) { | 
|---|
| 8929 | err = -EOPNOTSUPP; | 
|---|
| 8930 | goto out_err; | 
|---|
| 8931 | } | 
|---|
| 8932 |  | 
|---|
| 8933 | if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) { | 
|---|
| 8934 | err = -EOPNOTSUPP; | 
|---|
| 8935 | goto out_err; | 
|---|
| 8936 | } | 
|---|
| 8937 |  | 
|---|
| 8938 | while (1) { | 
|---|
| 8939 | err = rdev_dump_mpath(rdev, dev: wdev->netdev, idx: path_idx, dst, | 
|---|
| 8940 | next_hop, pinfo: &pinfo); | 
|---|
| 8941 | if (err == -ENOENT) | 
|---|
| 8942 | break; | 
|---|
| 8943 | if (err) | 
|---|
| 8944 | goto out_err; | 
|---|
| 8945 |  | 
|---|
| 8946 | if (nl80211_send_mpath(msg: skb, NETLINK_CB(cb->skb).portid, | 
|---|
| 8947 | seq: cb->nlh->nlmsg_seq, NLM_F_MULTI, | 
|---|
| 8948 | dev: wdev->netdev, dst, next_hop, | 
|---|
| 8949 | pinfo: &pinfo) < 0) | 
|---|
| 8950 | goto out; | 
|---|
| 8951 |  | 
|---|
| 8952 | path_idx++; | 
|---|
| 8953 | } | 
|---|
| 8954 |  | 
|---|
| 8955 | out: | 
|---|
| 8956 | cb->args[2] = path_idx; | 
|---|
| 8957 | err = skb->len; | 
|---|
| 8958 | out_err: | 
|---|
| 8959 | wiphy_unlock(wiphy: &rdev->wiphy); | 
|---|
| 8960 | return err; | 
|---|
| 8961 | } | 
|---|
| 8962 |  | 
|---|
| 8963 | static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 8964 | { | 
|---|
| 8965 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 8966 | int err; | 
|---|
| 8967 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 8968 | struct mpath_info pinfo; | 
|---|
| 8969 | struct sk_buff *msg; | 
|---|
| 8970 | u8 *dst = NULL; | 
|---|
| 8971 | u8 next_hop[ETH_ALEN]; | 
|---|
| 8972 |  | 
|---|
| 8973 | memset(s: &pinfo, c: 0, n: sizeof(pinfo)); | 
|---|
| 8974 |  | 
|---|
| 8975 | if (!info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 8976 | return -EINVAL; | 
|---|
| 8977 |  | 
|---|
| 8978 | dst = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 8979 |  | 
|---|
| 8980 | if (!rdev->ops->get_mpath) | 
|---|
| 8981 | return -EOPNOTSUPP; | 
|---|
| 8982 |  | 
|---|
| 8983 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) | 
|---|
| 8984 | return -EOPNOTSUPP; | 
|---|
| 8985 |  | 
|---|
| 8986 | err = rdev_get_mpath(rdev, dev, dst, next_hop, pinfo: &pinfo); | 
|---|
| 8987 | if (err) | 
|---|
| 8988 | return err; | 
|---|
| 8989 |  | 
|---|
| 8990 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 8991 | if (!msg) | 
|---|
| 8992 | return -ENOMEM; | 
|---|
| 8993 |  | 
|---|
| 8994 | if (nl80211_send_mpath(msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, | 
|---|
| 8995 | dev, dst, next_hop, pinfo: &pinfo) < 0) { | 
|---|
| 8996 | nlmsg_free(skb: msg); | 
|---|
| 8997 | return -ENOBUFS; | 
|---|
| 8998 | } | 
|---|
| 8999 |  | 
|---|
| 9000 | return genlmsg_reply(skb: msg, info); | 
|---|
| 9001 | } | 
|---|
| 9002 |  | 
|---|
| 9003 | static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 9004 | { | 
|---|
| 9005 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 9006 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 9007 | u8 *dst = NULL; | 
|---|
| 9008 | u8 *next_hop = NULL; | 
|---|
| 9009 |  | 
|---|
| 9010 | if (!info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 9011 | return -EINVAL; | 
|---|
| 9012 |  | 
|---|
| 9013 | if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]) | 
|---|
| 9014 | return -EINVAL; | 
|---|
| 9015 |  | 
|---|
| 9016 | dst = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 9017 | next_hop = nla_data(nla: info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); | 
|---|
| 9018 |  | 
|---|
| 9019 | if (!rdev->ops->change_mpath) | 
|---|
| 9020 | return -EOPNOTSUPP; | 
|---|
| 9021 |  | 
|---|
| 9022 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) | 
|---|
| 9023 | return -EOPNOTSUPP; | 
|---|
| 9024 |  | 
|---|
| 9025 | return rdev_change_mpath(rdev, dev, dst, next_hop); | 
|---|
| 9026 | } | 
|---|
| 9027 |  | 
|---|
| 9028 | static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 9029 | { | 
|---|
| 9030 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 9031 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 9032 | u8 *dst = NULL; | 
|---|
| 9033 | u8 *next_hop = NULL; | 
|---|
| 9034 |  | 
|---|
| 9035 | if (!info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 9036 | return -EINVAL; | 
|---|
| 9037 |  | 
|---|
| 9038 | if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]) | 
|---|
| 9039 | return -EINVAL; | 
|---|
| 9040 |  | 
|---|
| 9041 | dst = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 9042 | next_hop = nla_data(nla: info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); | 
|---|
| 9043 |  | 
|---|
| 9044 | if (!rdev->ops->add_mpath) | 
|---|
| 9045 | return -EOPNOTSUPP; | 
|---|
| 9046 |  | 
|---|
| 9047 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) | 
|---|
| 9048 | return -EOPNOTSUPP; | 
|---|
| 9049 |  | 
|---|
| 9050 | return rdev_add_mpath(rdev, dev, dst, next_hop); | 
|---|
| 9051 | } | 
|---|
| 9052 |  | 
|---|
| 9053 | static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 9054 | { | 
|---|
| 9055 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 9056 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 9057 | u8 *dst = NULL; | 
|---|
| 9058 |  | 
|---|
| 9059 | if (info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 9060 | dst = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 9061 |  | 
|---|
| 9062 | if (!rdev->ops->del_mpath) | 
|---|
| 9063 | return -EOPNOTSUPP; | 
|---|
| 9064 |  | 
|---|
| 9065 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) | 
|---|
| 9066 | return -EOPNOTSUPP; | 
|---|
| 9067 |  | 
|---|
| 9068 | return rdev_del_mpath(rdev, dev, dst); | 
|---|
| 9069 | } | 
|---|
| 9070 |  | 
|---|
| 9071 | static int nl80211_get_mpp(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 9072 | { | 
|---|
| 9073 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 9074 | int err; | 
|---|
| 9075 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 9076 | struct mpath_info pinfo; | 
|---|
| 9077 | struct sk_buff *msg; | 
|---|
| 9078 | u8 *dst = NULL; | 
|---|
| 9079 | u8 mpp[ETH_ALEN]; | 
|---|
| 9080 |  | 
|---|
| 9081 | memset(s: &pinfo, c: 0, n: sizeof(pinfo)); | 
|---|
| 9082 |  | 
|---|
| 9083 | if (!info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 9084 | return -EINVAL; | 
|---|
| 9085 |  | 
|---|
| 9086 | dst = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 9087 |  | 
|---|
| 9088 | if (!rdev->ops->get_mpp) | 
|---|
| 9089 | return -EOPNOTSUPP; | 
|---|
| 9090 |  | 
|---|
| 9091 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) | 
|---|
| 9092 | return -EOPNOTSUPP; | 
|---|
| 9093 |  | 
|---|
| 9094 | err = rdev_get_mpp(rdev, dev, dst, mpp, pinfo: &pinfo); | 
|---|
| 9095 | if (err) | 
|---|
| 9096 | return err; | 
|---|
| 9097 |  | 
|---|
| 9098 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 9099 | if (!msg) | 
|---|
| 9100 | return -ENOMEM; | 
|---|
| 9101 |  | 
|---|
| 9102 | if (nl80211_send_mpath(msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, | 
|---|
| 9103 | dev, dst, next_hop: mpp, pinfo: &pinfo) < 0) { | 
|---|
| 9104 | nlmsg_free(skb: msg); | 
|---|
| 9105 | return -ENOBUFS; | 
|---|
| 9106 | } | 
|---|
| 9107 |  | 
|---|
| 9108 | return genlmsg_reply(skb: msg, info); | 
|---|
| 9109 | } | 
|---|
| 9110 |  | 
|---|
| 9111 | static int nl80211_dump_mpp(struct sk_buff *skb, | 
|---|
| 9112 | struct netlink_callback *cb) | 
|---|
| 9113 | { | 
|---|
| 9114 | struct mpath_info pinfo; | 
|---|
| 9115 | struct cfg80211_registered_device *rdev; | 
|---|
| 9116 | struct wireless_dev *wdev; | 
|---|
| 9117 | u8 dst[ETH_ALEN]; | 
|---|
| 9118 | u8 mpp[ETH_ALEN]; | 
|---|
| 9119 | int path_idx = cb->args[2]; | 
|---|
| 9120 | int err; | 
|---|
| 9121 |  | 
|---|
| 9122 | err = nl80211_prepare_wdev_dump(cb, rdev: &rdev, wdev: &wdev, NULL); | 
|---|
| 9123 | if (err) | 
|---|
| 9124 | return err; | 
|---|
| 9125 | /* nl80211_prepare_wdev_dump acquired it in the successful case */ | 
|---|
| 9126 | __acquire(&rdev->wiphy.mtx); | 
|---|
| 9127 |  | 
|---|
| 9128 | if (!rdev->ops->dump_mpp) { | 
|---|
| 9129 | err = -EOPNOTSUPP; | 
|---|
| 9130 | goto out_err; | 
|---|
| 9131 | } | 
|---|
| 9132 |  | 
|---|
| 9133 | if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) { | 
|---|
| 9134 | err = -EOPNOTSUPP; | 
|---|
| 9135 | goto out_err; | 
|---|
| 9136 | } | 
|---|
| 9137 |  | 
|---|
| 9138 | while (1) { | 
|---|
| 9139 | err = rdev_dump_mpp(rdev, dev: wdev->netdev, idx: path_idx, dst, | 
|---|
| 9140 | mpp, pinfo: &pinfo); | 
|---|
| 9141 | if (err == -ENOENT) | 
|---|
| 9142 | break; | 
|---|
| 9143 | if (err) | 
|---|
| 9144 | goto out_err; | 
|---|
| 9145 |  | 
|---|
| 9146 | if (nl80211_send_mpath(msg: skb, NETLINK_CB(cb->skb).portid, | 
|---|
| 9147 | seq: cb->nlh->nlmsg_seq, NLM_F_MULTI, | 
|---|
| 9148 | dev: wdev->netdev, dst, next_hop: mpp, | 
|---|
| 9149 | pinfo: &pinfo) < 0) | 
|---|
| 9150 | goto out; | 
|---|
| 9151 |  | 
|---|
| 9152 | path_idx++; | 
|---|
| 9153 | } | 
|---|
| 9154 |  | 
|---|
| 9155 | out: | 
|---|
| 9156 | cb->args[2] = path_idx; | 
|---|
| 9157 | err = skb->len; | 
|---|
| 9158 | out_err: | 
|---|
| 9159 | wiphy_unlock(wiphy: &rdev->wiphy); | 
|---|
| 9160 | return err; | 
|---|
| 9161 | } | 
|---|
| 9162 |  | 
|---|
| 9163 | static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 9164 | { | 
|---|
| 9165 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 9166 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 9167 | struct bss_parameters params; | 
|---|
| 9168 | u32 bss_param_support = rdev->wiphy.bss_param_support; | 
|---|
| 9169 | u32 changed = 0; | 
|---|
| 9170 | bool strict; | 
|---|
| 9171 |  | 
|---|
| 9172 | memset(s: ¶ms, c: 0, n: sizeof(params)); | 
|---|
| 9173 | params.link_id = nl80211_link_id_or_invalid(attrs: info->attrs); | 
|---|
| 9174 | /* default to not changing parameters */ | 
|---|
| 9175 | params.use_cts_prot = -1; | 
|---|
| 9176 | params.use_short_preamble = -1; | 
|---|
| 9177 | params.use_short_slot_time = -1; | 
|---|
| 9178 | params.ap_isolate = -1; | 
|---|
| 9179 | params.ht_opmode = -1; | 
|---|
| 9180 | params.p2p_ctwindow = -1; | 
|---|
| 9181 | params.p2p_opp_ps = -1; | 
|---|
| 9182 |  | 
|---|
| 9183 | strict = nla_get_flag(nla: info->attrs[NL80211_ATTR_BSS_PARAM]); | 
|---|
| 9184 | if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) { | 
|---|
| 9185 | if (strict && !(bss_param_support & WIPHY_BSS_PARAM_CTS_PROT)) | 
|---|
| 9186 | return -EINVAL; | 
|---|
| 9187 | params.use_cts_prot = | 
|---|
| 9188 | nla_get_u8(nla: info->attrs[NL80211_ATTR_BSS_CTS_PROT]); | 
|---|
| 9189 | changed |= WIPHY_BSS_PARAM_CTS_PROT; | 
|---|
| 9190 | } | 
|---|
| 9191 | if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]) { | 
|---|
| 9192 | if (strict && | 
|---|
| 9193 | !(bss_param_support & WIPHY_BSS_PARAM_SHORT_PREAMBLE)) | 
|---|
| 9194 | return -EINVAL; | 
|---|
| 9195 | params.use_short_preamble = | 
|---|
| 9196 | nla_get_u8(nla: info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]); | 
|---|
| 9197 | changed |= WIPHY_BSS_PARAM_SHORT_PREAMBLE; | 
|---|
| 9198 | } | 
|---|
| 9199 | if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]) { | 
|---|
| 9200 | if (strict && | 
|---|
| 9201 | !(bss_param_support & WIPHY_BSS_PARAM_SHORT_SLOT_TIME)) | 
|---|
| 9202 | return -EINVAL; | 
|---|
| 9203 | params.use_short_slot_time = | 
|---|
| 9204 | nla_get_u8(nla: info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]); | 
|---|
| 9205 | changed |= WIPHY_BSS_PARAM_SHORT_SLOT_TIME; | 
|---|
| 9206 | } | 
|---|
| 9207 | if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { | 
|---|
| 9208 | if (strict && | 
|---|
| 9209 | !(bss_param_support & WIPHY_BSS_PARAM_BASIC_RATES)) | 
|---|
| 9210 | return -EINVAL; | 
|---|
| 9211 | params.basic_rates = | 
|---|
| 9212 | nla_data(nla: info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); | 
|---|
| 9213 | params.basic_rates_len = | 
|---|
| 9214 | nla_len(nla: info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); | 
|---|
| 9215 | changed |= WIPHY_BSS_PARAM_BASIC_RATES; | 
|---|
| 9216 | } | 
|---|
| 9217 | if (info->attrs[NL80211_ATTR_AP_ISOLATE]) { | 
|---|
| 9218 | if (strict && !(bss_param_support & WIPHY_BSS_PARAM_AP_ISOLATE)) | 
|---|
| 9219 | return -EINVAL; | 
|---|
| 9220 | params.ap_isolate = | 
|---|
| 9221 | !!nla_get_u8(nla: info->attrs[NL80211_ATTR_AP_ISOLATE]); | 
|---|
| 9222 | changed |= WIPHY_BSS_PARAM_AP_ISOLATE; | 
|---|
| 9223 | } | 
|---|
| 9224 | if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE]) { | 
|---|
| 9225 | if (strict && !(bss_param_support & WIPHY_BSS_PARAM_HT_OPMODE)) | 
|---|
| 9226 | return -EINVAL; | 
|---|
| 9227 | params.ht_opmode = | 
|---|
| 9228 | nla_get_u16(nla: info->attrs[NL80211_ATTR_BSS_HT_OPMODE]); | 
|---|
| 9229 | changed |= WIPHY_BSS_PARAM_HT_OPMODE; | 
|---|
| 9230 | } | 
|---|
| 9231 |  | 
|---|
| 9232 | if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) { | 
|---|
| 9233 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) | 
|---|
| 9234 | return -EINVAL; | 
|---|
| 9235 | params.p2p_ctwindow = | 
|---|
| 9236 | nla_get_u8(nla: info->attrs[NL80211_ATTR_P2P_CTWINDOW]); | 
|---|
| 9237 | if (params.p2p_ctwindow != 0 && | 
|---|
| 9238 | !(bss_param_support & WIPHY_BSS_PARAM_P2P_CTWINDOW)) | 
|---|
| 9239 | return -EINVAL; | 
|---|
| 9240 | changed |= WIPHY_BSS_PARAM_P2P_CTWINDOW; | 
|---|
| 9241 | } | 
|---|
| 9242 |  | 
|---|
| 9243 | if (info->attrs[NL80211_ATTR_P2P_OPPPS]) { | 
|---|
| 9244 | u8 tmp; | 
|---|
| 9245 |  | 
|---|
| 9246 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) | 
|---|
| 9247 | return -EINVAL; | 
|---|
| 9248 | tmp = nla_get_u8(nla: info->attrs[NL80211_ATTR_P2P_OPPPS]); | 
|---|
| 9249 | if (tmp && !(bss_param_support & WIPHY_BSS_PARAM_P2P_OPPPS)) | 
|---|
| 9250 | return -EINVAL; | 
|---|
| 9251 | params.p2p_opp_ps = tmp; | 
|---|
| 9252 | if (params.p2p_opp_ps && | 
|---|
| 9253 | !(rdev->wiphy.bss_param_support & WIPHY_BSS_PARAM_P2P_OPPPS)) | 
|---|
| 9254 | return -EINVAL; | 
|---|
| 9255 | } | 
|---|
| 9256 |  | 
|---|
| 9257 | if (!rdev->ops->change_bss) | 
|---|
| 9258 | return -EOPNOTSUPP; | 
|---|
| 9259 |  | 
|---|
| 9260 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | 
|---|
| 9261 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) | 
|---|
| 9262 | return -EOPNOTSUPP; | 
|---|
| 9263 |  | 
|---|
| 9264 | changed &= rdev->wiphy.bss_param_support; | 
|---|
| 9265 | if (!changed) | 
|---|
| 9266 | return 0; | 
|---|
| 9267 |  | 
|---|
| 9268 | return rdev_change_bss(rdev, dev, params: ¶ms); | 
|---|
| 9269 | } | 
|---|
| 9270 |  | 
|---|
| 9271 | static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 9272 | { | 
|---|
| 9273 | char *data = NULL; | 
|---|
| 9274 | bool is_indoor; | 
|---|
| 9275 | enum nl80211_user_reg_hint_type user_reg_hint_type; | 
|---|
| 9276 | u32 owner_nlportid; | 
|---|
| 9277 |  | 
|---|
| 9278 | /* | 
|---|
| 9279 | * You should only get this when cfg80211 hasn't yet initialized | 
|---|
| 9280 | * completely when built-in to the kernel right between the time | 
|---|
| 9281 | * window between nl80211_init() and regulatory_init(), if that is | 
|---|
| 9282 | * even possible. | 
|---|
| 9283 | */ | 
|---|
| 9284 | if (unlikely(!rcu_access_pointer(cfg80211_regdomain))) | 
|---|
| 9285 | return -EINPROGRESS; | 
|---|
| 9286 |  | 
|---|
| 9287 | user_reg_hint_type = | 
|---|
| 9288 | nla_get_u32_default(nla: info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE], | 
|---|
| 9289 | defvalue: NL80211_USER_REG_HINT_USER); | 
|---|
| 9290 |  | 
|---|
| 9291 | switch (user_reg_hint_type) { | 
|---|
| 9292 | case NL80211_USER_REG_HINT_USER: | 
|---|
| 9293 | case NL80211_USER_REG_HINT_CELL_BASE: | 
|---|
| 9294 | if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) | 
|---|
| 9295 | return -EINVAL; | 
|---|
| 9296 |  | 
|---|
| 9297 | data = nla_data(nla: info->attrs[NL80211_ATTR_REG_ALPHA2]); | 
|---|
| 9298 | return regulatory_hint_user(alpha2: data, user_reg_hint_type); | 
|---|
| 9299 | case NL80211_USER_REG_HINT_INDOOR: | 
|---|
| 9300 | if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) { | 
|---|
| 9301 | owner_nlportid = info->snd_portid; | 
|---|
| 9302 | is_indoor = !!info->attrs[NL80211_ATTR_REG_INDOOR]; | 
|---|
| 9303 | } else { | 
|---|
| 9304 | owner_nlportid = 0; | 
|---|
| 9305 | is_indoor = true; | 
|---|
| 9306 | } | 
|---|
| 9307 |  | 
|---|
| 9308 | regulatory_hint_indoor(is_indoor, portid: owner_nlportid); | 
|---|
| 9309 | return 0; | 
|---|
| 9310 | default: | 
|---|
| 9311 | return -EINVAL; | 
|---|
| 9312 | } | 
|---|
| 9313 | } | 
|---|
| 9314 |  | 
|---|
| 9315 | static int nl80211_reload_regdb(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 9316 | { | 
|---|
| 9317 | return reg_reload_regdb(); | 
|---|
| 9318 | } | 
|---|
| 9319 |  | 
|---|
| 9320 | static int nl80211_get_mesh_config(struct sk_buff *skb, | 
|---|
| 9321 | struct genl_info *info) | 
|---|
| 9322 | { | 
|---|
| 9323 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 9324 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 9325 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 9326 | struct mesh_config cur_params; | 
|---|
| 9327 | int err = 0; | 
|---|
| 9328 | void *hdr; | 
|---|
| 9329 | struct nlattr *pinfoattr; | 
|---|
| 9330 | struct sk_buff *msg; | 
|---|
| 9331 |  | 
|---|
| 9332 | if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) | 
|---|
| 9333 | return -EOPNOTSUPP; | 
|---|
| 9334 |  | 
|---|
| 9335 | if (!rdev->ops->get_mesh_config) | 
|---|
| 9336 | return -EOPNOTSUPP; | 
|---|
| 9337 |  | 
|---|
| 9338 | /* If not connected, get default parameters */ | 
|---|
| 9339 | if (!wdev->u.mesh.id_len) | 
|---|
| 9340 | memcpy(to: &cur_params, from: &default_mesh_config, len: sizeof(cur_params)); | 
|---|
| 9341 | else | 
|---|
| 9342 | err = rdev_get_mesh_config(rdev, dev, conf: &cur_params); | 
|---|
| 9343 |  | 
|---|
| 9344 | if (err) | 
|---|
| 9345 | return err; | 
|---|
| 9346 |  | 
|---|
| 9347 | /* Draw up a netlink message to send back */ | 
|---|
| 9348 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 9349 | if (!msg) | 
|---|
| 9350 | return -ENOMEM; | 
|---|
| 9351 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, | 
|---|
| 9352 | cmd: NL80211_CMD_GET_MESH_CONFIG); | 
|---|
| 9353 | if (!hdr) | 
|---|
| 9354 | goto out; | 
|---|
| 9355 | pinfoattr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_MESH_CONFIG); | 
|---|
| 9356 | if (!pinfoattr) | 
|---|
| 9357 | goto nla_put_failure; | 
|---|
| 9358 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || | 
|---|
| 9359 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_RETRY_TIMEOUT, | 
|---|
| 9360 | value: cur_params.dot11MeshRetryTimeout) || | 
|---|
| 9361 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_CONFIRM_TIMEOUT, | 
|---|
| 9362 | value: cur_params.dot11MeshConfirmTimeout) || | 
|---|
| 9363 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_HOLDING_TIMEOUT, | 
|---|
| 9364 | value: cur_params.dot11MeshHoldingTimeout) || | 
|---|
| 9365 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_MAX_PEER_LINKS, | 
|---|
| 9366 | value: cur_params.dot11MeshMaxPeerLinks) || | 
|---|
| 9367 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_MAX_RETRIES, | 
|---|
| 9368 | value: cur_params.dot11MeshMaxRetries) || | 
|---|
| 9369 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_TTL, | 
|---|
| 9370 | value: cur_params.dot11MeshTTL) || | 
|---|
| 9371 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_ELEMENT_TTL, | 
|---|
| 9372 | value: cur_params.element_ttl) || | 
|---|
| 9373 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_AUTO_OPEN_PLINKS, | 
|---|
| 9374 | value: cur_params.auto_open_plinks) || | 
|---|
| 9375 | nla_put_u32(skb: msg, attrtype: NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, | 
|---|
| 9376 | value: cur_params.dot11MeshNbrOffsetMaxNeighbor) || | 
|---|
| 9377 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, | 
|---|
| 9378 | value: cur_params.dot11MeshHWMPmaxPREQretries) || | 
|---|
| 9379 | nla_put_u32(skb: msg, attrtype: NL80211_MESHCONF_PATH_REFRESH_TIME, | 
|---|
| 9380 | value: cur_params.path_refresh_time) || | 
|---|
| 9381 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, | 
|---|
| 9382 | value: cur_params.min_discovery_timeout) || | 
|---|
| 9383 | nla_put_u32(skb: msg, attrtype: NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, | 
|---|
| 9384 | value: cur_params.dot11MeshHWMPactivePathTimeout) || | 
|---|
| 9385 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, | 
|---|
| 9386 | value: cur_params.dot11MeshHWMPpreqMinInterval) || | 
|---|
| 9387 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, | 
|---|
| 9388 | value: cur_params.dot11MeshHWMPperrMinInterval) || | 
|---|
| 9389 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, | 
|---|
| 9390 | value: cur_params.dot11MeshHWMPnetDiameterTraversalTime) || | 
|---|
| 9391 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_HWMP_ROOTMODE, | 
|---|
| 9392 | value: cur_params.dot11MeshHWMPRootMode) || | 
|---|
| 9393 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_HWMP_RANN_INTERVAL, | 
|---|
| 9394 | value: cur_params.dot11MeshHWMPRannInterval) || | 
|---|
| 9395 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_GATE_ANNOUNCEMENTS, | 
|---|
| 9396 | value: cur_params.dot11MeshGateAnnouncementProtocol) || | 
|---|
| 9397 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_FORWARDING, | 
|---|
| 9398 | value: cur_params.dot11MeshForwarding) || | 
|---|
| 9399 | nla_put_s32(skb: msg, attrtype: NL80211_MESHCONF_RSSI_THRESHOLD, | 
|---|
| 9400 | value: cur_params.rssi_threshold) || | 
|---|
| 9401 | nla_put_u32(skb: msg, attrtype: NL80211_MESHCONF_HT_OPMODE, | 
|---|
| 9402 | value: cur_params.ht_opmode) || | 
|---|
| 9403 | nla_put_u32(skb: msg, attrtype: NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, | 
|---|
| 9404 | value: cur_params.dot11MeshHWMPactivePathToRootTimeout) || | 
|---|
| 9405 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_HWMP_ROOT_INTERVAL, | 
|---|
| 9406 | value: cur_params.dot11MeshHWMProotInterval) || | 
|---|
| 9407 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, | 
|---|
| 9408 | value: cur_params.dot11MeshHWMPconfirmationInterval) || | 
|---|
| 9409 | nla_put_u32(skb: msg, attrtype: NL80211_MESHCONF_POWER_MODE, | 
|---|
| 9410 | value: cur_params.power_mode) || | 
|---|
| 9411 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_AWAKE_WINDOW, | 
|---|
| 9412 | value: cur_params.dot11MeshAwakeWindowDuration) || | 
|---|
| 9413 | nla_put_u32(skb: msg, attrtype: NL80211_MESHCONF_PLINK_TIMEOUT, | 
|---|
| 9414 | value: cur_params.plink_timeout) || | 
|---|
| 9415 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_CONNECTED_TO_GATE, | 
|---|
| 9416 | value: cur_params.dot11MeshConnectedToMeshGate) || | 
|---|
| 9417 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_NOLEARN, | 
|---|
| 9418 | value: cur_params.dot11MeshNolearn) || | 
|---|
| 9419 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_CONNECTED_TO_AS, | 
|---|
| 9420 | value: cur_params.dot11MeshConnectedToAuthServer)) | 
|---|
| 9421 | goto nla_put_failure; | 
|---|
| 9422 | nla_nest_end(skb: msg, start: pinfoattr); | 
|---|
| 9423 | genlmsg_end(skb: msg, hdr); | 
|---|
| 9424 | return genlmsg_reply(skb: msg, info); | 
|---|
| 9425 |  | 
|---|
| 9426 | nla_put_failure: | 
|---|
| 9427 | out: | 
|---|
| 9428 | nlmsg_free(skb: msg); | 
|---|
| 9429 | return -ENOBUFS; | 
|---|
| 9430 | } | 
|---|
| 9431 |  | 
|---|
| 9432 | static const struct nla_policy | 
|---|
| 9433 | nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = { | 
|---|
| 9434 | [NL80211_MESHCONF_RETRY_TIMEOUT] = | 
|---|
| 9435 | NLA_POLICY_RANGE(NLA_U16, 1, 255), | 
|---|
| 9436 | [NL80211_MESHCONF_CONFIRM_TIMEOUT] = | 
|---|
| 9437 | NLA_POLICY_RANGE(NLA_U16, 1, 255), | 
|---|
| 9438 | [NL80211_MESHCONF_HOLDING_TIMEOUT] = | 
|---|
| 9439 | NLA_POLICY_RANGE(NLA_U16, 1, 255), | 
|---|
| 9440 | [NL80211_MESHCONF_MAX_PEER_LINKS] = | 
|---|
| 9441 | NLA_POLICY_RANGE(NLA_U16, 0, 255), | 
|---|
| 9442 | [NL80211_MESHCONF_MAX_RETRIES] = NLA_POLICY_MAX(NLA_U8, 16), | 
|---|
| 9443 | [NL80211_MESHCONF_TTL] = NLA_POLICY_MIN(NLA_U8, 1), | 
|---|
| 9444 | [NL80211_MESHCONF_ELEMENT_TTL] = NLA_POLICY_MIN(NLA_U8, 1), | 
|---|
| 9445 | [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = NLA_POLICY_MAX(NLA_U8, 1), | 
|---|
| 9446 | [NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR] = | 
|---|
| 9447 | NLA_POLICY_RANGE(NLA_U32, 1, 255), | 
|---|
| 9448 | [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 }, | 
|---|
| 9449 | [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 }, | 
|---|
| 9450 | [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = NLA_POLICY_MIN(NLA_U16, 1), | 
|---|
| 9451 | [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 }, | 
|---|
| 9452 | [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = | 
|---|
| 9453 | NLA_POLICY_MIN(NLA_U16, 1), | 
|---|
| 9454 | [NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL] = | 
|---|
| 9455 | NLA_POLICY_MIN(NLA_U16, 1), | 
|---|
| 9456 | [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = | 
|---|
| 9457 | NLA_POLICY_MIN(NLA_U16, 1), | 
|---|
| 9458 | [NL80211_MESHCONF_HWMP_ROOTMODE] = NLA_POLICY_MAX(NLA_U8, 4), | 
|---|
| 9459 | [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = | 
|---|
| 9460 | NLA_POLICY_MIN(NLA_U16, 1), | 
|---|
| 9461 | [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = NLA_POLICY_MAX(NLA_U8, 1), | 
|---|
| 9462 | [NL80211_MESHCONF_FORWARDING] = NLA_POLICY_MAX(NLA_U8, 1), | 
|---|
| 9463 | [NL80211_MESHCONF_RSSI_THRESHOLD] = | 
|---|
| 9464 | NLA_POLICY_RANGE(NLA_S32, -255, 0), | 
|---|
| 9465 | [NL80211_MESHCONF_HT_OPMODE] = { .type = NLA_U16 }, | 
|---|
| 9466 | [NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT] = { .type = NLA_U32 }, | 
|---|
| 9467 | [NL80211_MESHCONF_HWMP_ROOT_INTERVAL] = | 
|---|
| 9468 | NLA_POLICY_MIN(NLA_U16, 1), | 
|---|
| 9469 | [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = | 
|---|
| 9470 | NLA_POLICY_MIN(NLA_U16, 1), | 
|---|
| 9471 | [NL80211_MESHCONF_POWER_MODE] = | 
|---|
| 9472 | NLA_POLICY_RANGE(NLA_U32, | 
|---|
| 9473 | NL80211_MESH_POWER_ACTIVE, | 
|---|
| 9474 | NL80211_MESH_POWER_MAX), | 
|---|
| 9475 | [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 }, | 
|---|
| 9476 | [NL80211_MESHCONF_PLINK_TIMEOUT] = { .type = NLA_U32 }, | 
|---|
| 9477 | [NL80211_MESHCONF_CONNECTED_TO_GATE] = NLA_POLICY_RANGE(NLA_U8, 0, 1), | 
|---|
| 9478 | [NL80211_MESHCONF_NOLEARN] = NLA_POLICY_RANGE(NLA_U8, 0, 1), | 
|---|
| 9479 | [NL80211_MESHCONF_CONNECTED_TO_AS] = NLA_POLICY_RANGE(NLA_U8, 0, 1), | 
|---|
| 9480 | }; | 
|---|
| 9481 |  | 
|---|
| 9482 | static const struct nla_policy | 
|---|
| 9483 | nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = { | 
|---|
| 9484 | [NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC] = { .type = NLA_U8 }, | 
|---|
| 9485 | [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 }, | 
|---|
| 9486 | [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 }, | 
|---|
| 9487 | [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG }, | 
|---|
| 9488 | [NL80211_MESH_SETUP_AUTH_PROTOCOL] = { .type = NLA_U8 }, | 
|---|
| 9489 | [NL80211_MESH_SETUP_USERSPACE_MPM] = { .type = NLA_FLAG }, | 
|---|
| 9490 | [NL80211_MESH_SETUP_IE] = | 
|---|
| 9491 | NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_ie_attr, | 
|---|
| 9492 | IEEE80211_MAX_DATA_LEN), | 
|---|
| 9493 | [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG }, | 
|---|
| 9494 | }; | 
|---|
| 9495 |  | 
|---|
| 9496 | static int nl80211_parse_mesh_config(struct genl_info *info, | 
|---|
| 9497 | struct mesh_config *cfg, | 
|---|
| 9498 | u32 *mask_out) | 
|---|
| 9499 | { | 
|---|
| 9500 | struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1]; | 
|---|
| 9501 | u32 mask = 0; | 
|---|
| 9502 | u16 ht_opmode; | 
|---|
| 9503 |  | 
|---|
| 9504 | #define FILL_IN_MESH_PARAM_IF_SET(tb, cfg, param, mask, attr, fn)	\ | 
|---|
| 9505 | do {									\ | 
|---|
| 9506 | if (tb[attr]) {							\ | 
|---|
| 9507 | cfg->param = fn(tb[attr]);				\ | 
|---|
| 9508 | mask |= BIT((attr) - 1);				\ | 
|---|
| 9509 | }								\ | 
|---|
| 9510 | } while (0) | 
|---|
| 9511 |  | 
|---|
| 9512 | if (!info->attrs[NL80211_ATTR_MESH_CONFIG]) | 
|---|
| 9513 | return -EINVAL; | 
|---|
| 9514 | if (nla_parse_nested_deprecated(tb, maxtype: NL80211_MESHCONF_ATTR_MAX, nla: info->attrs[NL80211_ATTR_MESH_CONFIG], policy: nl80211_meshconf_params_policy, extack: info->extack)) | 
|---|
| 9515 | return -EINVAL; | 
|---|
| 9516 |  | 
|---|
| 9517 | /* This makes sure that there aren't more than 32 mesh config | 
|---|
| 9518 | * parameters (otherwise our bitfield scheme would not work.) */ | 
|---|
| 9519 | BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32); | 
|---|
| 9520 |  | 
|---|
| 9521 | /* Fill in the params struct */ | 
|---|
| 9522 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, mask, | 
|---|
| 9523 | NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16); | 
|---|
| 9524 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, mask, | 
|---|
| 9525 | NL80211_MESHCONF_CONFIRM_TIMEOUT, | 
|---|
| 9526 | nla_get_u16); | 
|---|
| 9527 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, mask, | 
|---|
| 9528 | NL80211_MESHCONF_HOLDING_TIMEOUT, | 
|---|
| 9529 | nla_get_u16); | 
|---|
| 9530 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, mask, | 
|---|
| 9531 | NL80211_MESHCONF_MAX_PEER_LINKS, | 
|---|
| 9532 | nla_get_u16); | 
|---|
| 9533 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, mask, | 
|---|
| 9534 | NL80211_MESHCONF_MAX_RETRIES, nla_get_u8); | 
|---|
| 9535 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, mask, | 
|---|
| 9536 | NL80211_MESHCONF_TTL, nla_get_u8); | 
|---|
| 9537 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, mask, | 
|---|
| 9538 | NL80211_MESHCONF_ELEMENT_TTL, nla_get_u8); | 
|---|
| 9539 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, mask, | 
|---|
| 9540 | NL80211_MESHCONF_AUTO_OPEN_PLINKS, | 
|---|
| 9541 | nla_get_u8); | 
|---|
| 9542 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor, | 
|---|
| 9543 | mask, | 
|---|
| 9544 | NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, | 
|---|
| 9545 | nla_get_u32); | 
|---|
| 9546 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, mask, | 
|---|
| 9547 | NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, | 
|---|
| 9548 | nla_get_u8); | 
|---|
| 9549 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, mask, | 
|---|
| 9550 | NL80211_MESHCONF_PATH_REFRESH_TIME, | 
|---|
| 9551 | nla_get_u32); | 
|---|
| 9552 | if (mask & BIT(NL80211_MESHCONF_PATH_REFRESH_TIME) && | 
|---|
| 9553 | (cfg->path_refresh_time < 1 || cfg->path_refresh_time > 65535)) | 
|---|
| 9554 | return -EINVAL; | 
|---|
| 9555 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, mask, | 
|---|
| 9556 | NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, | 
|---|
| 9557 | nla_get_u16); | 
|---|
| 9558 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, | 
|---|
| 9559 | mask, | 
|---|
| 9560 | NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, | 
|---|
| 9561 | nla_get_u32); | 
|---|
| 9562 | if (mask & BIT(NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT) && | 
|---|
| 9563 | (cfg->dot11MeshHWMPactivePathTimeout < 1 || | 
|---|
| 9564 | cfg->dot11MeshHWMPactivePathTimeout > 65535)) | 
|---|
| 9565 | return -EINVAL; | 
|---|
| 9566 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval, mask, | 
|---|
| 9567 | NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, | 
|---|
| 9568 | nla_get_u16); | 
|---|
| 9569 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval, mask, | 
|---|
| 9570 | NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, | 
|---|
| 9571 | nla_get_u16); | 
|---|
| 9572 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, | 
|---|
| 9573 | dot11MeshHWMPnetDiameterTraversalTime, mask, | 
|---|
| 9574 | NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, | 
|---|
| 9575 | nla_get_u16); | 
|---|
| 9576 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, mask, | 
|---|
| 9577 | NL80211_MESHCONF_HWMP_ROOTMODE, nla_get_u8); | 
|---|
| 9578 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, mask, | 
|---|
| 9579 | NL80211_MESHCONF_HWMP_RANN_INTERVAL, | 
|---|
| 9580 | nla_get_u16); | 
|---|
| 9581 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshGateAnnouncementProtocol, | 
|---|
| 9582 | mask, NL80211_MESHCONF_GATE_ANNOUNCEMENTS, | 
|---|
| 9583 | nla_get_u8); | 
|---|
| 9584 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, mask, | 
|---|
| 9585 | NL80211_MESHCONF_FORWARDING, nla_get_u8); | 
|---|
| 9586 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, mask, | 
|---|
| 9587 | NL80211_MESHCONF_RSSI_THRESHOLD, | 
|---|
| 9588 | nla_get_s32); | 
|---|
| 9589 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConnectedToMeshGate, mask, | 
|---|
| 9590 | NL80211_MESHCONF_CONNECTED_TO_GATE, | 
|---|
| 9591 | nla_get_u8); | 
|---|
| 9592 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConnectedToAuthServer, mask, | 
|---|
| 9593 | NL80211_MESHCONF_CONNECTED_TO_AS, | 
|---|
| 9594 | nla_get_u8); | 
|---|
| 9595 | /* | 
|---|
| 9596 | * Check HT operation mode based on | 
|---|
| 9597 | * IEEE 802.11-2016 9.4.2.57 HT Operation element. | 
|---|
| 9598 | */ | 
|---|
| 9599 | if (tb[NL80211_MESHCONF_HT_OPMODE]) { | 
|---|
| 9600 | ht_opmode = nla_get_u16(nla: tb[NL80211_MESHCONF_HT_OPMODE]); | 
|---|
| 9601 |  | 
|---|
| 9602 | if (ht_opmode & ~(IEEE80211_HT_OP_MODE_PROTECTION | | 
|---|
| 9603 | IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT | | 
|---|
| 9604 | IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT)) | 
|---|
| 9605 | return -EINVAL; | 
|---|
| 9606 |  | 
|---|
| 9607 | /* NON_HT_STA bit is reserved, but some programs set it */ | 
|---|
| 9608 | ht_opmode &= ~IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT; | 
|---|
| 9609 |  | 
|---|
| 9610 | cfg->ht_opmode = ht_opmode; | 
|---|
| 9611 | mask |= (1 << (NL80211_MESHCONF_HT_OPMODE - 1)); | 
|---|
| 9612 | } | 
|---|
| 9613 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, | 
|---|
| 9614 | dot11MeshHWMPactivePathToRootTimeout, mask, | 
|---|
| 9615 | NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, | 
|---|
| 9616 | nla_get_u32); | 
|---|
| 9617 | if (mask & BIT(NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT) && | 
|---|
| 9618 | (cfg->dot11MeshHWMPactivePathToRootTimeout < 1 || | 
|---|
| 9619 | cfg->dot11MeshHWMPactivePathToRootTimeout > 65535)) | 
|---|
| 9620 | return -EINVAL; | 
|---|
| 9621 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, mask, | 
|---|
| 9622 | NL80211_MESHCONF_HWMP_ROOT_INTERVAL, | 
|---|
| 9623 | nla_get_u16); | 
|---|
| 9624 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPconfirmationInterval, | 
|---|
| 9625 | mask, | 
|---|
| 9626 | NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, | 
|---|
| 9627 | nla_get_u16); | 
|---|
| 9628 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, power_mode, mask, | 
|---|
| 9629 | NL80211_MESHCONF_POWER_MODE, nla_get_u32); | 
|---|
| 9630 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration, mask, | 
|---|
| 9631 | NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16); | 
|---|
| 9632 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, mask, | 
|---|
| 9633 | NL80211_MESHCONF_PLINK_TIMEOUT, nla_get_u32); | 
|---|
| 9634 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNolearn, mask, | 
|---|
| 9635 | NL80211_MESHCONF_NOLEARN, nla_get_u8); | 
|---|
| 9636 | if (mask_out) | 
|---|
| 9637 | *mask_out = mask; | 
|---|
| 9638 |  | 
|---|
| 9639 | return 0; | 
|---|
| 9640 |  | 
|---|
| 9641 | #undef FILL_IN_MESH_PARAM_IF_SET | 
|---|
| 9642 | } | 
|---|
| 9643 |  | 
|---|
| 9644 | static int nl80211_parse_mesh_setup(struct genl_info *info, | 
|---|
| 9645 | struct mesh_setup *setup) | 
|---|
| 9646 | { | 
|---|
| 9647 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 9648 | struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1]; | 
|---|
| 9649 |  | 
|---|
| 9650 | if (!info->attrs[NL80211_ATTR_MESH_SETUP]) | 
|---|
| 9651 | return -EINVAL; | 
|---|
| 9652 | if (nla_parse_nested_deprecated(tb, maxtype: NL80211_MESH_SETUP_ATTR_MAX, nla: info->attrs[NL80211_ATTR_MESH_SETUP], policy: nl80211_mesh_setup_params_policy, extack: info->extack)) | 
|---|
| 9653 | return -EINVAL; | 
|---|
| 9654 |  | 
|---|
| 9655 | if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC]) | 
|---|
| 9656 | setup->sync_method = | 
|---|
| 9657 | (nla_get_u8(nla: tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])) ? | 
|---|
| 9658 | IEEE80211_SYNC_METHOD_VENDOR : | 
|---|
| 9659 | IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET; | 
|---|
| 9660 |  | 
|---|
| 9661 | if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL]) | 
|---|
| 9662 | setup->path_sel_proto = | 
|---|
| 9663 | (nla_get_u8(nla: tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ? | 
|---|
| 9664 | IEEE80211_PATH_PROTOCOL_VENDOR : | 
|---|
| 9665 | IEEE80211_PATH_PROTOCOL_HWMP; | 
|---|
| 9666 |  | 
|---|
| 9667 | if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC]) | 
|---|
| 9668 | setup->path_metric = | 
|---|
| 9669 | (nla_get_u8(nla: tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ? | 
|---|
| 9670 | IEEE80211_PATH_METRIC_VENDOR : | 
|---|
| 9671 | IEEE80211_PATH_METRIC_AIRTIME; | 
|---|
| 9672 |  | 
|---|
| 9673 | if (tb[NL80211_MESH_SETUP_IE]) { | 
|---|
| 9674 | struct nlattr *ieattr = | 
|---|
| 9675 | tb[NL80211_MESH_SETUP_IE]; | 
|---|
| 9676 | setup->ie = nla_data(nla: ieattr); | 
|---|
| 9677 | setup->ie_len = nla_len(nla: ieattr); | 
|---|
| 9678 | } | 
|---|
| 9679 | if (tb[NL80211_MESH_SETUP_USERSPACE_MPM] && | 
|---|
| 9680 | !(rdev->wiphy.features & NL80211_FEATURE_USERSPACE_MPM)) | 
|---|
| 9681 | return -EINVAL; | 
|---|
| 9682 | setup->user_mpm = nla_get_flag(nla: tb[NL80211_MESH_SETUP_USERSPACE_MPM]); | 
|---|
| 9683 | setup->is_authenticated = nla_get_flag(nla: tb[NL80211_MESH_SETUP_USERSPACE_AUTH]); | 
|---|
| 9684 | setup->is_secure = nla_get_flag(nla: tb[NL80211_MESH_SETUP_USERSPACE_AMPE]); | 
|---|
| 9685 | if (setup->is_secure) | 
|---|
| 9686 | setup->user_mpm = true; | 
|---|
| 9687 |  | 
|---|
| 9688 | if (tb[NL80211_MESH_SETUP_AUTH_PROTOCOL]) { | 
|---|
| 9689 | if (!setup->user_mpm) | 
|---|
| 9690 | return -EINVAL; | 
|---|
| 9691 | setup->auth_id = | 
|---|
| 9692 | nla_get_u8(nla: tb[NL80211_MESH_SETUP_AUTH_PROTOCOL]); | 
|---|
| 9693 | } | 
|---|
| 9694 |  | 
|---|
| 9695 | return 0; | 
|---|
| 9696 | } | 
|---|
| 9697 |  | 
|---|
| 9698 | static int nl80211_update_mesh_config(struct sk_buff *skb, | 
|---|
| 9699 | struct genl_info *info) | 
|---|
| 9700 | { | 
|---|
| 9701 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 9702 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 9703 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 9704 | struct mesh_config cfg = {}; | 
|---|
| 9705 | u32 mask; | 
|---|
| 9706 | int err; | 
|---|
| 9707 |  | 
|---|
| 9708 | if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) | 
|---|
| 9709 | return -EOPNOTSUPP; | 
|---|
| 9710 |  | 
|---|
| 9711 | if (!rdev->ops->update_mesh_config) | 
|---|
| 9712 | return -EOPNOTSUPP; | 
|---|
| 9713 |  | 
|---|
| 9714 | err = nl80211_parse_mesh_config(info, cfg: &cfg, mask_out: &mask); | 
|---|
| 9715 | if (err) | 
|---|
| 9716 | return err; | 
|---|
| 9717 |  | 
|---|
| 9718 | if (!wdev->u.mesh.id_len) | 
|---|
| 9719 | err = -ENOLINK; | 
|---|
| 9720 |  | 
|---|
| 9721 | if (!err) | 
|---|
| 9722 | err = rdev_update_mesh_config(rdev, dev, mask, nconf: &cfg); | 
|---|
| 9723 |  | 
|---|
| 9724 | return err; | 
|---|
| 9725 | } | 
|---|
| 9726 |  | 
|---|
| 9727 | static int nl80211_put_regdom(const struct ieee80211_regdomain *regdom, | 
|---|
| 9728 | struct sk_buff *msg) | 
|---|
| 9729 | { | 
|---|
| 9730 | struct nlattr *nl_reg_rules; | 
|---|
| 9731 | unsigned int i; | 
|---|
| 9732 |  | 
|---|
| 9733 | if (nla_put_string(skb: msg, attrtype: NL80211_ATTR_REG_ALPHA2, str: regdom->alpha2) || | 
|---|
| 9734 | (regdom->dfs_region && | 
|---|
| 9735 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_DFS_REGION, value: regdom->dfs_region))) | 
|---|
| 9736 | goto nla_put_failure; | 
|---|
| 9737 |  | 
|---|
| 9738 | nl_reg_rules = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_REG_RULES); | 
|---|
| 9739 | if (!nl_reg_rules) | 
|---|
| 9740 | goto nla_put_failure; | 
|---|
| 9741 |  | 
|---|
| 9742 | for (i = 0; i < regdom->n_reg_rules; i++) { | 
|---|
| 9743 | struct nlattr *nl_reg_rule; | 
|---|
| 9744 | const struct ieee80211_reg_rule *reg_rule; | 
|---|
| 9745 | const struct ieee80211_freq_range *freq_range; | 
|---|
| 9746 | const struct ieee80211_power_rule *power_rule; | 
|---|
| 9747 | unsigned int max_bandwidth_khz; | 
|---|
| 9748 |  | 
|---|
| 9749 | reg_rule = ®dom->reg_rules[i]; | 
|---|
| 9750 | freq_range = ®_rule->freq_range; | 
|---|
| 9751 | power_rule = ®_rule->power_rule; | 
|---|
| 9752 |  | 
|---|
| 9753 | nl_reg_rule = nla_nest_start_noflag(skb: msg, attrtype: i); | 
|---|
| 9754 | if (!nl_reg_rule) | 
|---|
| 9755 | goto nla_put_failure; | 
|---|
| 9756 |  | 
|---|
| 9757 | max_bandwidth_khz = freq_range->max_bandwidth_khz; | 
|---|
| 9758 | if (!max_bandwidth_khz) | 
|---|
| 9759 | max_bandwidth_khz = reg_get_max_bandwidth(rd: regdom, | 
|---|
| 9760 | rule: reg_rule); | 
|---|
| 9761 |  | 
|---|
| 9762 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_REG_RULE_FLAGS, | 
|---|
| 9763 | value: reg_rule->flags) || | 
|---|
| 9764 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_FREQ_RANGE_START, | 
|---|
| 9765 | value: freq_range->start_freq_khz) || | 
|---|
| 9766 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_FREQ_RANGE_END, | 
|---|
| 9767 | value: freq_range->end_freq_khz) || | 
|---|
| 9768 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_FREQ_RANGE_MAX_BW, | 
|---|
| 9769 | value: max_bandwidth_khz) || | 
|---|
| 9770 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, | 
|---|
| 9771 | value: power_rule->max_antenna_gain) || | 
|---|
| 9772 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_POWER_RULE_MAX_EIRP, | 
|---|
| 9773 | value: power_rule->max_eirp) || | 
|---|
| 9774 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_DFS_CAC_TIME, | 
|---|
| 9775 | value: reg_rule->dfs_cac_ms)) | 
|---|
| 9776 | goto nla_put_failure; | 
|---|
| 9777 |  | 
|---|
| 9778 | if ((reg_rule->flags & NL80211_RRF_PSD) && | 
|---|
| 9779 | nla_put_s8(skb: msg, attrtype: NL80211_ATTR_POWER_RULE_PSD, | 
|---|
| 9780 | value: reg_rule->psd)) | 
|---|
| 9781 | goto nla_put_failure; | 
|---|
| 9782 |  | 
|---|
| 9783 | nla_nest_end(skb: msg, start: nl_reg_rule); | 
|---|
| 9784 | } | 
|---|
| 9785 |  | 
|---|
| 9786 | nla_nest_end(skb: msg, start: nl_reg_rules); | 
|---|
| 9787 | return 0; | 
|---|
| 9788 |  | 
|---|
| 9789 | nla_put_failure: | 
|---|
| 9790 | return -EMSGSIZE; | 
|---|
| 9791 | } | 
|---|
| 9792 |  | 
|---|
| 9793 | static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 9794 | { | 
|---|
| 9795 | const struct ieee80211_regdomain *regdom = NULL; | 
|---|
| 9796 | struct cfg80211_registered_device *rdev; | 
|---|
| 9797 | struct wiphy *wiphy = NULL; | 
|---|
| 9798 | struct sk_buff *msg; | 
|---|
| 9799 | int err = -EMSGSIZE; | 
|---|
| 9800 | void *hdr; | 
|---|
| 9801 |  | 
|---|
| 9802 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 9803 | if (!msg) | 
|---|
| 9804 | return -ENOBUFS; | 
|---|
| 9805 |  | 
|---|
| 9806 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, | 
|---|
| 9807 | cmd: NL80211_CMD_GET_REG); | 
|---|
| 9808 | if (!hdr) | 
|---|
| 9809 | goto put_failure; | 
|---|
| 9810 |  | 
|---|
| 9811 | rtnl_lock(); | 
|---|
| 9812 |  | 
|---|
| 9813 | if (info->attrs[NL80211_ATTR_WIPHY]) { | 
|---|
| 9814 | bool self_managed; | 
|---|
| 9815 |  | 
|---|
| 9816 | rdev = cfg80211_get_dev_from_info(netns: genl_info_net(info), info); | 
|---|
| 9817 | if (IS_ERR(ptr: rdev)) { | 
|---|
| 9818 | err = PTR_ERR(ptr: rdev); | 
|---|
| 9819 | goto nla_put_failure; | 
|---|
| 9820 | } | 
|---|
| 9821 |  | 
|---|
| 9822 | wiphy = &rdev->wiphy; | 
|---|
| 9823 | self_managed = wiphy->regulatory_flags & | 
|---|
| 9824 | REGULATORY_WIPHY_SELF_MANAGED; | 
|---|
| 9825 |  | 
|---|
| 9826 | rcu_read_lock(); | 
|---|
| 9827 |  | 
|---|
| 9828 | regdom = get_wiphy_regdom(wiphy); | 
|---|
| 9829 |  | 
|---|
| 9830 | /* a self-managed-reg device must have a private regdom */ | 
|---|
| 9831 | if (WARN_ON(!regdom && self_managed)) { | 
|---|
| 9832 | err = -EINVAL; | 
|---|
| 9833 | goto nla_put_failure_rcu; | 
|---|
| 9834 | } | 
|---|
| 9835 |  | 
|---|
| 9836 | if (regdom && | 
|---|
| 9837 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: get_wiphy_idx(wiphy))) | 
|---|
| 9838 | goto nla_put_failure_rcu; | 
|---|
| 9839 | } else { | 
|---|
| 9840 | rcu_read_lock(); | 
|---|
| 9841 | } | 
|---|
| 9842 |  | 
|---|
| 9843 | if (!wiphy && reg_last_request_cell_base() && | 
|---|
| 9844 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_USER_REG_HINT_TYPE, | 
|---|
| 9845 | value: NL80211_USER_REG_HINT_CELL_BASE)) | 
|---|
| 9846 | goto nla_put_failure_rcu; | 
|---|
| 9847 |  | 
|---|
| 9848 | if (!regdom) | 
|---|
| 9849 | regdom = rcu_dereference(cfg80211_regdomain); | 
|---|
| 9850 |  | 
|---|
| 9851 | if (nl80211_put_regdom(regdom, msg)) | 
|---|
| 9852 | goto nla_put_failure_rcu; | 
|---|
| 9853 |  | 
|---|
| 9854 | rcu_read_unlock(); | 
|---|
| 9855 |  | 
|---|
| 9856 | genlmsg_end(skb: msg, hdr); | 
|---|
| 9857 | rtnl_unlock(); | 
|---|
| 9858 | return genlmsg_reply(skb: msg, info); | 
|---|
| 9859 |  | 
|---|
| 9860 | nla_put_failure_rcu: | 
|---|
| 9861 | rcu_read_unlock(); | 
|---|
| 9862 | nla_put_failure: | 
|---|
| 9863 | rtnl_unlock(); | 
|---|
| 9864 | put_failure: | 
|---|
| 9865 | nlmsg_free(skb: msg); | 
|---|
| 9866 | return err; | 
|---|
| 9867 | } | 
|---|
| 9868 |  | 
|---|
| 9869 | static int nl80211_send_regdom(struct sk_buff *msg, struct netlink_callback *cb, | 
|---|
| 9870 | u32 seq, int flags, struct wiphy *wiphy, | 
|---|
| 9871 | const struct ieee80211_regdomain *regdom) | 
|---|
| 9872 | { | 
|---|
| 9873 | void *hdr = nl80211hdr_put(skb: msg, NETLINK_CB(cb->skb).portid, seq, flags, | 
|---|
| 9874 | cmd: NL80211_CMD_GET_REG); | 
|---|
| 9875 |  | 
|---|
| 9876 | if (!hdr) | 
|---|
| 9877 | return -1; | 
|---|
| 9878 |  | 
|---|
| 9879 | genl_dump_check_consistent(cb, user_hdr: hdr); | 
|---|
| 9880 |  | 
|---|
| 9881 | if (nl80211_put_regdom(regdom, msg)) | 
|---|
| 9882 | goto nla_put_failure; | 
|---|
| 9883 |  | 
|---|
| 9884 | if (!wiphy && reg_last_request_cell_base() && | 
|---|
| 9885 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_USER_REG_HINT_TYPE, | 
|---|
| 9886 | value: NL80211_USER_REG_HINT_CELL_BASE)) | 
|---|
| 9887 | goto nla_put_failure; | 
|---|
| 9888 |  | 
|---|
| 9889 | if (wiphy && | 
|---|
| 9890 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: get_wiphy_idx(wiphy))) | 
|---|
| 9891 | goto nla_put_failure; | 
|---|
| 9892 |  | 
|---|
| 9893 | if (wiphy && wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED && | 
|---|
| 9894 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_WIPHY_SELF_MANAGED_REG)) | 
|---|
| 9895 | goto nla_put_failure; | 
|---|
| 9896 |  | 
|---|
| 9897 | genlmsg_end(skb: msg, hdr); | 
|---|
| 9898 | return 0; | 
|---|
| 9899 |  | 
|---|
| 9900 | nla_put_failure: | 
|---|
| 9901 | genlmsg_cancel(skb: msg, hdr); | 
|---|
| 9902 | return -EMSGSIZE; | 
|---|
| 9903 | } | 
|---|
| 9904 |  | 
|---|
| 9905 | static int nl80211_get_reg_dump(struct sk_buff *skb, | 
|---|
| 9906 | struct netlink_callback *cb) | 
|---|
| 9907 | { | 
|---|
| 9908 | const struct ieee80211_regdomain *regdom = NULL; | 
|---|
| 9909 | struct cfg80211_registered_device *rdev; | 
|---|
| 9910 | int err, reg_idx, start = cb->args[2]; | 
|---|
| 9911 |  | 
|---|
| 9912 | rcu_read_lock(); | 
|---|
| 9913 |  | 
|---|
| 9914 | if (cfg80211_regdomain && start == 0) { | 
|---|
| 9915 | err = nl80211_send_regdom(msg: skb, cb, seq: cb->nlh->nlmsg_seq, | 
|---|
| 9916 | NLM_F_MULTI, NULL, | 
|---|
| 9917 | rcu_dereference(cfg80211_regdomain)); | 
|---|
| 9918 | if (err < 0) | 
|---|
| 9919 | goto out_err; | 
|---|
| 9920 | } | 
|---|
| 9921 |  | 
|---|
| 9922 | /* the global regdom is idx 0 */ | 
|---|
| 9923 | reg_idx = 1; | 
|---|
| 9924 | list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { | 
|---|
| 9925 | regdom = get_wiphy_regdom(wiphy: &rdev->wiphy); | 
|---|
| 9926 | if (!regdom) | 
|---|
| 9927 | continue; | 
|---|
| 9928 |  | 
|---|
| 9929 | if (++reg_idx <= start) | 
|---|
| 9930 | continue; | 
|---|
| 9931 |  | 
|---|
| 9932 | err = nl80211_send_regdom(msg: skb, cb, seq: cb->nlh->nlmsg_seq, | 
|---|
| 9933 | NLM_F_MULTI, wiphy: &rdev->wiphy, regdom); | 
|---|
| 9934 | if (err < 0) { | 
|---|
| 9935 | reg_idx--; | 
|---|
| 9936 | break; | 
|---|
| 9937 | } | 
|---|
| 9938 | } | 
|---|
| 9939 |  | 
|---|
| 9940 | cb->args[2] = reg_idx; | 
|---|
| 9941 | err = skb->len; | 
|---|
| 9942 | out_err: | 
|---|
| 9943 | rcu_read_unlock(); | 
|---|
| 9944 | return err; | 
|---|
| 9945 | } | 
|---|
| 9946 |  | 
|---|
| 9947 | #ifdef CONFIG_CFG80211_CRDA_SUPPORT | 
|---|
| 9948 | static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { | 
|---|
| 9949 | [NL80211_ATTR_REG_RULE_FLAGS]		= { .type = NLA_U32 }, | 
|---|
| 9950 | [NL80211_ATTR_FREQ_RANGE_START]		= { .type = NLA_U32 }, | 
|---|
| 9951 | [NL80211_ATTR_FREQ_RANGE_END]		= { .type = NLA_U32 }, | 
|---|
| 9952 | [NL80211_ATTR_FREQ_RANGE_MAX_BW]	= { .type = NLA_U32 }, | 
|---|
| 9953 | [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]	= { .type = NLA_U32 }, | 
|---|
| 9954 | [NL80211_ATTR_POWER_RULE_MAX_EIRP]	= { .type = NLA_U32 }, | 
|---|
| 9955 | [NL80211_ATTR_DFS_CAC_TIME]		= { .type = NLA_U32 }, | 
|---|
| 9956 | }; | 
|---|
| 9957 |  | 
|---|
| 9958 | static int parse_reg_rule(struct nlattr *tb[], | 
|---|
| 9959 | struct ieee80211_reg_rule *reg_rule) | 
|---|
| 9960 | { | 
|---|
| 9961 | struct ieee80211_freq_range *freq_range = ®_rule->freq_range; | 
|---|
| 9962 | struct ieee80211_power_rule *power_rule = ®_rule->power_rule; | 
|---|
| 9963 |  | 
|---|
| 9964 | if (!tb[NL80211_ATTR_REG_RULE_FLAGS]) | 
|---|
| 9965 | return -EINVAL; | 
|---|
| 9966 | if (!tb[NL80211_ATTR_FREQ_RANGE_START]) | 
|---|
| 9967 | return -EINVAL; | 
|---|
| 9968 | if (!tb[NL80211_ATTR_FREQ_RANGE_END]) | 
|---|
| 9969 | return -EINVAL; | 
|---|
| 9970 | if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) | 
|---|
| 9971 | return -EINVAL; | 
|---|
| 9972 | if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]) | 
|---|
| 9973 | return -EINVAL; | 
|---|
| 9974 |  | 
|---|
| 9975 | reg_rule->flags = nla_get_u32(nla: tb[NL80211_ATTR_REG_RULE_FLAGS]); | 
|---|
| 9976 |  | 
|---|
| 9977 | freq_range->start_freq_khz = | 
|---|
| 9978 | nla_get_u32(nla: tb[NL80211_ATTR_FREQ_RANGE_START]); | 
|---|
| 9979 | freq_range->end_freq_khz = | 
|---|
| 9980 | nla_get_u32(nla: tb[NL80211_ATTR_FREQ_RANGE_END]); | 
|---|
| 9981 | freq_range->max_bandwidth_khz = | 
|---|
| 9982 | nla_get_u32(nla: tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]); | 
|---|
| 9983 |  | 
|---|
| 9984 | power_rule->max_eirp = | 
|---|
| 9985 | nla_get_u32(nla: tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]); | 
|---|
| 9986 |  | 
|---|
| 9987 | if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]) | 
|---|
| 9988 | power_rule->max_antenna_gain = | 
|---|
| 9989 | nla_get_u32(nla: tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]); | 
|---|
| 9990 |  | 
|---|
| 9991 | if (tb[NL80211_ATTR_DFS_CAC_TIME]) | 
|---|
| 9992 | reg_rule->dfs_cac_ms = | 
|---|
| 9993 | nla_get_u32(nla: tb[NL80211_ATTR_DFS_CAC_TIME]); | 
|---|
| 9994 |  | 
|---|
| 9995 | return 0; | 
|---|
| 9996 | } | 
|---|
| 9997 |  | 
|---|
| 9998 | static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 9999 | { | 
|---|
| 10000 | struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; | 
|---|
| 10001 | struct nlattr *nl_reg_rule; | 
|---|
| 10002 | char *alpha2; | 
|---|
| 10003 | int rem_reg_rules, r; | 
|---|
| 10004 | u32 num_rules = 0, rule_idx = 0; | 
|---|
| 10005 | enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET; | 
|---|
| 10006 | struct ieee80211_regdomain *rd; | 
|---|
| 10007 |  | 
|---|
| 10008 | if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) | 
|---|
| 10009 | return -EINVAL; | 
|---|
| 10010 |  | 
|---|
| 10011 | if (!info->attrs[NL80211_ATTR_REG_RULES]) | 
|---|
| 10012 | return -EINVAL; | 
|---|
| 10013 |  | 
|---|
| 10014 | alpha2 = nla_data(nla: info->attrs[NL80211_ATTR_REG_ALPHA2]); | 
|---|
| 10015 |  | 
|---|
| 10016 | if (info->attrs[NL80211_ATTR_DFS_REGION]) | 
|---|
| 10017 | dfs_region = nla_get_u8(nla: info->attrs[NL80211_ATTR_DFS_REGION]); | 
|---|
| 10018 |  | 
|---|
| 10019 | nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], | 
|---|
| 10020 | rem_reg_rules) { | 
|---|
| 10021 | num_rules++; | 
|---|
| 10022 | if (num_rules > NL80211_MAX_SUPP_REG_RULES) | 
|---|
| 10023 | return -EINVAL; | 
|---|
| 10024 | } | 
|---|
| 10025 |  | 
|---|
| 10026 | rtnl_lock(); | 
|---|
| 10027 | if (!reg_is_valid_request(alpha2)) { | 
|---|
| 10028 | r = -EINVAL; | 
|---|
| 10029 | goto out; | 
|---|
| 10030 | } | 
|---|
| 10031 |  | 
|---|
| 10032 | rd = kzalloc(struct_size(rd, reg_rules, num_rules), GFP_KERNEL); | 
|---|
| 10033 | if (!rd) { | 
|---|
| 10034 | r = -ENOMEM; | 
|---|
| 10035 | goto out; | 
|---|
| 10036 | } | 
|---|
| 10037 |  | 
|---|
| 10038 | rd->n_reg_rules = num_rules; | 
|---|
| 10039 | rd->alpha2[0] = alpha2[0]; | 
|---|
| 10040 | rd->alpha2[1] = alpha2[1]; | 
|---|
| 10041 |  | 
|---|
| 10042 | /* | 
|---|
| 10043 | * Disable DFS master mode if the DFS region was | 
|---|
| 10044 | * not supported or known on this kernel. | 
|---|
| 10045 | */ | 
|---|
| 10046 | if (reg_supported_dfs_region(dfs_region)) | 
|---|
| 10047 | rd->dfs_region = dfs_region; | 
|---|
| 10048 |  | 
|---|
| 10049 | nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], | 
|---|
| 10050 | rem_reg_rules) { | 
|---|
| 10051 | r = nla_parse_nested_deprecated(tb, maxtype: NL80211_REG_RULE_ATTR_MAX, | 
|---|
| 10052 | nla: nl_reg_rule, policy: reg_rule_policy, | 
|---|
| 10053 | extack: info->extack); | 
|---|
| 10054 | if (r) | 
|---|
| 10055 | goto bad_reg; | 
|---|
| 10056 | r = parse_reg_rule(tb, reg_rule: &rd->reg_rules[rule_idx]); | 
|---|
| 10057 | if (r) | 
|---|
| 10058 | goto bad_reg; | 
|---|
| 10059 |  | 
|---|
| 10060 | rule_idx++; | 
|---|
| 10061 |  | 
|---|
| 10062 | if (rule_idx > NL80211_MAX_SUPP_REG_RULES) { | 
|---|
| 10063 | r = -EINVAL; | 
|---|
| 10064 | goto bad_reg; | 
|---|
| 10065 | } | 
|---|
| 10066 | } | 
|---|
| 10067 |  | 
|---|
| 10068 | r = set_regdom(rd, regd_src: REGD_SOURCE_CRDA); | 
|---|
| 10069 | /* set_regdom takes ownership of rd */ | 
|---|
| 10070 | rd = NULL; | 
|---|
| 10071 | bad_reg: | 
|---|
| 10072 | kfree(objp: rd); | 
|---|
| 10073 | out: | 
|---|
| 10074 | rtnl_unlock(); | 
|---|
| 10075 | return r; | 
|---|
| 10076 | } | 
|---|
| 10077 | #endif /* CONFIG_CFG80211_CRDA_SUPPORT */ | 
|---|
| 10078 |  | 
|---|
| 10079 | static int validate_scan_freqs(struct nlattr *freqs) | 
|---|
| 10080 | { | 
|---|
| 10081 | struct nlattr *attr1, *attr2; | 
|---|
| 10082 | int n_channels = 0, tmp1, tmp2; | 
|---|
| 10083 |  | 
|---|
| 10084 | nla_for_each_nested(attr1, freqs, tmp1) | 
|---|
| 10085 | if (nla_len(nla: attr1) != sizeof(u32)) | 
|---|
| 10086 | return 0; | 
|---|
| 10087 |  | 
|---|
| 10088 | nla_for_each_nested(attr1, freqs, tmp1) { | 
|---|
| 10089 | n_channels++; | 
|---|
| 10090 | /* | 
|---|
| 10091 | * Some hardware has a limited channel list for | 
|---|
| 10092 | * scanning, and it is pretty much nonsensical | 
|---|
| 10093 | * to scan for a channel twice, so disallow that | 
|---|
| 10094 | * and don't require drivers to check that the | 
|---|
| 10095 | * channel list they get isn't longer than what | 
|---|
| 10096 | * they can scan, as long as they can scan all | 
|---|
| 10097 | * the channels they registered at once. | 
|---|
| 10098 | */ | 
|---|
| 10099 | nla_for_each_nested(attr2, freqs, tmp2) | 
|---|
| 10100 | if (attr1 != attr2 && | 
|---|
| 10101 | nla_get_u32(nla: attr1) == nla_get_u32(nla: attr2)) | 
|---|
| 10102 | return 0; | 
|---|
| 10103 | } | 
|---|
| 10104 |  | 
|---|
| 10105 | return n_channels; | 
|---|
| 10106 | } | 
|---|
| 10107 |  | 
|---|
| 10108 | static bool is_band_valid(struct wiphy *wiphy, enum nl80211_band b) | 
|---|
| 10109 | { | 
|---|
| 10110 | return b < NUM_NL80211_BANDS && wiphy->bands[b]; | 
|---|
| 10111 | } | 
|---|
| 10112 |  | 
|---|
| 10113 | static int parse_bss_select(struct nlattr *nla, struct wiphy *wiphy, | 
|---|
| 10114 | struct cfg80211_bss_selection *bss_select) | 
|---|
| 10115 | { | 
|---|
| 10116 | struct nlattr *attr[NL80211_BSS_SELECT_ATTR_MAX + 1]; | 
|---|
| 10117 | struct nlattr *nest; | 
|---|
| 10118 | int err; | 
|---|
| 10119 | bool found = false; | 
|---|
| 10120 | int i; | 
|---|
| 10121 |  | 
|---|
| 10122 | /* only process one nested attribute */ | 
|---|
| 10123 | nest = nla_data(nla); | 
|---|
| 10124 | if (!nla_ok(nla: nest, remaining: nla_len(nla: nest))) | 
|---|
| 10125 | return -EINVAL; | 
|---|
| 10126 |  | 
|---|
| 10127 | err = nla_parse_nested_deprecated(tb: attr, maxtype: NL80211_BSS_SELECT_ATTR_MAX, | 
|---|
| 10128 | nla: nest, policy: nl80211_bss_select_policy, | 
|---|
| 10129 | NULL); | 
|---|
| 10130 | if (err) | 
|---|
| 10131 | return err; | 
|---|
| 10132 |  | 
|---|
| 10133 | /* only one attribute may be given */ | 
|---|
| 10134 | for (i = 0; i <= NL80211_BSS_SELECT_ATTR_MAX; i++) { | 
|---|
| 10135 | if (attr[i]) { | 
|---|
| 10136 | if (found) | 
|---|
| 10137 | return -EINVAL; | 
|---|
| 10138 | found = true; | 
|---|
| 10139 | } | 
|---|
| 10140 | } | 
|---|
| 10141 |  | 
|---|
| 10142 | bss_select->behaviour = __NL80211_BSS_SELECT_ATTR_INVALID; | 
|---|
| 10143 |  | 
|---|
| 10144 | if (attr[NL80211_BSS_SELECT_ATTR_RSSI]) | 
|---|
| 10145 | bss_select->behaviour = NL80211_BSS_SELECT_ATTR_RSSI; | 
|---|
| 10146 |  | 
|---|
| 10147 | if (attr[NL80211_BSS_SELECT_ATTR_BAND_PREF]) { | 
|---|
| 10148 | bss_select->behaviour = NL80211_BSS_SELECT_ATTR_BAND_PREF; | 
|---|
| 10149 | bss_select->param.band_pref = | 
|---|
| 10150 | nla_get_u32(nla: attr[NL80211_BSS_SELECT_ATTR_BAND_PREF]); | 
|---|
| 10151 | if (!is_band_valid(wiphy, b: bss_select->param.band_pref)) | 
|---|
| 10152 | return -EINVAL; | 
|---|
| 10153 | } | 
|---|
| 10154 |  | 
|---|
| 10155 | if (attr[NL80211_BSS_SELECT_ATTR_RSSI_ADJUST]) { | 
|---|
| 10156 | struct nl80211_bss_select_rssi_adjust *adj_param; | 
|---|
| 10157 |  | 
|---|
| 10158 | adj_param = nla_data(nla: attr[NL80211_BSS_SELECT_ATTR_RSSI_ADJUST]); | 
|---|
| 10159 | bss_select->behaviour = NL80211_BSS_SELECT_ATTR_RSSI_ADJUST; | 
|---|
| 10160 | bss_select->param.adjust.band = adj_param->band; | 
|---|
| 10161 | bss_select->param.adjust.delta = adj_param->delta; | 
|---|
| 10162 | if (!is_band_valid(wiphy, b: bss_select->param.adjust.band)) | 
|---|
| 10163 | return -EINVAL; | 
|---|
| 10164 | } | 
|---|
| 10165 |  | 
|---|
| 10166 | /* user-space did not provide behaviour attribute */ | 
|---|
| 10167 | if (bss_select->behaviour == __NL80211_BSS_SELECT_ATTR_INVALID) | 
|---|
| 10168 | return -EINVAL; | 
|---|
| 10169 |  | 
|---|
| 10170 | if (!(wiphy->bss_select_support & BIT(bss_select->behaviour))) | 
|---|
| 10171 | return -EINVAL; | 
|---|
| 10172 |  | 
|---|
| 10173 | return 0; | 
|---|
| 10174 | } | 
|---|
| 10175 |  | 
|---|
| 10176 | int nl80211_parse_random_mac(struct nlattr **attrs, | 
|---|
| 10177 | u8 *mac_addr, u8 *mac_addr_mask) | 
|---|
| 10178 | { | 
|---|
| 10179 | int i; | 
|---|
| 10180 |  | 
|---|
| 10181 | if (!attrs[NL80211_ATTR_MAC] && !attrs[NL80211_ATTR_MAC_MASK]) { | 
|---|
| 10182 | eth_zero_addr(addr: mac_addr); | 
|---|
| 10183 | eth_zero_addr(addr: mac_addr_mask); | 
|---|
| 10184 | mac_addr[0] = 0x2; | 
|---|
| 10185 | mac_addr_mask[0] = 0x3; | 
|---|
| 10186 |  | 
|---|
| 10187 | return 0; | 
|---|
| 10188 | } | 
|---|
| 10189 |  | 
|---|
| 10190 | /* need both or none */ | 
|---|
| 10191 | if (!attrs[NL80211_ATTR_MAC] || !attrs[NL80211_ATTR_MAC_MASK]) | 
|---|
| 10192 | return -EINVAL; | 
|---|
| 10193 |  | 
|---|
| 10194 | memcpy(to: mac_addr, from: nla_data(nla: attrs[NL80211_ATTR_MAC]), ETH_ALEN); | 
|---|
| 10195 | memcpy(to: mac_addr_mask, from: nla_data(nla: attrs[NL80211_ATTR_MAC_MASK]), ETH_ALEN); | 
|---|
| 10196 |  | 
|---|
| 10197 | /* don't allow or configure an mcast address */ | 
|---|
| 10198 | if (!is_multicast_ether_addr(addr: mac_addr_mask) || | 
|---|
| 10199 | is_multicast_ether_addr(addr: mac_addr)) | 
|---|
| 10200 | return -EINVAL; | 
|---|
| 10201 |  | 
|---|
| 10202 | /* | 
|---|
| 10203 | * allow users to pass a MAC address that has bits set outside | 
|---|
| 10204 | * of the mask, but don't bother drivers with having to deal | 
|---|
| 10205 | * with such bits | 
|---|
| 10206 | */ | 
|---|
| 10207 | for (i = 0; i < ETH_ALEN; i++) | 
|---|
| 10208 | mac_addr[i] &= mac_addr_mask[i]; | 
|---|
| 10209 |  | 
|---|
| 10210 | return 0; | 
|---|
| 10211 | } | 
|---|
| 10212 |  | 
|---|
| 10213 | static bool cfg80211_off_channel_oper_allowed(struct wireless_dev *wdev, | 
|---|
| 10214 | struct ieee80211_channel *chan) | 
|---|
| 10215 | { | 
|---|
| 10216 | unsigned int link_id; | 
|---|
| 10217 | bool all_ok = true; | 
|---|
| 10218 | int radio_idx; | 
|---|
| 10219 |  | 
|---|
| 10220 | lockdep_assert_wiphy(wdev->wiphy); | 
|---|
| 10221 |  | 
|---|
| 10222 | if (!cfg80211_wdev_channel_allowed(wdev, chan)) | 
|---|
| 10223 | return false; | 
|---|
| 10224 |  | 
|---|
| 10225 | if (!cfg80211_beaconing_iface_active(wdev)) | 
|---|
| 10226 | return true; | 
|---|
| 10227 |  | 
|---|
| 10228 | radio_idx = cfg80211_get_radio_idx_by_chan(wiphy: wdev->wiphy, chan); | 
|---|
| 10229 |  | 
|---|
| 10230 | /* | 
|---|
| 10231 | * FIXME: check if we have a free radio/link for chan | 
|---|
| 10232 | * | 
|---|
| 10233 | * This, as well as the FIXME below, requires knowing the link | 
|---|
| 10234 | * capabilities of the hardware. | 
|---|
| 10235 | */ | 
|---|
| 10236 |  | 
|---|
| 10237 | /* we cannot leave radar channels */ | 
|---|
| 10238 | for_each_valid_link(wdev, link_id) { | 
|---|
| 10239 | struct cfg80211_chan_def *chandef; | 
|---|
| 10240 | int link_radio_idx; | 
|---|
| 10241 |  | 
|---|
| 10242 | chandef = wdev_chandef(wdev, link_id); | 
|---|
| 10243 | if (!chandef || !chandef->chan) | 
|---|
| 10244 | continue; | 
|---|
| 10245 |  | 
|---|
| 10246 | if (!(chandef->chan->flags & IEEE80211_CHAN_RADAR)) | 
|---|
| 10247 | continue; | 
|---|
| 10248 |  | 
|---|
| 10249 | /* | 
|---|
| 10250 | * chandef->chan is a radar channel. If the radio/link onto | 
|---|
| 10251 | * which this radar channel falls is the same radio/link onto | 
|---|
| 10252 | * which the input 'chan' falls, off-channel operation should | 
|---|
| 10253 | * not be allowed. Hence, set 'all_ok' to false. | 
|---|
| 10254 | */ | 
|---|
| 10255 |  | 
|---|
| 10256 | link_radio_idx = cfg80211_get_radio_idx_by_chan(wiphy: wdev->wiphy, | 
|---|
| 10257 | chan: chandef->chan); | 
|---|
| 10258 | if (link_radio_idx == radio_idx) { | 
|---|
| 10259 | all_ok = false; | 
|---|
| 10260 | break; | 
|---|
| 10261 | } | 
|---|
| 10262 | } | 
|---|
| 10263 |  | 
|---|
| 10264 | if (all_ok) | 
|---|
| 10265 | return true; | 
|---|
| 10266 |  | 
|---|
| 10267 | return regulatory_pre_cac_allowed(wiphy: wdev->wiphy); | 
|---|
| 10268 | } | 
|---|
| 10269 |  | 
|---|
| 10270 | static bool nl80211_check_scan_feat(struct wiphy *wiphy, u32 flags, u32 flag, | 
|---|
| 10271 | enum nl80211_ext_feature_index feat) | 
|---|
| 10272 | { | 
|---|
| 10273 | if (!(flags & flag)) | 
|---|
| 10274 | return true; | 
|---|
| 10275 | if (wiphy_ext_feature_isset(wiphy, ftidx: feat)) | 
|---|
| 10276 | return true; | 
|---|
| 10277 | return false; | 
|---|
| 10278 | } | 
|---|
| 10279 |  | 
|---|
| 10280 | static int | 
|---|
| 10281 | nl80211_check_scan_flags(struct wiphy *wiphy, struct wireless_dev *wdev, | 
|---|
| 10282 | struct nlattr **attrs, u8 *mac_addr, u8 *mac_addr_mask, | 
|---|
| 10283 | u32 *flags, enum nl80211_feature_flags randomness_flag) | 
|---|
| 10284 | { | 
|---|
| 10285 | if (!attrs[NL80211_ATTR_SCAN_FLAGS]) | 
|---|
| 10286 | return 0; | 
|---|
| 10287 |  | 
|---|
| 10288 | *flags = nla_get_u32(nla: attrs[NL80211_ATTR_SCAN_FLAGS]); | 
|---|
| 10289 |  | 
|---|
| 10290 | if (((*flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && | 
|---|
| 10291 | !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) || | 
|---|
| 10292 | !nl80211_check_scan_feat(wiphy, flags: *flags, | 
|---|
| 10293 | flag: NL80211_SCAN_FLAG_LOW_SPAN, | 
|---|
| 10294 | feat: NL80211_EXT_FEATURE_LOW_SPAN_SCAN) || | 
|---|
| 10295 | !nl80211_check_scan_feat(wiphy, flags: *flags, | 
|---|
| 10296 | flag: NL80211_SCAN_FLAG_LOW_POWER, | 
|---|
| 10297 | feat: NL80211_EXT_FEATURE_LOW_POWER_SCAN) || | 
|---|
| 10298 | !nl80211_check_scan_feat(wiphy, flags: *flags, | 
|---|
| 10299 | flag: NL80211_SCAN_FLAG_HIGH_ACCURACY, | 
|---|
| 10300 | feat: NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN) || | 
|---|
| 10301 | !nl80211_check_scan_feat(wiphy, flags: *flags, | 
|---|
| 10302 | flag: NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME, | 
|---|
| 10303 | feat: NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME) || | 
|---|
| 10304 | !nl80211_check_scan_feat(wiphy, flags: *flags, | 
|---|
| 10305 | flag: NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP, | 
|---|
| 10306 | feat: NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP) || | 
|---|
| 10307 | !nl80211_check_scan_feat(wiphy, flags: *flags, | 
|---|
| 10308 | flag: NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION, | 
|---|
| 10309 | feat: NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION) || | 
|---|
| 10310 | !nl80211_check_scan_feat(wiphy, flags: *flags, | 
|---|
| 10311 | flag: NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE, | 
|---|
| 10312 | feat: NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE) || | 
|---|
| 10313 | !nl80211_check_scan_feat(wiphy, flags: *flags, | 
|---|
| 10314 | flag: NL80211_SCAN_FLAG_RANDOM_SN, | 
|---|
| 10315 | feat: NL80211_EXT_FEATURE_SCAN_RANDOM_SN) || | 
|---|
| 10316 | !nl80211_check_scan_feat(wiphy, flags: *flags, | 
|---|
| 10317 | flag: NL80211_SCAN_FLAG_MIN_PREQ_CONTENT, | 
|---|
| 10318 | feat: NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT)) | 
|---|
| 10319 | return -EOPNOTSUPP; | 
|---|
| 10320 |  | 
|---|
| 10321 | if (*flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { | 
|---|
| 10322 | int err; | 
|---|
| 10323 |  | 
|---|
| 10324 | if (!(wiphy->features & randomness_flag) || | 
|---|
| 10325 | (wdev && wdev->connected)) | 
|---|
| 10326 | return -EOPNOTSUPP; | 
|---|
| 10327 |  | 
|---|
| 10328 | err = nl80211_parse_random_mac(attrs, mac_addr, mac_addr_mask); | 
|---|
| 10329 | if (err) | 
|---|
| 10330 | return err; | 
|---|
| 10331 | } | 
|---|
| 10332 |  | 
|---|
| 10333 | return 0; | 
|---|
| 10334 | } | 
|---|
| 10335 |  | 
|---|
| 10336 | static int | 
|---|
| 10337 | nl80211_check_scan_flags_sched(struct wiphy *wiphy, struct wireless_dev *wdev, | 
|---|
| 10338 | struct nlattr **attrs, | 
|---|
| 10339 | struct cfg80211_sched_scan_request *req) | 
|---|
| 10340 | { | 
|---|
| 10341 | return nl80211_check_scan_flags(wiphy, wdev, attrs, | 
|---|
| 10342 | mac_addr: req->mac_addr, mac_addr_mask: req->mac_addr_mask, | 
|---|
| 10343 | flags: &req->flags, | 
|---|
| 10344 | randomness_flag: wdev ? NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR : | 
|---|
| 10345 | NL80211_FEATURE_ND_RANDOM_MAC_ADDR); | 
|---|
| 10346 | } | 
|---|
| 10347 |  | 
|---|
| 10348 | static int | 
|---|
| 10349 | nl80211_check_scan_flags_reg(struct wiphy *wiphy, struct wireless_dev *wdev, | 
|---|
| 10350 | struct nlattr **attrs, | 
|---|
| 10351 | struct cfg80211_scan_request_int *req) | 
|---|
| 10352 | { | 
|---|
| 10353 | return nl80211_check_scan_flags(wiphy, wdev, attrs, | 
|---|
| 10354 | mac_addr: req->req.mac_addr, | 
|---|
| 10355 | mac_addr_mask: req->req.mac_addr_mask, | 
|---|
| 10356 | flags: &req->req.flags, | 
|---|
| 10357 | randomness_flag: NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR); | 
|---|
| 10358 | } | 
|---|
| 10359 |  | 
|---|
| 10360 | static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 10361 | { | 
|---|
| 10362 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 10363 | struct wireless_dev *wdev = info->user_ptr[1]; | 
|---|
| 10364 | struct cfg80211_scan_request_int *request; | 
|---|
| 10365 | struct nlattr *scan_freqs = NULL; | 
|---|
| 10366 | bool scan_freqs_khz = false; | 
|---|
| 10367 | struct nlattr *attr; | 
|---|
| 10368 | struct wiphy *wiphy; | 
|---|
| 10369 | int err, tmp, n_ssids = 0, n_channels, i; | 
|---|
| 10370 | size_t ie_len, size; | 
|---|
| 10371 | size_t ssids_offset, ie_offset; | 
|---|
| 10372 |  | 
|---|
| 10373 | wiphy = &rdev->wiphy; | 
|---|
| 10374 |  | 
|---|
| 10375 | if (wdev->iftype == NL80211_IFTYPE_NAN) | 
|---|
| 10376 | return -EOPNOTSUPP; | 
|---|
| 10377 |  | 
|---|
| 10378 | if (!rdev->ops->scan) | 
|---|
| 10379 | return -EOPNOTSUPP; | 
|---|
| 10380 |  | 
|---|
| 10381 | if (rdev->scan_req || rdev->scan_msg) | 
|---|
| 10382 | return -EBUSY; | 
|---|
| 10383 |  | 
|---|
| 10384 | if (info->attrs[NL80211_ATTR_SCAN_FREQ_KHZ]) { | 
|---|
| 10385 | if (!wiphy_ext_feature_isset(wiphy, | 
|---|
| 10386 | ftidx: NL80211_EXT_FEATURE_SCAN_FREQ_KHZ)) | 
|---|
| 10387 | return -EOPNOTSUPP; | 
|---|
| 10388 | scan_freqs = info->attrs[NL80211_ATTR_SCAN_FREQ_KHZ]; | 
|---|
| 10389 | scan_freqs_khz = true; | 
|---|
| 10390 | } else if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) | 
|---|
| 10391 | scan_freqs = info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]; | 
|---|
| 10392 |  | 
|---|
| 10393 | if (scan_freqs) { | 
|---|
| 10394 | n_channels = validate_scan_freqs(freqs: scan_freqs); | 
|---|
| 10395 | if (!n_channels) | 
|---|
| 10396 | return -EINVAL; | 
|---|
| 10397 | } else { | 
|---|
| 10398 | n_channels = ieee80211_get_num_supported_channels(wiphy); | 
|---|
| 10399 | } | 
|---|
| 10400 |  | 
|---|
| 10401 | if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) | 
|---|
| 10402 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) | 
|---|
| 10403 | n_ssids++; | 
|---|
| 10404 |  | 
|---|
| 10405 | if (n_ssids > wiphy->max_scan_ssids) | 
|---|
| 10406 | return -EINVAL; | 
|---|
| 10407 |  | 
|---|
| 10408 | if (info->attrs[NL80211_ATTR_IE]) | 
|---|
| 10409 | ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 10410 | else | 
|---|
| 10411 | ie_len = 0; | 
|---|
| 10412 |  | 
|---|
| 10413 | if (ie_len > wiphy->max_scan_ie_len) | 
|---|
| 10414 | return -EINVAL; | 
|---|
| 10415 |  | 
|---|
| 10416 | size = struct_size(request, req.channels, n_channels); | 
|---|
| 10417 | ssids_offset = size; | 
|---|
| 10418 | size = size_add(addend1: size, array_size(sizeof(*request->req.ssids), n_ssids)); | 
|---|
| 10419 | ie_offset = size; | 
|---|
| 10420 | size = size_add(addend1: size, addend2: ie_len); | 
|---|
| 10421 | request = kzalloc(size, GFP_KERNEL); | 
|---|
| 10422 | if (!request) | 
|---|
| 10423 | return -ENOMEM; | 
|---|
| 10424 |  | 
|---|
| 10425 | if (n_ssids) | 
|---|
| 10426 | request->req.ssids = (void *)request + ssids_offset; | 
|---|
| 10427 | request->req.n_ssids = n_ssids; | 
|---|
| 10428 | if (ie_len) | 
|---|
| 10429 | request->req.ie = (void *)request + ie_offset; | 
|---|
| 10430 |  | 
|---|
| 10431 | i = 0; | 
|---|
| 10432 | if (scan_freqs) { | 
|---|
| 10433 | /* user specified, bail out if channel not found */ | 
|---|
| 10434 | nla_for_each_nested(attr, scan_freqs, tmp) { | 
|---|
| 10435 | struct ieee80211_channel *chan; | 
|---|
| 10436 | int freq = nla_get_u32(nla: attr); | 
|---|
| 10437 |  | 
|---|
| 10438 | if (!scan_freqs_khz) | 
|---|
| 10439 | freq = MHZ_TO_KHZ(freq); | 
|---|
| 10440 |  | 
|---|
| 10441 | chan = ieee80211_get_channel_khz(wiphy, freq); | 
|---|
| 10442 | if (!chan) { | 
|---|
| 10443 | err = -EINVAL; | 
|---|
| 10444 | goto out_free; | 
|---|
| 10445 | } | 
|---|
| 10446 |  | 
|---|
| 10447 | /* Ignore disabled / no primary channels */ | 
|---|
| 10448 | if (chan->flags & IEEE80211_CHAN_DISABLED || | 
|---|
| 10449 | chan->flags & IEEE80211_CHAN_S1G_NO_PRIMARY || | 
|---|
| 10450 | !cfg80211_wdev_channel_allowed(wdev, chan)) | 
|---|
| 10451 | continue; | 
|---|
| 10452 |  | 
|---|
| 10453 | request->req.channels[i] = chan; | 
|---|
| 10454 | i++; | 
|---|
| 10455 | } | 
|---|
| 10456 | } else { | 
|---|
| 10457 | enum nl80211_band band; | 
|---|
| 10458 |  | 
|---|
| 10459 | /* all channels */ | 
|---|
| 10460 | for (band = 0; band < NUM_NL80211_BANDS; band++) { | 
|---|
| 10461 | int j; | 
|---|
| 10462 |  | 
|---|
| 10463 | if (!wiphy->bands[band]) | 
|---|
| 10464 | continue; | 
|---|
| 10465 | for (j = 0; j < wiphy->bands[band]->n_channels; j++) { | 
|---|
| 10466 | struct ieee80211_channel *chan; | 
|---|
| 10467 |  | 
|---|
| 10468 | chan = &wiphy->bands[band]->channels[j]; | 
|---|
| 10469 |  | 
|---|
| 10470 | if (chan->flags & IEEE80211_CHAN_DISABLED || | 
|---|
| 10471 | chan->flags & | 
|---|
| 10472 | IEEE80211_CHAN_S1G_NO_PRIMARY || | 
|---|
| 10473 | !cfg80211_wdev_channel_allowed(wdev, chan)) | 
|---|
| 10474 | continue; | 
|---|
| 10475 |  | 
|---|
| 10476 | request->req.channels[i] = chan; | 
|---|
| 10477 | i++; | 
|---|
| 10478 | } | 
|---|
| 10479 | } | 
|---|
| 10480 | } | 
|---|
| 10481 |  | 
|---|
| 10482 | if (!i) { | 
|---|
| 10483 | err = -EINVAL; | 
|---|
| 10484 | goto out_free; | 
|---|
| 10485 | } | 
|---|
| 10486 |  | 
|---|
| 10487 | request->req.n_channels = i; | 
|---|
| 10488 |  | 
|---|
| 10489 | for (i = 0; i < request->req.n_channels; i++) { | 
|---|
| 10490 | struct ieee80211_channel *chan = request->req.channels[i]; | 
|---|
| 10491 |  | 
|---|
| 10492 | /* if we can go off-channel to the target channel we're good */ | 
|---|
| 10493 | if (cfg80211_off_channel_oper_allowed(wdev, chan)) | 
|---|
| 10494 | continue; | 
|---|
| 10495 |  | 
|---|
| 10496 | if (!cfg80211_wdev_on_sub_chan(wdev, chan, primary_only: true)) { | 
|---|
| 10497 | err = -EBUSY; | 
|---|
| 10498 | goto out_free; | 
|---|
| 10499 | } | 
|---|
| 10500 | } | 
|---|
| 10501 |  | 
|---|
| 10502 | i = 0; | 
|---|
| 10503 | if (n_ssids) { | 
|---|
| 10504 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { | 
|---|
| 10505 | if (nla_len(nla: attr) > IEEE80211_MAX_SSID_LEN) { | 
|---|
| 10506 | err = -EINVAL; | 
|---|
| 10507 | goto out_free; | 
|---|
| 10508 | } | 
|---|
| 10509 | request->req.ssids[i].ssid_len = nla_len(nla: attr); | 
|---|
| 10510 | memcpy(to: request->req.ssids[i].ssid, | 
|---|
| 10511 | from: nla_data(nla: attr), len: nla_len(nla: attr)); | 
|---|
| 10512 | i++; | 
|---|
| 10513 | } | 
|---|
| 10514 | } | 
|---|
| 10515 |  | 
|---|
| 10516 | if (info->attrs[NL80211_ATTR_IE]) { | 
|---|
| 10517 | request->req.ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 10518 | memcpy(to: (void *)request->req.ie, | 
|---|
| 10519 | from: nla_data(nla: info->attrs[NL80211_ATTR_IE]), | 
|---|
| 10520 | len: request->req.ie_len); | 
|---|
| 10521 | } | 
|---|
| 10522 |  | 
|---|
| 10523 | for (i = 0; i < NUM_NL80211_BANDS; i++) | 
|---|
| 10524 | if (wiphy->bands[i]) | 
|---|
| 10525 | request->req.rates[i] = | 
|---|
| 10526 | (1 << wiphy->bands[i]->n_bitrates) - 1; | 
|---|
| 10527 |  | 
|---|
| 10528 | if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) { | 
|---|
| 10529 | nla_for_each_nested(attr, | 
|---|
| 10530 | info->attrs[NL80211_ATTR_SCAN_SUPP_RATES], | 
|---|
| 10531 | tmp) { | 
|---|
| 10532 | enum nl80211_band band = nla_type(nla: attr); | 
|---|
| 10533 |  | 
|---|
| 10534 | if (band < 0 || band >= NUM_NL80211_BANDS) { | 
|---|
| 10535 | err = -EINVAL; | 
|---|
| 10536 | goto out_free; | 
|---|
| 10537 | } | 
|---|
| 10538 |  | 
|---|
| 10539 | if (!wiphy->bands[band]) | 
|---|
| 10540 | continue; | 
|---|
| 10541 |  | 
|---|
| 10542 | err = ieee80211_get_ratemask(sband: wiphy->bands[band], | 
|---|
| 10543 | rates: nla_data(nla: attr), | 
|---|
| 10544 | n_rates: nla_len(nla: attr), | 
|---|
| 10545 | mask: &request->req.rates[band]); | 
|---|
| 10546 | if (err) | 
|---|
| 10547 | goto out_free; | 
|---|
| 10548 | } | 
|---|
| 10549 | } | 
|---|
| 10550 |  | 
|---|
| 10551 | if (info->attrs[NL80211_ATTR_MEASUREMENT_DURATION]) { | 
|---|
| 10552 | request->req.duration = | 
|---|
| 10553 | nla_get_u16(nla: info->attrs[NL80211_ATTR_MEASUREMENT_DURATION]); | 
|---|
| 10554 | request->req.duration_mandatory = | 
|---|
| 10555 | nla_get_flag(nla: info->attrs[NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY]); | 
|---|
| 10556 | } | 
|---|
| 10557 |  | 
|---|
| 10558 | err = nl80211_check_scan_flags_reg(wiphy, wdev, attrs: info->attrs, req: request); | 
|---|
| 10559 | if (err) | 
|---|
| 10560 | goto out_free; | 
|---|
| 10561 |  | 
|---|
| 10562 | request->req.no_cck = | 
|---|
| 10563 | nla_get_flag(nla: info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); | 
|---|
| 10564 |  | 
|---|
| 10565 | /* Initial implementation used NL80211_ATTR_MAC to set the specific | 
|---|
| 10566 | * BSSID to scan for. This was problematic because that same attribute | 
|---|
| 10567 | * was already used for another purpose (local random MAC address). The | 
|---|
| 10568 | * NL80211_ATTR_BSSID attribute was added to fix this. For backwards | 
|---|
| 10569 | * compatibility with older userspace components, also use the | 
|---|
| 10570 | * NL80211_ATTR_MAC value here if it can be determined to be used for | 
|---|
| 10571 | * the specific BSSID use case instead of the random MAC address | 
|---|
| 10572 | * (NL80211_ATTR_SCAN_FLAGS is used to enable random MAC address use). | 
|---|
| 10573 | */ | 
|---|
| 10574 | if (info->attrs[NL80211_ATTR_BSSID]) | 
|---|
| 10575 | memcpy(to: request->req.bssid, | 
|---|
| 10576 | from: nla_data(nla: info->attrs[NL80211_ATTR_BSSID]), ETH_ALEN); | 
|---|
| 10577 | else if (!(request->req.flags & NL80211_SCAN_FLAG_RANDOM_ADDR) && | 
|---|
| 10578 | info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 10579 | memcpy(to: request->req.bssid, | 
|---|
| 10580 | from: nla_data(nla: info->attrs[NL80211_ATTR_MAC]), | 
|---|
| 10581 | ETH_ALEN); | 
|---|
| 10582 | else | 
|---|
| 10583 | eth_broadcast_addr(addr: request->req.bssid); | 
|---|
| 10584 |  | 
|---|
| 10585 | request->req.tsf_report_link_id = | 
|---|
| 10586 | nl80211_link_id_or_invalid(attrs: info->attrs); | 
|---|
| 10587 | request->req.wdev = wdev; | 
|---|
| 10588 | request->req.wiphy = &rdev->wiphy; | 
|---|
| 10589 | request->req.scan_start = jiffies; | 
|---|
| 10590 |  | 
|---|
| 10591 | rdev->scan_req = request; | 
|---|
| 10592 | err = cfg80211_scan(rdev); | 
|---|
| 10593 |  | 
|---|
| 10594 | if (err) | 
|---|
| 10595 | goto out_free; | 
|---|
| 10596 |  | 
|---|
| 10597 | nl80211_send_scan_start(rdev, wdev); | 
|---|
| 10598 | dev_hold(dev: wdev->netdev); | 
|---|
| 10599 |  | 
|---|
| 10600 | return 0; | 
|---|
| 10601 |  | 
|---|
| 10602 | out_free: | 
|---|
| 10603 | rdev->scan_req = NULL; | 
|---|
| 10604 | kfree(objp: request); | 
|---|
| 10605 |  | 
|---|
| 10606 | return err; | 
|---|
| 10607 | } | 
|---|
| 10608 |  | 
|---|
| 10609 | static int nl80211_abort_scan(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 10610 | { | 
|---|
| 10611 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 10612 | struct wireless_dev *wdev = info->user_ptr[1]; | 
|---|
| 10613 |  | 
|---|
| 10614 | if (!rdev->ops->abort_scan) | 
|---|
| 10615 | return -EOPNOTSUPP; | 
|---|
| 10616 |  | 
|---|
| 10617 | if (rdev->scan_msg) | 
|---|
| 10618 | return 0; | 
|---|
| 10619 |  | 
|---|
| 10620 | if (!rdev->scan_req) | 
|---|
| 10621 | return -ENOENT; | 
|---|
| 10622 |  | 
|---|
| 10623 | rdev_abort_scan(rdev, wdev); | 
|---|
| 10624 | return 0; | 
|---|
| 10625 | } | 
|---|
| 10626 |  | 
|---|
| 10627 | static int | 
|---|
| 10628 | nl80211_parse_sched_scan_plans(struct wiphy *wiphy, int n_plans, | 
|---|
| 10629 | struct cfg80211_sched_scan_request *request, | 
|---|
| 10630 | struct nlattr **attrs) | 
|---|
| 10631 | { | 
|---|
| 10632 | int tmp, err, i = 0; | 
|---|
| 10633 | struct nlattr *attr; | 
|---|
| 10634 |  | 
|---|
| 10635 | if (!attrs[NL80211_ATTR_SCHED_SCAN_PLANS]) { | 
|---|
| 10636 | u32 interval; | 
|---|
| 10637 |  | 
|---|
| 10638 | /* | 
|---|
| 10639 | * If scan plans are not specified, | 
|---|
| 10640 | * %NL80211_ATTR_SCHED_SCAN_INTERVAL will be specified. In this | 
|---|
| 10641 | * case one scan plan will be set with the specified scan | 
|---|
| 10642 | * interval and infinite number of iterations. | 
|---|
| 10643 | */ | 
|---|
| 10644 | interval = nla_get_u32(nla: attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]); | 
|---|
| 10645 | if (!interval) | 
|---|
| 10646 | return -EINVAL; | 
|---|
| 10647 |  | 
|---|
| 10648 | request->scan_plans[0].interval = | 
|---|
| 10649 | DIV_ROUND_UP(interval, MSEC_PER_SEC); | 
|---|
| 10650 | if (!request->scan_plans[0].interval) | 
|---|
| 10651 | return -EINVAL; | 
|---|
| 10652 |  | 
|---|
| 10653 | if (request->scan_plans[0].interval > | 
|---|
| 10654 | wiphy->max_sched_scan_plan_interval) | 
|---|
| 10655 | request->scan_plans[0].interval = | 
|---|
| 10656 | wiphy->max_sched_scan_plan_interval; | 
|---|
| 10657 |  | 
|---|
| 10658 | return 0; | 
|---|
| 10659 | } | 
|---|
| 10660 |  | 
|---|
| 10661 | nla_for_each_nested(attr, attrs[NL80211_ATTR_SCHED_SCAN_PLANS], tmp) { | 
|---|
| 10662 | struct nlattr *plan[NL80211_SCHED_SCAN_PLAN_MAX + 1]; | 
|---|
| 10663 |  | 
|---|
| 10664 | if (WARN_ON(i >= n_plans)) | 
|---|
| 10665 | return -EINVAL; | 
|---|
| 10666 |  | 
|---|
| 10667 | err = nla_parse_nested_deprecated(tb: plan, | 
|---|
| 10668 | maxtype: NL80211_SCHED_SCAN_PLAN_MAX, | 
|---|
| 10669 | nla: attr, policy: nl80211_plan_policy, | 
|---|
| 10670 | NULL); | 
|---|
| 10671 | if (err) | 
|---|
| 10672 | return err; | 
|---|
| 10673 |  | 
|---|
| 10674 | if (!plan[NL80211_SCHED_SCAN_PLAN_INTERVAL]) | 
|---|
| 10675 | return -EINVAL; | 
|---|
| 10676 |  | 
|---|
| 10677 | request->scan_plans[i].interval = | 
|---|
| 10678 | nla_get_u32(nla: plan[NL80211_SCHED_SCAN_PLAN_INTERVAL]); | 
|---|
| 10679 | if (!request->scan_plans[i].interval || | 
|---|
| 10680 | request->scan_plans[i].interval > | 
|---|
| 10681 | wiphy->max_sched_scan_plan_interval) | 
|---|
| 10682 | return -EINVAL; | 
|---|
| 10683 |  | 
|---|
| 10684 | if (plan[NL80211_SCHED_SCAN_PLAN_ITERATIONS]) { | 
|---|
| 10685 | request->scan_plans[i].iterations = | 
|---|
| 10686 | nla_get_u32(nla: plan[NL80211_SCHED_SCAN_PLAN_ITERATIONS]); | 
|---|
| 10687 | if (!request->scan_plans[i].iterations || | 
|---|
| 10688 | (request->scan_plans[i].iterations > | 
|---|
| 10689 | wiphy->max_sched_scan_plan_iterations)) | 
|---|
| 10690 | return -EINVAL; | 
|---|
| 10691 | } else if (i < n_plans - 1) { | 
|---|
| 10692 | /* | 
|---|
| 10693 | * All scan plans but the last one must specify | 
|---|
| 10694 | * a finite number of iterations | 
|---|
| 10695 | */ | 
|---|
| 10696 | return -EINVAL; | 
|---|
| 10697 | } | 
|---|
| 10698 |  | 
|---|
| 10699 | i++; | 
|---|
| 10700 | } | 
|---|
| 10701 |  | 
|---|
| 10702 | /* | 
|---|
| 10703 | * The last scan plan must not specify the number of | 
|---|
| 10704 | * iterations, it is supposed to run infinitely | 
|---|
| 10705 | */ | 
|---|
| 10706 | if (request->scan_plans[n_plans - 1].iterations) | 
|---|
| 10707 | return  -EINVAL; | 
|---|
| 10708 |  | 
|---|
| 10709 | return 0; | 
|---|
| 10710 | } | 
|---|
| 10711 |  | 
|---|
| 10712 | static struct cfg80211_sched_scan_request * | 
|---|
| 10713 | nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev, | 
|---|
| 10714 | struct nlattr **attrs, int max_match_sets) | 
|---|
| 10715 | { | 
|---|
| 10716 | struct cfg80211_sched_scan_request *request; | 
|---|
| 10717 | struct nlattr *attr; | 
|---|
| 10718 | int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i, n_plans = 0; | 
|---|
| 10719 | enum nl80211_band band; | 
|---|
| 10720 | size_t ie_len, size; | 
|---|
| 10721 | struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1]; | 
|---|
| 10722 | s32  = NL80211_SCAN_RSSI_THOLD_OFF; | 
|---|
| 10723 |  | 
|---|
| 10724 | if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { | 
|---|
| 10725 | n_channels = validate_scan_freqs( | 
|---|
| 10726 | freqs: attrs[NL80211_ATTR_SCAN_FREQUENCIES]); | 
|---|
| 10727 | if (!n_channels) | 
|---|
| 10728 | return ERR_PTR(error: -EINVAL); | 
|---|
| 10729 | } else { | 
|---|
| 10730 | n_channels = ieee80211_get_num_supported_channels(wiphy); | 
|---|
| 10731 | } | 
|---|
| 10732 |  | 
|---|
| 10733 | if (attrs[NL80211_ATTR_SCAN_SSIDS]) | 
|---|
| 10734 | nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS], | 
|---|
| 10735 | tmp) | 
|---|
| 10736 | n_ssids++; | 
|---|
| 10737 |  | 
|---|
| 10738 | if (n_ssids > wiphy->max_sched_scan_ssids) | 
|---|
| 10739 | return ERR_PTR(error: -EINVAL); | 
|---|
| 10740 |  | 
|---|
| 10741 | /* | 
|---|
| 10742 | * First, count the number of 'real' matchsets. Due to an issue with | 
|---|
| 10743 | * the old implementation, matchsets containing only the RSSI attribute | 
|---|
| 10744 | * (NL80211_SCHED_SCAN_MATCH_ATTR_RSSI) are considered as the 'default' | 
|---|
| 10745 | * RSSI for all matchsets, rather than their own matchset for reporting | 
|---|
| 10746 | * all APs with a strong RSSI. This is needed to be compatible with | 
|---|
| 10747 | * older userspace that treated a matchset with only the RSSI as the | 
|---|
| 10748 | * global RSSI for all other matchsets - if there are other matchsets. | 
|---|
| 10749 | */ | 
|---|
| 10750 | if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) { | 
|---|
| 10751 | nla_for_each_nested(attr, | 
|---|
| 10752 | attrs[NL80211_ATTR_SCHED_SCAN_MATCH], | 
|---|
| 10753 | tmp) { | 
|---|
| 10754 | struct nlattr *; | 
|---|
| 10755 |  | 
|---|
| 10756 | err = nla_parse_nested_deprecated(tb, | 
|---|
| 10757 | maxtype: NL80211_SCHED_SCAN_MATCH_ATTR_MAX, | 
|---|
| 10758 | nla: attr, | 
|---|
| 10759 | policy: nl80211_match_policy, | 
|---|
| 10760 | NULL); | 
|---|
| 10761 | if (err) | 
|---|
| 10762 | return ERR_PTR(error: err); | 
|---|
| 10763 |  | 
|---|
| 10764 | /* SSID and BSSID are mutually exclusive */ | 
|---|
| 10765 | if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID] && | 
|---|
| 10766 | tb[NL80211_SCHED_SCAN_MATCH_ATTR_BSSID]) | 
|---|
| 10767 | return ERR_PTR(error: -EINVAL); | 
|---|
| 10768 |  | 
|---|
| 10769 | /* add other standalone attributes here */ | 
|---|
| 10770 | if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID] || | 
|---|
| 10771 | tb[NL80211_SCHED_SCAN_MATCH_ATTR_BSSID]) { | 
|---|
| 10772 | n_match_sets++; | 
|---|
| 10773 | continue; | 
|---|
| 10774 | } | 
|---|
| 10775 | rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI]; | 
|---|
| 10776 | if (rssi) | 
|---|
| 10777 | default_match_rssi = nla_get_s32(nla: rssi); | 
|---|
| 10778 | } | 
|---|
| 10779 | } | 
|---|
| 10780 |  | 
|---|
| 10781 | /* However, if there's no other matchset, add the RSSI one */ | 
|---|
| 10782 | if (!n_match_sets && default_match_rssi != NL80211_SCAN_RSSI_THOLD_OFF) | 
|---|
| 10783 | n_match_sets = 1; | 
|---|
| 10784 |  | 
|---|
| 10785 | if (n_match_sets > max_match_sets) | 
|---|
| 10786 | return ERR_PTR(error: -EINVAL); | 
|---|
| 10787 |  | 
|---|
| 10788 | if (attrs[NL80211_ATTR_IE]) | 
|---|
| 10789 | ie_len = nla_len(nla: attrs[NL80211_ATTR_IE]); | 
|---|
| 10790 | else | 
|---|
| 10791 | ie_len = 0; | 
|---|
| 10792 |  | 
|---|
| 10793 | if (ie_len > wiphy->max_sched_scan_ie_len) | 
|---|
| 10794 | return ERR_PTR(error: -EINVAL); | 
|---|
| 10795 |  | 
|---|
| 10796 | if (attrs[NL80211_ATTR_SCHED_SCAN_PLANS]) { | 
|---|
| 10797 | /* | 
|---|
| 10798 | * NL80211_ATTR_SCHED_SCAN_INTERVAL must not be specified since | 
|---|
| 10799 | * each scan plan already specifies its own interval | 
|---|
| 10800 | */ | 
|---|
| 10801 | if (attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]) | 
|---|
| 10802 | return ERR_PTR(error: -EINVAL); | 
|---|
| 10803 |  | 
|---|
| 10804 | nla_for_each_nested(attr, | 
|---|
| 10805 | attrs[NL80211_ATTR_SCHED_SCAN_PLANS], tmp) | 
|---|
| 10806 | n_plans++; | 
|---|
| 10807 | } else { | 
|---|
| 10808 | /* | 
|---|
| 10809 | * The scan interval attribute is kept for backward | 
|---|
| 10810 | * compatibility. If no scan plans are specified and sched scan | 
|---|
| 10811 | * interval is specified, one scan plan will be set with this | 
|---|
| 10812 | * scan interval and infinite number of iterations. | 
|---|
| 10813 | */ | 
|---|
| 10814 | if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]) | 
|---|
| 10815 | return ERR_PTR(error: -EINVAL); | 
|---|
| 10816 |  | 
|---|
| 10817 | n_plans = 1; | 
|---|
| 10818 | } | 
|---|
| 10819 |  | 
|---|
| 10820 | if (!n_plans || n_plans > wiphy->max_sched_scan_plans) | 
|---|
| 10821 | return ERR_PTR(error: -EINVAL); | 
|---|
| 10822 |  | 
|---|
| 10823 | if (!wiphy_ext_feature_isset( | 
|---|
| 10824 | wiphy, ftidx: NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI) && | 
|---|
| 10825 | (attrs[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI] || | 
|---|
| 10826 | attrs[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST])) | 
|---|
| 10827 | return ERR_PTR(error: -EINVAL); | 
|---|
| 10828 |  | 
|---|
| 10829 | size = struct_size(request, channels, n_channels); | 
|---|
| 10830 | size = size_add(addend1: size, array_size(sizeof(*request->ssids), n_ssids)); | 
|---|
| 10831 | size = size_add(addend1: size, array_size(sizeof(*request->match_sets), | 
|---|
| 10832 | n_match_sets)); | 
|---|
| 10833 | size = size_add(addend1: size, array_size(sizeof(*request->scan_plans), | 
|---|
| 10834 | n_plans)); | 
|---|
| 10835 | size = size_add(addend1: size, addend2: ie_len); | 
|---|
| 10836 | request = kzalloc(size, GFP_KERNEL); | 
|---|
| 10837 | if (!request) | 
|---|
| 10838 | return ERR_PTR(error: -ENOMEM); | 
|---|
| 10839 | request->n_channels = n_channels; | 
|---|
| 10840 |  | 
|---|
| 10841 | if (n_ssids) | 
|---|
| 10842 | request->ssids = (void *)request + | 
|---|
| 10843 | struct_size(request, channels, n_channels); | 
|---|
| 10844 | request->n_ssids = n_ssids; | 
|---|
| 10845 | if (ie_len) { | 
|---|
| 10846 | if (n_ssids) | 
|---|
| 10847 | request->ie = (void *)(request->ssids + n_ssids); | 
|---|
| 10848 | else | 
|---|
| 10849 | request->ie = (void *)(request->channels + n_channels); | 
|---|
| 10850 | } | 
|---|
| 10851 |  | 
|---|
| 10852 | if (n_match_sets) { | 
|---|
| 10853 | if (request->ie) | 
|---|
| 10854 | request->match_sets = (void *)(request->ie + ie_len); | 
|---|
| 10855 | else if (n_ssids) | 
|---|
| 10856 | request->match_sets = | 
|---|
| 10857 | (void *)(request->ssids + n_ssids); | 
|---|
| 10858 | else | 
|---|
| 10859 | request->match_sets = | 
|---|
| 10860 | (void *)(request->channels + n_channels); | 
|---|
| 10861 | } | 
|---|
| 10862 | request->n_match_sets = n_match_sets; | 
|---|
| 10863 |  | 
|---|
| 10864 | if (n_match_sets) | 
|---|
| 10865 | request->scan_plans = (void *)(request->match_sets + | 
|---|
| 10866 | n_match_sets); | 
|---|
| 10867 | else if (request->ie) | 
|---|
| 10868 | request->scan_plans = (void *)(request->ie + ie_len); | 
|---|
| 10869 | else if (n_ssids) | 
|---|
| 10870 | request->scan_plans = (void *)(request->ssids + n_ssids); | 
|---|
| 10871 | else | 
|---|
| 10872 | request->scan_plans = (void *)(request->channels + n_channels); | 
|---|
| 10873 |  | 
|---|
| 10874 | request->n_scan_plans = n_plans; | 
|---|
| 10875 |  | 
|---|
| 10876 | i = 0; | 
|---|
| 10877 | if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { | 
|---|
| 10878 | /* user specified, bail out if channel not found */ | 
|---|
| 10879 | nla_for_each_nested(attr, | 
|---|
| 10880 | attrs[NL80211_ATTR_SCAN_FREQUENCIES], | 
|---|
| 10881 | tmp) { | 
|---|
| 10882 | struct ieee80211_channel *chan; | 
|---|
| 10883 |  | 
|---|
| 10884 | chan = ieee80211_get_channel(wiphy, freq: nla_get_u32(nla: attr)); | 
|---|
| 10885 |  | 
|---|
| 10886 | if (!chan) { | 
|---|
| 10887 | err = -EINVAL; | 
|---|
| 10888 | goto out_free; | 
|---|
| 10889 | } | 
|---|
| 10890 |  | 
|---|
| 10891 | /* ignore disabled channels */ | 
|---|
| 10892 | if (chan->flags & IEEE80211_CHAN_DISABLED) | 
|---|
| 10893 | continue; | 
|---|
| 10894 |  | 
|---|
| 10895 | request->channels[i] = chan; | 
|---|
| 10896 | i++; | 
|---|
| 10897 | } | 
|---|
| 10898 | } else { | 
|---|
| 10899 | /* all channels */ | 
|---|
| 10900 | for (band = 0; band < NUM_NL80211_BANDS; band++) { | 
|---|
| 10901 | int j; | 
|---|
| 10902 |  | 
|---|
| 10903 | if (!wiphy->bands[band]) | 
|---|
| 10904 | continue; | 
|---|
| 10905 | for (j = 0; j < wiphy->bands[band]->n_channels; j++) { | 
|---|
| 10906 | struct ieee80211_channel *chan; | 
|---|
| 10907 |  | 
|---|
| 10908 | chan = &wiphy->bands[band]->channels[j]; | 
|---|
| 10909 |  | 
|---|
| 10910 | if (chan->flags & IEEE80211_CHAN_DISABLED) | 
|---|
| 10911 | continue; | 
|---|
| 10912 |  | 
|---|
| 10913 | request->channels[i] = chan; | 
|---|
| 10914 | i++; | 
|---|
| 10915 | } | 
|---|
| 10916 | } | 
|---|
| 10917 | } | 
|---|
| 10918 |  | 
|---|
| 10919 | if (!i) { | 
|---|
| 10920 | err = -EINVAL; | 
|---|
| 10921 | goto out_free; | 
|---|
| 10922 | } | 
|---|
| 10923 |  | 
|---|
| 10924 | request->n_channels = i; | 
|---|
| 10925 |  | 
|---|
| 10926 | i = 0; | 
|---|
| 10927 | if (n_ssids) { | 
|---|
| 10928 | nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS], | 
|---|
| 10929 | tmp) { | 
|---|
| 10930 | if (nla_len(nla: attr) > IEEE80211_MAX_SSID_LEN) { | 
|---|
| 10931 | err = -EINVAL; | 
|---|
| 10932 | goto out_free; | 
|---|
| 10933 | } | 
|---|
| 10934 | request->ssids[i].ssid_len = nla_len(nla: attr); | 
|---|
| 10935 | memcpy(to: request->ssids[i].ssid, from: nla_data(nla: attr), | 
|---|
| 10936 | len: nla_len(nla: attr)); | 
|---|
| 10937 | i++; | 
|---|
| 10938 | } | 
|---|
| 10939 | } | 
|---|
| 10940 |  | 
|---|
| 10941 | i = 0; | 
|---|
| 10942 | if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) { | 
|---|
| 10943 | nla_for_each_nested(attr, | 
|---|
| 10944 | attrs[NL80211_ATTR_SCHED_SCAN_MATCH], | 
|---|
| 10945 | tmp) { | 
|---|
| 10946 | struct nlattr *ssid, *bssid, *; | 
|---|
| 10947 |  | 
|---|
| 10948 | err = nla_parse_nested_deprecated(tb, | 
|---|
| 10949 | maxtype: NL80211_SCHED_SCAN_MATCH_ATTR_MAX, | 
|---|
| 10950 | nla: attr, | 
|---|
| 10951 | policy: nl80211_match_policy, | 
|---|
| 10952 | NULL); | 
|---|
| 10953 | if (err) | 
|---|
| 10954 | goto out_free; | 
|---|
| 10955 | ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]; | 
|---|
| 10956 | bssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_BSSID]; | 
|---|
| 10957 |  | 
|---|
| 10958 | if (!ssid && !bssid) { | 
|---|
| 10959 | i++; | 
|---|
| 10960 | continue; | 
|---|
| 10961 | } | 
|---|
| 10962 |  | 
|---|
| 10963 | if (WARN_ON(i >= n_match_sets)) { | 
|---|
| 10964 | /* this indicates a programming error, | 
|---|
| 10965 | * the loop above should have verified | 
|---|
| 10966 | * things properly | 
|---|
| 10967 | */ | 
|---|
| 10968 | err = -EINVAL; | 
|---|
| 10969 | goto out_free; | 
|---|
| 10970 | } | 
|---|
| 10971 |  | 
|---|
| 10972 | if (ssid) { | 
|---|
| 10973 | memcpy(to: request->match_sets[i].ssid.ssid, | 
|---|
| 10974 | from: nla_data(nla: ssid), len: nla_len(nla: ssid)); | 
|---|
| 10975 | request->match_sets[i].ssid.ssid_len = | 
|---|
| 10976 | nla_len(nla: ssid); | 
|---|
| 10977 | } | 
|---|
| 10978 | if (bssid) | 
|---|
| 10979 | memcpy(to: request->match_sets[i].bssid, | 
|---|
| 10980 | from: nla_data(nla: bssid), ETH_ALEN); | 
|---|
| 10981 |  | 
|---|
| 10982 | /* special attribute - old implementation w/a */ | 
|---|
| 10983 | request->match_sets[i].rssi_thold = default_match_rssi; | 
|---|
| 10984 | rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI]; | 
|---|
| 10985 | if (rssi) | 
|---|
| 10986 | request->match_sets[i].rssi_thold = | 
|---|
| 10987 | nla_get_s32(nla: rssi); | 
|---|
| 10988 | i++; | 
|---|
| 10989 | } | 
|---|
| 10990 |  | 
|---|
| 10991 | /* there was no other matchset, so the RSSI one is alone */ | 
|---|
| 10992 | if (i == 0 && n_match_sets) | 
|---|
| 10993 | request->match_sets[0].rssi_thold = default_match_rssi; | 
|---|
| 10994 |  | 
|---|
| 10995 | request->min_rssi_thold = INT_MAX; | 
|---|
| 10996 | for (i = 0; i < n_match_sets; i++) | 
|---|
| 10997 | request->min_rssi_thold = | 
|---|
| 10998 | min(request->match_sets[i].rssi_thold, | 
|---|
| 10999 | request->min_rssi_thold); | 
|---|
| 11000 | } else { | 
|---|
| 11001 | request->min_rssi_thold = NL80211_SCAN_RSSI_THOLD_OFF; | 
|---|
| 11002 | } | 
|---|
| 11003 |  | 
|---|
| 11004 | if (ie_len) { | 
|---|
| 11005 | request->ie_len = ie_len; | 
|---|
| 11006 | memcpy(to: (void *)request->ie, | 
|---|
| 11007 | from: nla_data(nla: attrs[NL80211_ATTR_IE]), | 
|---|
| 11008 | len: request->ie_len); | 
|---|
| 11009 | } | 
|---|
| 11010 |  | 
|---|
| 11011 | err = nl80211_check_scan_flags_sched(wiphy, wdev, attrs, req: request); | 
|---|
| 11012 | if (err) | 
|---|
| 11013 | goto out_free; | 
|---|
| 11014 |  | 
|---|
| 11015 | if (attrs[NL80211_ATTR_SCHED_SCAN_DELAY]) | 
|---|
| 11016 | request->delay = | 
|---|
| 11017 | nla_get_u32(nla: attrs[NL80211_ATTR_SCHED_SCAN_DELAY]); | 
|---|
| 11018 |  | 
|---|
| 11019 | if (attrs[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI]) { | 
|---|
| 11020 | request->relative_rssi = nla_get_s8( | 
|---|
| 11021 | nla: attrs[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI]); | 
|---|
| 11022 | request->relative_rssi_set = true; | 
|---|
| 11023 | } | 
|---|
| 11024 |  | 
|---|
| 11025 | if (request->relative_rssi_set && | 
|---|
| 11026 | attrs[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST]) { | 
|---|
| 11027 | struct nl80211_bss_select_rssi_adjust *; | 
|---|
| 11028 |  | 
|---|
| 11029 | rssi_adjust = nla_data( | 
|---|
| 11030 | nla: attrs[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST]); | 
|---|
| 11031 | request->rssi_adjust.band = rssi_adjust->band; | 
|---|
| 11032 | request->rssi_adjust.delta = rssi_adjust->delta; | 
|---|
| 11033 | if (!is_band_valid(wiphy, b: request->rssi_adjust.band)) { | 
|---|
| 11034 | err = -EINVAL; | 
|---|
| 11035 | goto out_free; | 
|---|
| 11036 | } | 
|---|
| 11037 | } | 
|---|
| 11038 |  | 
|---|
| 11039 | err = nl80211_parse_sched_scan_plans(wiphy, n_plans, request, attrs); | 
|---|
| 11040 | if (err) | 
|---|
| 11041 | goto out_free; | 
|---|
| 11042 |  | 
|---|
| 11043 | request->scan_start = jiffies; | 
|---|
| 11044 |  | 
|---|
| 11045 | return request; | 
|---|
| 11046 |  | 
|---|
| 11047 | out_free: | 
|---|
| 11048 | kfree(objp: request); | 
|---|
| 11049 | return ERR_PTR(error: err); | 
|---|
| 11050 | } | 
|---|
| 11051 |  | 
|---|
| 11052 | static int nl80211_start_sched_scan(struct sk_buff *skb, | 
|---|
| 11053 | struct genl_info *info) | 
|---|
| 11054 | { | 
|---|
| 11055 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 11056 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 11057 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 11058 | struct cfg80211_sched_scan_request *sched_scan_req; | 
|---|
| 11059 | bool want_multi; | 
|---|
| 11060 | int err; | 
|---|
| 11061 |  | 
|---|
| 11062 | if (!rdev->wiphy.max_sched_scan_reqs || !rdev->ops->sched_scan_start) | 
|---|
| 11063 | return -EOPNOTSUPP; | 
|---|
| 11064 |  | 
|---|
| 11065 | want_multi = info->attrs[NL80211_ATTR_SCHED_SCAN_MULTI]; | 
|---|
| 11066 | err = cfg80211_sched_scan_req_possible(rdev, want_multi); | 
|---|
| 11067 | if (err) | 
|---|
| 11068 | return err; | 
|---|
| 11069 |  | 
|---|
| 11070 | sched_scan_req = nl80211_parse_sched_scan(wiphy: &rdev->wiphy, wdev, | 
|---|
| 11071 | attrs: info->attrs, | 
|---|
| 11072 | max_match_sets: rdev->wiphy.max_match_sets); | 
|---|
| 11073 |  | 
|---|
| 11074 | err = PTR_ERR_OR_ZERO(ptr: sched_scan_req); | 
|---|
| 11075 | if (err) | 
|---|
| 11076 | goto out_err; | 
|---|
| 11077 |  | 
|---|
| 11078 | /* leave request id zero for legacy request | 
|---|
| 11079 | * or if driver does not support multi-scheduled scan | 
|---|
| 11080 | */ | 
|---|
| 11081 | if (want_multi && rdev->wiphy.max_sched_scan_reqs > 1) | 
|---|
| 11082 | sched_scan_req->reqid = cfg80211_assign_cookie(rdev); | 
|---|
| 11083 |  | 
|---|
| 11084 | err = rdev_sched_scan_start(rdev, dev, request: sched_scan_req); | 
|---|
| 11085 | if (err) | 
|---|
| 11086 | goto out_free; | 
|---|
| 11087 |  | 
|---|
| 11088 | sched_scan_req->dev = dev; | 
|---|
| 11089 | sched_scan_req->wiphy = &rdev->wiphy; | 
|---|
| 11090 |  | 
|---|
| 11091 | if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) | 
|---|
| 11092 | sched_scan_req->owner_nlportid = info->snd_portid; | 
|---|
| 11093 |  | 
|---|
| 11094 | cfg80211_add_sched_scan_req(rdev, req: sched_scan_req); | 
|---|
| 11095 |  | 
|---|
| 11096 | nl80211_send_sched_scan(req: sched_scan_req, cmd: NL80211_CMD_START_SCHED_SCAN); | 
|---|
| 11097 | return 0; | 
|---|
| 11098 |  | 
|---|
| 11099 | out_free: | 
|---|
| 11100 | kfree(objp: sched_scan_req); | 
|---|
| 11101 | out_err: | 
|---|
| 11102 | return err; | 
|---|
| 11103 | } | 
|---|
| 11104 |  | 
|---|
| 11105 | static int nl80211_stop_sched_scan(struct sk_buff *skb, | 
|---|
| 11106 | struct genl_info *info) | 
|---|
| 11107 | { | 
|---|
| 11108 | struct cfg80211_sched_scan_request *req; | 
|---|
| 11109 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 11110 | u64 cookie; | 
|---|
| 11111 |  | 
|---|
| 11112 | if (!rdev->wiphy.max_sched_scan_reqs || !rdev->ops->sched_scan_stop) | 
|---|
| 11113 | return -EOPNOTSUPP; | 
|---|
| 11114 |  | 
|---|
| 11115 | if (info->attrs[NL80211_ATTR_COOKIE]) { | 
|---|
| 11116 | cookie = nla_get_u64(nla: info->attrs[NL80211_ATTR_COOKIE]); | 
|---|
| 11117 | return __cfg80211_stop_sched_scan(rdev, reqid: cookie, driver_initiated: false); | 
|---|
| 11118 | } | 
|---|
| 11119 |  | 
|---|
| 11120 | req = list_first_or_null_rcu(&rdev->sched_scan_req_list, | 
|---|
| 11121 | struct cfg80211_sched_scan_request, | 
|---|
| 11122 | list); | 
|---|
| 11123 | if (!req || req->reqid || | 
|---|
| 11124 | (req->owner_nlportid && | 
|---|
| 11125 | req->owner_nlportid != info->snd_portid)) | 
|---|
| 11126 | return -ENOENT; | 
|---|
| 11127 |  | 
|---|
| 11128 | return cfg80211_stop_sched_scan_req(rdev, req, driver_initiated: false); | 
|---|
| 11129 | } | 
|---|
| 11130 |  | 
|---|
| 11131 | static int nl80211_start_radar_detection(struct sk_buff *skb, | 
|---|
| 11132 | struct genl_info *info) | 
|---|
| 11133 | { | 
|---|
| 11134 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 11135 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 11136 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 11137 | int link_id = nl80211_link_id(attrs: info->attrs); | 
|---|
| 11138 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 11139 | struct cfg80211_chan_def chandef; | 
|---|
| 11140 | enum nl80211_dfs_regions dfs_region; | 
|---|
| 11141 | unsigned int cac_time_ms; | 
|---|
| 11142 | int err; | 
|---|
| 11143 |  | 
|---|
| 11144 | flush_delayed_work(dwork: &rdev->dfs_update_channels_wk); | 
|---|
| 11145 |  | 
|---|
| 11146 | switch (wdev->iftype) { | 
|---|
| 11147 | case NL80211_IFTYPE_AP: | 
|---|
| 11148 | case NL80211_IFTYPE_P2P_GO: | 
|---|
| 11149 | case NL80211_IFTYPE_MESH_POINT: | 
|---|
| 11150 | case NL80211_IFTYPE_ADHOC: | 
|---|
| 11151 | break; | 
|---|
| 11152 | default: | 
|---|
| 11153 | /* caution - see cfg80211_beaconing_iface_active() below */ | 
|---|
| 11154 | return -EINVAL; | 
|---|
| 11155 | } | 
|---|
| 11156 |  | 
|---|
| 11157 | guard(wiphy)(T: wiphy); | 
|---|
| 11158 |  | 
|---|
| 11159 | dfs_region = reg_get_dfs_region(wiphy); | 
|---|
| 11160 | if (dfs_region == NL80211_DFS_UNSET) | 
|---|
| 11161 | return -EINVAL; | 
|---|
| 11162 |  | 
|---|
| 11163 | err = nl80211_parse_chandef(rdev, info, chandef: &chandef); | 
|---|
| 11164 | if (err) | 
|---|
| 11165 | return err; | 
|---|
| 11166 |  | 
|---|
| 11167 | err = cfg80211_chandef_dfs_required(wiphy, chandef: &chandef, iftype: wdev->iftype); | 
|---|
| 11168 | if (err < 0) | 
|---|
| 11169 | return err; | 
|---|
| 11170 |  | 
|---|
| 11171 | if (err == 0) | 
|---|
| 11172 | return -EINVAL; | 
|---|
| 11173 |  | 
|---|
| 11174 | if (!cfg80211_chandef_dfs_usable(wiphy, chandef: &chandef)) | 
|---|
| 11175 | return -EINVAL; | 
|---|
| 11176 |  | 
|---|
| 11177 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_RADAR_BACKGROUND])) | 
|---|
| 11178 | return cfg80211_start_background_radar_detection(rdev, wdev, | 
|---|
| 11179 | chandef: &chandef); | 
|---|
| 11180 |  | 
|---|
| 11181 | if (cfg80211_beaconing_iface_active(wdev)) { | 
|---|
| 11182 | /* During MLO other link(s) can beacon, only the current link | 
|---|
| 11183 | * can not already beacon | 
|---|
| 11184 | */ | 
|---|
| 11185 | if (wdev->valid_links && | 
|---|
| 11186 | !wdev->links[link_id].ap.beacon_interval) { | 
|---|
| 11187 | /* nothing */ | 
|---|
| 11188 | } else { | 
|---|
| 11189 | return -EBUSY; | 
|---|
| 11190 | } | 
|---|
| 11191 | } | 
|---|
| 11192 |  | 
|---|
| 11193 | if (wdev->links[link_id].cac_started) | 
|---|
| 11194 | return -EBUSY; | 
|---|
| 11195 |  | 
|---|
| 11196 | /* CAC start is offloaded to HW and can't be started manually */ | 
|---|
| 11197 | if (wiphy_ext_feature_isset(wiphy, ftidx: NL80211_EXT_FEATURE_DFS_OFFLOAD)) | 
|---|
| 11198 | return -EOPNOTSUPP; | 
|---|
| 11199 |  | 
|---|
| 11200 | if (!rdev->ops->start_radar_detection) | 
|---|
| 11201 | return -EOPNOTSUPP; | 
|---|
| 11202 |  | 
|---|
| 11203 | cac_time_ms = cfg80211_chandef_dfs_cac_time(wiphy: &rdev->wiphy, chandef: &chandef); | 
|---|
| 11204 | if (WARN_ON(!cac_time_ms)) | 
|---|
| 11205 | cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; | 
|---|
| 11206 |  | 
|---|
| 11207 | err = rdev_start_radar_detection(rdev, dev, chandef: &chandef, cac_time_ms, | 
|---|
| 11208 | link_id); | 
|---|
| 11209 | if (err) | 
|---|
| 11210 | return err; | 
|---|
| 11211 |  | 
|---|
| 11212 | switch (wdev->iftype) { | 
|---|
| 11213 | case NL80211_IFTYPE_AP: | 
|---|
| 11214 | case NL80211_IFTYPE_P2P_GO: | 
|---|
| 11215 | wdev->links[link_id].ap.chandef = chandef; | 
|---|
| 11216 | break; | 
|---|
| 11217 | case NL80211_IFTYPE_ADHOC: | 
|---|
| 11218 | wdev->u.ibss.chandef = chandef; | 
|---|
| 11219 | break; | 
|---|
| 11220 | case NL80211_IFTYPE_MESH_POINT: | 
|---|
| 11221 | wdev->u.mesh.chandef = chandef; | 
|---|
| 11222 | break; | 
|---|
| 11223 | default: | 
|---|
| 11224 | break; | 
|---|
| 11225 | } | 
|---|
| 11226 | wdev->links[link_id].cac_started = true; | 
|---|
| 11227 | wdev->links[link_id].cac_start_time = jiffies; | 
|---|
| 11228 | wdev->links[link_id].cac_time_ms = cac_time_ms; | 
|---|
| 11229 |  | 
|---|
| 11230 | return 0; | 
|---|
| 11231 | } | 
|---|
| 11232 |  | 
|---|
| 11233 | static int nl80211_notify_radar_detection(struct sk_buff *skb, | 
|---|
| 11234 | struct genl_info *info) | 
|---|
| 11235 | { | 
|---|
| 11236 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 11237 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 11238 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 11239 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 11240 | struct cfg80211_chan_def chandef; | 
|---|
| 11241 | enum nl80211_dfs_regions dfs_region; | 
|---|
| 11242 | int err; | 
|---|
| 11243 |  | 
|---|
| 11244 | dfs_region = reg_get_dfs_region(wiphy); | 
|---|
| 11245 | if (dfs_region == NL80211_DFS_UNSET) { | 
|---|
| 11246 | GENL_SET_ERR_MSG(info, | 
|---|
| 11247 | "DFS Region is not set. Unexpected Radar indication"); | 
|---|
| 11248 | return -EINVAL; | 
|---|
| 11249 | } | 
|---|
| 11250 |  | 
|---|
| 11251 | err = nl80211_parse_chandef(rdev, info, chandef: &chandef); | 
|---|
| 11252 | if (err) { | 
|---|
| 11253 | GENL_SET_ERR_MSG(info, "Unable to extract chandef info"); | 
|---|
| 11254 | return err; | 
|---|
| 11255 | } | 
|---|
| 11256 |  | 
|---|
| 11257 | err = cfg80211_chandef_dfs_required(wiphy, chandef: &chandef, iftype: wdev->iftype); | 
|---|
| 11258 | if (err < 0) { | 
|---|
| 11259 | GENL_SET_ERR_MSG(info, "chandef is invalid"); | 
|---|
| 11260 | return err; | 
|---|
| 11261 | } | 
|---|
| 11262 |  | 
|---|
| 11263 | if (err == 0) { | 
|---|
| 11264 | GENL_SET_ERR_MSG(info, | 
|---|
| 11265 | "Unexpected Radar indication for chandef/iftype"); | 
|---|
| 11266 | return -EINVAL; | 
|---|
| 11267 | } | 
|---|
| 11268 |  | 
|---|
| 11269 | /* Do not process this notification if radar is already detected | 
|---|
| 11270 | * by kernel on this channel, and return success. | 
|---|
| 11271 | */ | 
|---|
| 11272 | if (chandef.chan->dfs_state == NL80211_DFS_UNAVAILABLE) | 
|---|
| 11273 | return 0; | 
|---|
| 11274 |  | 
|---|
| 11275 | cfg80211_set_dfs_state(wiphy, chandef: &chandef, dfs_state: NL80211_DFS_UNAVAILABLE); | 
|---|
| 11276 |  | 
|---|
| 11277 | cfg80211_sched_dfs_chan_update(rdev); | 
|---|
| 11278 |  | 
|---|
| 11279 | rdev->radar_chandef = chandef; | 
|---|
| 11280 |  | 
|---|
| 11281 | /* Propagate this notification to other radios as well */ | 
|---|
| 11282 | queue_work(wq: cfg80211_wq, work: &rdev->propagate_radar_detect_wk); | 
|---|
| 11283 |  | 
|---|
| 11284 | return 0; | 
|---|
| 11285 | } | 
|---|
| 11286 |  | 
|---|
| 11287 | static int nl80211_parse_counter_offsets(struct cfg80211_registered_device *rdev, | 
|---|
| 11288 | const u8 *data, size_t datalen, | 
|---|
| 11289 | int first_count, struct nlattr *attr, | 
|---|
| 11290 | const u16 **offsets, unsigned int *n_offsets) | 
|---|
| 11291 | { | 
|---|
| 11292 | int i; | 
|---|
| 11293 |  | 
|---|
| 11294 | *n_offsets = 0; | 
|---|
| 11295 |  | 
|---|
| 11296 | if (!attr) | 
|---|
| 11297 | return 0; | 
|---|
| 11298 |  | 
|---|
| 11299 | if (!nla_len(nla: attr) || (nla_len(nla: attr) % sizeof(u16))) | 
|---|
| 11300 | return -EINVAL; | 
|---|
| 11301 |  | 
|---|
| 11302 | *n_offsets = nla_len(nla: attr) / sizeof(u16); | 
|---|
| 11303 | if (rdev->wiphy.max_num_csa_counters && | 
|---|
| 11304 | (*n_offsets > rdev->wiphy.max_num_csa_counters)) | 
|---|
| 11305 | return -EINVAL; | 
|---|
| 11306 |  | 
|---|
| 11307 | *offsets = nla_data(nla: attr); | 
|---|
| 11308 |  | 
|---|
| 11309 | /* sanity checks - counters should fit and be the same */ | 
|---|
| 11310 | for (i = 0; i < *n_offsets; i++) { | 
|---|
| 11311 | u16 offset = (*offsets)[i]; | 
|---|
| 11312 |  | 
|---|
| 11313 | if (offset >= datalen) | 
|---|
| 11314 | return -EINVAL; | 
|---|
| 11315 |  | 
|---|
| 11316 | if (first_count != -1 && data[offset] != first_count) | 
|---|
| 11317 | return -EINVAL; | 
|---|
| 11318 | } | 
|---|
| 11319 |  | 
|---|
| 11320 | return 0; | 
|---|
| 11321 | } | 
|---|
| 11322 |  | 
|---|
| 11323 | static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 11324 | { | 
|---|
| 11325 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 11326 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); | 
|---|
| 11327 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 11328 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 11329 | struct cfg80211_csa_settings params; | 
|---|
| 11330 | struct nlattr **csa_attrs = NULL; | 
|---|
| 11331 | int err; | 
|---|
| 11332 | bool need_new_beacon = false; | 
|---|
| 11333 | bool need_handle_dfs_flag = true; | 
|---|
| 11334 | u32 cs_count; | 
|---|
| 11335 |  | 
|---|
| 11336 | if (!rdev->ops->channel_switch || | 
|---|
| 11337 | !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)) | 
|---|
| 11338 | return -EOPNOTSUPP; | 
|---|
| 11339 |  | 
|---|
| 11340 | switch (dev->ieee80211_ptr->iftype) { | 
|---|
| 11341 | case NL80211_IFTYPE_AP: | 
|---|
| 11342 | case NL80211_IFTYPE_P2P_GO: | 
|---|
| 11343 | need_new_beacon = true; | 
|---|
| 11344 | /* For all modes except AP the handle_dfs flag needs to be | 
|---|
| 11345 | * supplied to tell the kernel that userspace will handle radar | 
|---|
| 11346 | * events when they happen. Otherwise a switch to a channel | 
|---|
| 11347 | * requiring DFS will be rejected. | 
|---|
| 11348 | */ | 
|---|
| 11349 | need_handle_dfs_flag = false; | 
|---|
| 11350 |  | 
|---|
| 11351 | /* useless if AP is not running */ | 
|---|
| 11352 | if (!wdev->links[link_id].ap.beacon_interval) | 
|---|
| 11353 | return -ENOTCONN; | 
|---|
| 11354 | break; | 
|---|
| 11355 | case NL80211_IFTYPE_ADHOC: | 
|---|
| 11356 | if (!wdev->u.ibss.ssid_len) | 
|---|
| 11357 | return -ENOTCONN; | 
|---|
| 11358 | break; | 
|---|
| 11359 | case NL80211_IFTYPE_MESH_POINT: | 
|---|
| 11360 | if (!wdev->u.mesh.id_len) | 
|---|
| 11361 | return -ENOTCONN; | 
|---|
| 11362 | break; | 
|---|
| 11363 | default: | 
|---|
| 11364 | return -EOPNOTSUPP; | 
|---|
| 11365 | } | 
|---|
| 11366 |  | 
|---|
| 11367 | memset(s: ¶ms, c: 0, n: sizeof(params)); | 
|---|
| 11368 | params.beacon_csa.ftm_responder = -1; | 
|---|
| 11369 |  | 
|---|
| 11370 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || | 
|---|
| 11371 | !info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]) | 
|---|
| 11372 | return -EINVAL; | 
|---|
| 11373 |  | 
|---|
| 11374 | /* only important for AP, IBSS and mesh create IEs internally */ | 
|---|
| 11375 | if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES]) | 
|---|
| 11376 | return -EINVAL; | 
|---|
| 11377 |  | 
|---|
| 11378 | /* Even though the attribute is u32, the specification says | 
|---|
| 11379 | * u8, so let's make sure we don't overflow. | 
|---|
| 11380 | */ | 
|---|
| 11381 | cs_count = nla_get_u32(nla: info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]); | 
|---|
| 11382 | if (cs_count > 255) | 
|---|
| 11383 | return -EINVAL; | 
|---|
| 11384 |  | 
|---|
| 11385 | params.count = cs_count; | 
|---|
| 11386 |  | 
|---|
| 11387 | if (!need_new_beacon) | 
|---|
| 11388 | goto skip_beacons; | 
|---|
| 11389 |  | 
|---|
| 11390 | err = nl80211_parse_beacon(rdev, attrs: info->attrs, bcn: ¶ms.beacon_after, | 
|---|
| 11391 | extack: info->extack); | 
|---|
| 11392 | if (err) | 
|---|
| 11393 | goto free; | 
|---|
| 11394 |  | 
|---|
| 11395 | csa_attrs = kcalloc(NL80211_ATTR_MAX + 1, sizeof(*csa_attrs), | 
|---|
| 11396 | GFP_KERNEL); | 
|---|
| 11397 | if (!csa_attrs) { | 
|---|
| 11398 | err = -ENOMEM; | 
|---|
| 11399 | goto free; | 
|---|
| 11400 | } | 
|---|
| 11401 |  | 
|---|
| 11402 | err = nla_parse_nested_deprecated(tb: csa_attrs, maxtype: NL80211_ATTR_MAX, | 
|---|
| 11403 | nla: info->attrs[NL80211_ATTR_CSA_IES], | 
|---|
| 11404 | policy: nl80211_policy, extack: info->extack); | 
|---|
| 11405 | if (err) | 
|---|
| 11406 | goto free; | 
|---|
| 11407 |  | 
|---|
| 11408 | err = nl80211_parse_beacon(rdev, attrs: csa_attrs, bcn: ¶ms.beacon_csa, | 
|---|
| 11409 | extack: info->extack); | 
|---|
| 11410 | if (err) | 
|---|
| 11411 | goto free; | 
|---|
| 11412 |  | 
|---|
| 11413 | if (!csa_attrs[NL80211_ATTR_CNTDWN_OFFS_BEACON]) { | 
|---|
| 11414 | err = -EINVAL; | 
|---|
| 11415 | goto free; | 
|---|
| 11416 | } | 
|---|
| 11417 |  | 
|---|
| 11418 | err = nl80211_parse_counter_offsets(rdev, data: params.beacon_csa.tail, | 
|---|
| 11419 | datalen: params.beacon_csa.tail_len, | 
|---|
| 11420 | first_count: params.count, | 
|---|
| 11421 | attr: csa_attrs[NL80211_ATTR_CNTDWN_OFFS_BEACON], | 
|---|
| 11422 | offsets: ¶ms.counter_offsets_beacon, | 
|---|
| 11423 | n_offsets: ¶ms.n_counter_offsets_beacon); | 
|---|
| 11424 | if (err) | 
|---|
| 11425 | goto free; | 
|---|
| 11426 |  | 
|---|
| 11427 | err = nl80211_parse_counter_offsets(rdev, data: params.beacon_csa.probe_resp, | 
|---|
| 11428 | datalen: params.beacon_csa.probe_resp_len, | 
|---|
| 11429 | first_count: params.count, | 
|---|
| 11430 | attr: csa_attrs[NL80211_ATTR_CNTDWN_OFFS_PRESP], | 
|---|
| 11431 | offsets: ¶ms.counter_offsets_presp, | 
|---|
| 11432 | n_offsets: ¶ms.n_counter_offsets_presp); | 
|---|
| 11433 | if (err) | 
|---|
| 11434 | goto free; | 
|---|
| 11435 |  | 
|---|
| 11436 | skip_beacons: | 
|---|
| 11437 | err = nl80211_parse_chandef(rdev, info, chandef: ¶ms.chandef); | 
|---|
| 11438 | if (err) | 
|---|
| 11439 | goto free; | 
|---|
| 11440 |  | 
|---|
| 11441 | if (!cfg80211_reg_can_beacon_relax(wiphy: &rdev->wiphy, chandef: ¶ms.chandef, | 
|---|
| 11442 | iftype: wdev->iftype)) { | 
|---|
| 11443 | err = -EINVAL; | 
|---|
| 11444 | goto free; | 
|---|
| 11445 | } | 
|---|
| 11446 |  | 
|---|
| 11447 | err = cfg80211_chandef_dfs_required(wiphy: wdev->wiphy, | 
|---|
| 11448 | chandef: ¶ms.chandef, | 
|---|
| 11449 | iftype: wdev->iftype); | 
|---|
| 11450 | if (err < 0) | 
|---|
| 11451 | goto free; | 
|---|
| 11452 |  | 
|---|
| 11453 | if (err > 0) { | 
|---|
| 11454 | params.radar_required = true; | 
|---|
| 11455 | if (need_handle_dfs_flag && | 
|---|
| 11456 | !nla_get_flag(nla: info->attrs[NL80211_ATTR_HANDLE_DFS])) { | 
|---|
| 11457 | err = -EINVAL; | 
|---|
| 11458 | goto free; | 
|---|
| 11459 | } | 
|---|
| 11460 | } | 
|---|
| 11461 |  | 
|---|
| 11462 | if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX]) | 
|---|
| 11463 | params.block_tx = true; | 
|---|
| 11464 |  | 
|---|
| 11465 | if ((wdev->iftype == NL80211_IFTYPE_AP || | 
|---|
| 11466 | wdev->iftype == NL80211_IFTYPE_P2P_GO) && | 
|---|
| 11467 | info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP]) { | 
|---|
| 11468 | err = nl80211_parse_unsol_bcast_probe_resp( | 
|---|
| 11469 | rdev, attrs: info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP], | 
|---|
| 11470 | presp: ¶ms.unsol_bcast_probe_resp); | 
|---|
| 11471 | if (err) | 
|---|
| 11472 | goto free; | 
|---|
| 11473 | } | 
|---|
| 11474 |  | 
|---|
| 11475 | params.link_id = link_id; | 
|---|
| 11476 | err = rdev_channel_switch(rdev, dev, params: ¶ms); | 
|---|
| 11477 |  | 
|---|
| 11478 | free: | 
|---|
| 11479 | kfree(objp: params.beacon_after.mbssid_ies); | 
|---|
| 11480 | kfree(objp: params.beacon_csa.mbssid_ies); | 
|---|
| 11481 | kfree(objp: params.beacon_after.rnr_ies); | 
|---|
| 11482 | kfree(objp: params.beacon_csa.rnr_ies); | 
|---|
| 11483 | kfree(objp: csa_attrs); | 
|---|
| 11484 | return err; | 
|---|
| 11485 | } | 
|---|
| 11486 |  | 
|---|
| 11487 | static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, | 
|---|
| 11488 | u32 seq, int flags, | 
|---|
| 11489 | struct cfg80211_registered_device *rdev, | 
|---|
| 11490 | struct wireless_dev *wdev, | 
|---|
| 11491 | struct cfg80211_internal_bss *intbss) | 
|---|
| 11492 | { | 
|---|
| 11493 | struct cfg80211_bss *res = &intbss->pub; | 
|---|
| 11494 | const struct cfg80211_bss_ies *ies; | 
|---|
| 11495 | unsigned int link_id; | 
|---|
| 11496 | void *hdr; | 
|---|
| 11497 | struct nlattr *bss; | 
|---|
| 11498 |  | 
|---|
| 11499 | lockdep_assert_wiphy(wdev->wiphy); | 
|---|
| 11500 |  | 
|---|
| 11501 | hdr = nl80211hdr_put(skb: msg, NETLINK_CB(cb->skb).portid, seq, flags, | 
|---|
| 11502 | cmd: NL80211_CMD_NEW_SCAN_RESULTS); | 
|---|
| 11503 | if (!hdr) | 
|---|
| 11504 | return -1; | 
|---|
| 11505 |  | 
|---|
| 11506 | genl_dump_check_consistent(cb, user_hdr: hdr); | 
|---|
| 11507 |  | 
|---|
| 11508 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_GENERATION, value: rdev->bss_generation)) | 
|---|
| 11509 | goto nla_put_failure; | 
|---|
| 11510 | if (wdev->netdev && | 
|---|
| 11511 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: wdev->netdev->ifindex)) | 
|---|
| 11512 | goto nla_put_failure; | 
|---|
| 11513 | if (nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), | 
|---|
| 11514 | padattr: NL80211_ATTR_PAD)) | 
|---|
| 11515 | goto nla_put_failure; | 
|---|
| 11516 |  | 
|---|
| 11517 | bss = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_BSS); | 
|---|
| 11518 | if (!bss) | 
|---|
| 11519 | goto nla_put_failure; | 
|---|
| 11520 | if ((!is_zero_ether_addr(addr: res->bssid) && | 
|---|
| 11521 | nla_put(skb: msg, attrtype: NL80211_BSS_BSSID, ETH_ALEN, data: res->bssid))) | 
|---|
| 11522 | goto nla_put_failure; | 
|---|
| 11523 |  | 
|---|
| 11524 | rcu_read_lock(); | 
|---|
| 11525 | /* indicate whether we have probe response data or not */ | 
|---|
| 11526 | if (rcu_access_pointer(res->proberesp_ies) && | 
|---|
| 11527 | nla_put_flag(skb: msg, attrtype: NL80211_BSS_PRESP_DATA)) | 
|---|
| 11528 | goto fail_unlock_rcu; | 
|---|
| 11529 |  | 
|---|
| 11530 | /* this pointer prefers to be pointed to probe response data | 
|---|
| 11531 | * but is always valid | 
|---|
| 11532 | */ | 
|---|
| 11533 | ies = rcu_dereference(res->ies); | 
|---|
| 11534 | if (ies) { | 
|---|
| 11535 | if (nla_put_u64_64bit(skb: msg, attrtype: NL80211_BSS_TSF, value: ies->tsf, | 
|---|
| 11536 | padattr: NL80211_BSS_PAD)) | 
|---|
| 11537 | goto fail_unlock_rcu; | 
|---|
| 11538 | if (ies->len && nla_put(skb: msg, attrtype: NL80211_BSS_INFORMATION_ELEMENTS, | 
|---|
| 11539 | attrlen: ies->len, data: ies->data)) | 
|---|
| 11540 | goto fail_unlock_rcu; | 
|---|
| 11541 | } | 
|---|
| 11542 |  | 
|---|
| 11543 | /* and this pointer is always (unless driver didn't know) beacon data */ | 
|---|
| 11544 | ies = rcu_dereference(res->beacon_ies); | 
|---|
| 11545 | if (ies && ies->from_beacon) { | 
|---|
| 11546 | if (nla_put_u64_64bit(skb: msg, attrtype: NL80211_BSS_BEACON_TSF, value: ies->tsf, | 
|---|
| 11547 | padattr: NL80211_BSS_PAD)) | 
|---|
| 11548 | goto fail_unlock_rcu; | 
|---|
| 11549 | if (ies->len && nla_put(skb: msg, attrtype: NL80211_BSS_BEACON_IES, | 
|---|
| 11550 | attrlen: ies->len, data: ies->data)) | 
|---|
| 11551 | goto fail_unlock_rcu; | 
|---|
| 11552 | } | 
|---|
| 11553 | rcu_read_unlock(); | 
|---|
| 11554 |  | 
|---|
| 11555 | if (res->beacon_interval && | 
|---|
| 11556 | nla_put_u16(skb: msg, attrtype: NL80211_BSS_BEACON_INTERVAL, value: res->beacon_interval)) | 
|---|
| 11557 | goto nla_put_failure; | 
|---|
| 11558 | if (nla_put_u16(skb: msg, attrtype: NL80211_BSS_CAPABILITY, value: res->capability) || | 
|---|
| 11559 | nla_put_u32(skb: msg, attrtype: NL80211_BSS_FREQUENCY, value: res->channel->center_freq) || | 
|---|
| 11560 | nla_put_u32(skb: msg, attrtype: NL80211_BSS_FREQUENCY_OFFSET, | 
|---|
| 11561 | value: res->channel->freq_offset) || | 
|---|
| 11562 | nla_put_u32(skb: msg, attrtype: NL80211_BSS_SEEN_MS_AGO, | 
|---|
| 11563 | value: jiffies_to_msecs(j: jiffies - intbss->ts))) | 
|---|
| 11564 | goto nla_put_failure; | 
|---|
| 11565 |  | 
|---|
| 11566 | if (intbss->parent_tsf && | 
|---|
| 11567 | (nla_put_u64_64bit(skb: msg, attrtype: NL80211_BSS_PARENT_TSF, | 
|---|
| 11568 | value: intbss->parent_tsf, padattr: NL80211_BSS_PAD) || | 
|---|
| 11569 | nla_put(skb: msg, attrtype: NL80211_BSS_PARENT_BSSID, ETH_ALEN, | 
|---|
| 11570 | data: intbss->parent_bssid))) | 
|---|
| 11571 | goto nla_put_failure; | 
|---|
| 11572 |  | 
|---|
| 11573 | if (res->ts_boottime && | 
|---|
| 11574 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_BSS_LAST_SEEN_BOOTTIME, | 
|---|
| 11575 | value: res->ts_boottime, padattr: NL80211_BSS_PAD)) | 
|---|
| 11576 | goto nla_put_failure; | 
|---|
| 11577 |  | 
|---|
| 11578 | if (!nl80211_put_signal(msg, mask: intbss->pub.chains, | 
|---|
| 11579 | signal: intbss->pub.chain_signal, | 
|---|
| 11580 | id: NL80211_BSS_CHAIN_SIGNAL)) | 
|---|
| 11581 | goto nla_put_failure; | 
|---|
| 11582 |  | 
|---|
| 11583 | if (intbss->bss_source != BSS_SOURCE_STA_PROFILE) { | 
|---|
| 11584 | switch (rdev->wiphy.signal_type) { | 
|---|
| 11585 | case CFG80211_SIGNAL_TYPE_MBM: | 
|---|
| 11586 | if (nla_put_u32(skb: msg, attrtype: NL80211_BSS_SIGNAL_MBM, | 
|---|
| 11587 | value: res->signal)) | 
|---|
| 11588 | goto nla_put_failure; | 
|---|
| 11589 | break; | 
|---|
| 11590 | case CFG80211_SIGNAL_TYPE_UNSPEC: | 
|---|
| 11591 | if (nla_put_u8(skb: msg, attrtype: NL80211_BSS_SIGNAL_UNSPEC, | 
|---|
| 11592 | value: res->signal)) | 
|---|
| 11593 | goto nla_put_failure; | 
|---|
| 11594 | break; | 
|---|
| 11595 | default: | 
|---|
| 11596 | break; | 
|---|
| 11597 | } | 
|---|
| 11598 | } | 
|---|
| 11599 |  | 
|---|
| 11600 | switch (wdev->iftype) { | 
|---|
| 11601 | case NL80211_IFTYPE_P2P_CLIENT: | 
|---|
| 11602 | case NL80211_IFTYPE_STATION: | 
|---|
| 11603 | for_each_valid_link(wdev, link_id) { | 
|---|
| 11604 | if (intbss == wdev->links[link_id].client.current_bss && | 
|---|
| 11605 | (nla_put_u32(skb: msg, attrtype: NL80211_BSS_STATUS, | 
|---|
| 11606 | value: NL80211_BSS_STATUS_ASSOCIATED) || | 
|---|
| 11607 | (wdev->valid_links && | 
|---|
| 11608 | (nla_put_u8(skb: msg, attrtype: NL80211_BSS_MLO_LINK_ID, | 
|---|
| 11609 | value: link_id) || | 
|---|
| 11610 | nla_put(skb: msg, attrtype: NL80211_BSS_MLD_ADDR, ETH_ALEN, | 
|---|
| 11611 | data: wdev->u.client.connected_addr))))) | 
|---|
| 11612 | goto nla_put_failure; | 
|---|
| 11613 | } | 
|---|
| 11614 | break; | 
|---|
| 11615 | case NL80211_IFTYPE_ADHOC: | 
|---|
| 11616 | if (intbss == wdev->u.ibss.current_bss && | 
|---|
| 11617 | nla_put_u32(skb: msg, attrtype: NL80211_BSS_STATUS, | 
|---|
| 11618 | value: NL80211_BSS_STATUS_IBSS_JOINED)) | 
|---|
| 11619 | goto nla_put_failure; | 
|---|
| 11620 | break; | 
|---|
| 11621 | default: | 
|---|
| 11622 | break; | 
|---|
| 11623 | } | 
|---|
| 11624 |  | 
|---|
| 11625 | if (nla_put_u32(skb: msg, attrtype: NL80211_BSS_USE_FOR, value: res->use_for)) | 
|---|
| 11626 | goto nla_put_failure; | 
|---|
| 11627 |  | 
|---|
| 11628 | if (res->cannot_use_reasons && | 
|---|
| 11629 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_BSS_CANNOT_USE_REASONS, | 
|---|
| 11630 | value: res->cannot_use_reasons, | 
|---|
| 11631 | padattr: NL80211_BSS_PAD)) | 
|---|
| 11632 | goto nla_put_failure; | 
|---|
| 11633 |  | 
|---|
| 11634 | nla_nest_end(skb: msg, start: bss); | 
|---|
| 11635 |  | 
|---|
| 11636 | genlmsg_end(skb: msg, hdr); | 
|---|
| 11637 | return 0; | 
|---|
| 11638 |  | 
|---|
| 11639 | fail_unlock_rcu: | 
|---|
| 11640 | rcu_read_unlock(); | 
|---|
| 11641 | nla_put_failure: | 
|---|
| 11642 | genlmsg_cancel(skb: msg, hdr); | 
|---|
| 11643 | return -EMSGSIZE; | 
|---|
| 11644 | } | 
|---|
| 11645 |  | 
|---|
| 11646 | static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb) | 
|---|
| 11647 | { | 
|---|
| 11648 | struct cfg80211_registered_device *rdev; | 
|---|
| 11649 | struct cfg80211_internal_bss *scan; | 
|---|
| 11650 | struct wireless_dev *wdev; | 
|---|
| 11651 | struct nlattr **attrbuf; | 
|---|
| 11652 | int start = cb->args[2], idx = 0; | 
|---|
| 11653 | bool dump_include_use_data; | 
|---|
| 11654 | int err; | 
|---|
| 11655 |  | 
|---|
| 11656 | attrbuf = kcalloc(NUM_NL80211_ATTR, sizeof(*attrbuf), GFP_KERNEL); | 
|---|
| 11657 | if (!attrbuf) | 
|---|
| 11658 | return -ENOMEM; | 
|---|
| 11659 |  | 
|---|
| 11660 | err = nl80211_prepare_wdev_dump(cb, rdev: &rdev, wdev: &wdev, attrbuf); | 
|---|
| 11661 | if (err) { | 
|---|
| 11662 | kfree(objp: attrbuf); | 
|---|
| 11663 | return err; | 
|---|
| 11664 | } | 
|---|
| 11665 | /* nl80211_prepare_wdev_dump acquired it in the successful case */ | 
|---|
| 11666 | __acquire(&rdev->wiphy.mtx); | 
|---|
| 11667 |  | 
|---|
| 11668 | dump_include_use_data = | 
|---|
| 11669 | attrbuf[NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA]; | 
|---|
| 11670 | kfree(objp: attrbuf); | 
|---|
| 11671 |  | 
|---|
| 11672 | spin_lock_bh(lock: &rdev->bss_lock); | 
|---|
| 11673 |  | 
|---|
| 11674 | /* | 
|---|
| 11675 | * dump_scan will be called multiple times to break up the scan results | 
|---|
| 11676 | * into multiple messages.  It is unlikely that any more bss-es will be | 
|---|
| 11677 | * expired after the first call, so only call only call this on the | 
|---|
| 11678 | * first dump_scan invocation. | 
|---|
| 11679 | */ | 
|---|
| 11680 | if (start == 0) | 
|---|
| 11681 | cfg80211_bss_expire(rdev); | 
|---|
| 11682 |  | 
|---|
| 11683 | cb->seq = rdev->bss_generation; | 
|---|
| 11684 |  | 
|---|
| 11685 | list_for_each_entry(scan, &rdev->bss_list, list) { | 
|---|
| 11686 | if (++idx <= start) | 
|---|
| 11687 | continue; | 
|---|
| 11688 | if (!dump_include_use_data && | 
|---|
| 11689 | !(scan->pub.use_for & NL80211_BSS_USE_FOR_NORMAL)) | 
|---|
| 11690 | continue; | 
|---|
| 11691 | if (nl80211_send_bss(msg: skb, cb, | 
|---|
| 11692 | seq: cb->nlh->nlmsg_seq, NLM_F_MULTI, | 
|---|
| 11693 | rdev, wdev, intbss: scan) < 0) { | 
|---|
| 11694 | idx--; | 
|---|
| 11695 | break; | 
|---|
| 11696 | } | 
|---|
| 11697 | } | 
|---|
| 11698 |  | 
|---|
| 11699 | spin_unlock_bh(lock: &rdev->bss_lock); | 
|---|
| 11700 |  | 
|---|
| 11701 | cb->args[2] = idx; | 
|---|
| 11702 | wiphy_unlock(wiphy: &rdev->wiphy); | 
|---|
| 11703 |  | 
|---|
| 11704 | return skb->len; | 
|---|
| 11705 | } | 
|---|
| 11706 |  | 
|---|
| 11707 | static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq, | 
|---|
| 11708 | int flags, struct net_device *dev, | 
|---|
| 11709 | bool allow_radio_stats, | 
|---|
| 11710 | struct survey_info *survey) | 
|---|
| 11711 | { | 
|---|
| 11712 | void *hdr; | 
|---|
| 11713 | struct nlattr *infoattr; | 
|---|
| 11714 |  | 
|---|
| 11715 | /* skip radio stats if userspace didn't request them */ | 
|---|
| 11716 | if (!survey->channel && !allow_radio_stats) | 
|---|
| 11717 | return 0; | 
|---|
| 11718 |  | 
|---|
| 11719 | hdr = nl80211hdr_put(skb: msg, portid, seq, flags, | 
|---|
| 11720 | cmd: NL80211_CMD_NEW_SURVEY_RESULTS); | 
|---|
| 11721 | if (!hdr) | 
|---|
| 11722 | return -ENOMEM; | 
|---|
| 11723 |  | 
|---|
| 11724 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex)) | 
|---|
| 11725 | goto nla_put_failure; | 
|---|
| 11726 |  | 
|---|
| 11727 | infoattr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_SURVEY_INFO); | 
|---|
| 11728 | if (!infoattr) | 
|---|
| 11729 | goto nla_put_failure; | 
|---|
| 11730 |  | 
|---|
| 11731 | if (survey->channel && | 
|---|
| 11732 | nla_put_u32(skb: msg, attrtype: NL80211_SURVEY_INFO_FREQUENCY, | 
|---|
| 11733 | value: survey->channel->center_freq)) | 
|---|
| 11734 | goto nla_put_failure; | 
|---|
| 11735 |  | 
|---|
| 11736 | if (survey->channel && survey->channel->freq_offset && | 
|---|
| 11737 | nla_put_u32(skb: msg, attrtype: NL80211_SURVEY_INFO_FREQUENCY_OFFSET, | 
|---|
| 11738 | value: survey->channel->freq_offset)) | 
|---|
| 11739 | goto nla_put_failure; | 
|---|
| 11740 |  | 
|---|
| 11741 | if ((survey->filled & SURVEY_INFO_NOISE_DBM) && | 
|---|
| 11742 | nla_put_u8(skb: msg, attrtype: NL80211_SURVEY_INFO_NOISE, value: survey->noise)) | 
|---|
| 11743 | goto nla_put_failure; | 
|---|
| 11744 | if ((survey->filled & SURVEY_INFO_IN_USE) && | 
|---|
| 11745 | nla_put_flag(skb: msg, attrtype: NL80211_SURVEY_INFO_IN_USE)) | 
|---|
| 11746 | goto nla_put_failure; | 
|---|
| 11747 | if ((survey->filled & SURVEY_INFO_TIME) && | 
|---|
| 11748 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_SURVEY_INFO_TIME, | 
|---|
| 11749 | value: survey->time, padattr: NL80211_SURVEY_INFO_PAD)) | 
|---|
| 11750 | goto nla_put_failure; | 
|---|
| 11751 | if ((survey->filled & SURVEY_INFO_TIME_BUSY) && | 
|---|
| 11752 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_SURVEY_INFO_TIME_BUSY, | 
|---|
| 11753 | value: survey->time_busy, padattr: NL80211_SURVEY_INFO_PAD)) | 
|---|
| 11754 | goto nla_put_failure; | 
|---|
| 11755 | if ((survey->filled & SURVEY_INFO_TIME_EXT_BUSY) && | 
|---|
| 11756 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_SURVEY_INFO_TIME_EXT_BUSY, | 
|---|
| 11757 | value: survey->time_ext_busy, padattr: NL80211_SURVEY_INFO_PAD)) | 
|---|
| 11758 | goto nla_put_failure; | 
|---|
| 11759 | if ((survey->filled & SURVEY_INFO_TIME_RX) && | 
|---|
| 11760 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_SURVEY_INFO_TIME_RX, | 
|---|
| 11761 | value: survey->time_rx, padattr: NL80211_SURVEY_INFO_PAD)) | 
|---|
| 11762 | goto nla_put_failure; | 
|---|
| 11763 | if ((survey->filled & SURVEY_INFO_TIME_TX) && | 
|---|
| 11764 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_SURVEY_INFO_TIME_TX, | 
|---|
| 11765 | value: survey->time_tx, padattr: NL80211_SURVEY_INFO_PAD)) | 
|---|
| 11766 | goto nla_put_failure; | 
|---|
| 11767 | if ((survey->filled & SURVEY_INFO_TIME_SCAN) && | 
|---|
| 11768 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_SURVEY_INFO_TIME_SCAN, | 
|---|
| 11769 | value: survey->time_scan, padattr: NL80211_SURVEY_INFO_PAD)) | 
|---|
| 11770 | goto nla_put_failure; | 
|---|
| 11771 | if ((survey->filled & SURVEY_INFO_TIME_BSS_RX) && | 
|---|
| 11772 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_SURVEY_INFO_TIME_BSS_RX, | 
|---|
| 11773 | value: survey->time_bss_rx, padattr: NL80211_SURVEY_INFO_PAD)) | 
|---|
| 11774 | goto nla_put_failure; | 
|---|
| 11775 |  | 
|---|
| 11776 | nla_nest_end(skb: msg, start: infoattr); | 
|---|
| 11777 |  | 
|---|
| 11778 | genlmsg_end(skb: msg, hdr); | 
|---|
| 11779 | return 0; | 
|---|
| 11780 |  | 
|---|
| 11781 | nla_put_failure: | 
|---|
| 11782 | genlmsg_cancel(skb: msg, hdr); | 
|---|
| 11783 | return -EMSGSIZE; | 
|---|
| 11784 | } | 
|---|
| 11785 |  | 
|---|
| 11786 | static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb) | 
|---|
| 11787 | { | 
|---|
| 11788 | struct nlattr **attrbuf; | 
|---|
| 11789 | struct survey_info survey; | 
|---|
| 11790 | struct cfg80211_registered_device *rdev; | 
|---|
| 11791 | struct wireless_dev *wdev; | 
|---|
| 11792 | int survey_idx = cb->args[2]; | 
|---|
| 11793 | int res; | 
|---|
| 11794 | bool radio_stats; | 
|---|
| 11795 |  | 
|---|
| 11796 | attrbuf = kcalloc(NUM_NL80211_ATTR, sizeof(*attrbuf), GFP_KERNEL); | 
|---|
| 11797 | if (!attrbuf) | 
|---|
| 11798 | return -ENOMEM; | 
|---|
| 11799 |  | 
|---|
| 11800 | res = nl80211_prepare_wdev_dump(cb, rdev: &rdev, wdev: &wdev, attrbuf); | 
|---|
| 11801 | if (res) { | 
|---|
| 11802 | kfree(objp: attrbuf); | 
|---|
| 11803 | return res; | 
|---|
| 11804 | } | 
|---|
| 11805 | /* nl80211_prepare_wdev_dump acquired it in the successful case */ | 
|---|
| 11806 | __acquire(&rdev->wiphy.mtx); | 
|---|
| 11807 |  | 
|---|
| 11808 | /* prepare_wdev_dump parsed the attributes */ | 
|---|
| 11809 | radio_stats = attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS]; | 
|---|
| 11810 |  | 
|---|
| 11811 | if (!wdev->netdev) { | 
|---|
| 11812 | res = -EINVAL; | 
|---|
| 11813 | goto out_err; | 
|---|
| 11814 | } | 
|---|
| 11815 |  | 
|---|
| 11816 | if (!rdev->ops->dump_survey) { | 
|---|
| 11817 | res = -EOPNOTSUPP; | 
|---|
| 11818 | goto out_err; | 
|---|
| 11819 | } | 
|---|
| 11820 |  | 
|---|
| 11821 | while (1) { | 
|---|
| 11822 | res = rdev_dump_survey(rdev, netdev: wdev->netdev, idx: survey_idx, info: &survey); | 
|---|
| 11823 | if (res == -ENOENT) | 
|---|
| 11824 | break; | 
|---|
| 11825 | if (res) | 
|---|
| 11826 | goto out_err; | 
|---|
| 11827 |  | 
|---|
| 11828 | /* don't send disabled channels, but do send non-channel data */ | 
|---|
| 11829 | if (survey.channel && | 
|---|
| 11830 | survey.channel->flags & IEEE80211_CHAN_DISABLED) { | 
|---|
| 11831 | survey_idx++; | 
|---|
| 11832 | continue; | 
|---|
| 11833 | } | 
|---|
| 11834 |  | 
|---|
| 11835 | if (nl80211_send_survey(msg: skb, | 
|---|
| 11836 | NETLINK_CB(cb->skb).portid, | 
|---|
| 11837 | seq: cb->nlh->nlmsg_seq, NLM_F_MULTI, | 
|---|
| 11838 | dev: wdev->netdev, allow_radio_stats: radio_stats, survey: &survey) < 0) | 
|---|
| 11839 | goto out; | 
|---|
| 11840 | survey_idx++; | 
|---|
| 11841 | } | 
|---|
| 11842 |  | 
|---|
| 11843 | out: | 
|---|
| 11844 | cb->args[2] = survey_idx; | 
|---|
| 11845 | res = skb->len; | 
|---|
| 11846 | out_err: | 
|---|
| 11847 | kfree(objp: attrbuf); | 
|---|
| 11848 | wiphy_unlock(wiphy: &rdev->wiphy); | 
|---|
| 11849 | return res; | 
|---|
| 11850 | } | 
|---|
| 11851 |  | 
|---|
| 11852 | static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 11853 | { | 
|---|
| 11854 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 11855 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 11856 | struct ieee80211_channel *chan; | 
|---|
| 11857 | const u8 *bssid, *ssid; | 
|---|
| 11858 | int err, ssid_len; | 
|---|
| 11859 | enum nl80211_auth_type auth_type; | 
|---|
| 11860 | struct key_parse key; | 
|---|
| 11861 | bool local_state_change; | 
|---|
| 11862 | struct cfg80211_auth_request req = {}; | 
|---|
| 11863 | u32 freq; | 
|---|
| 11864 |  | 
|---|
| 11865 | if (!info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 11866 | return -EINVAL; | 
|---|
| 11867 |  | 
|---|
| 11868 | if (!info->attrs[NL80211_ATTR_AUTH_TYPE]) | 
|---|
| 11869 | return -EINVAL; | 
|---|
| 11870 |  | 
|---|
| 11871 | if (!info->attrs[NL80211_ATTR_SSID]) | 
|---|
| 11872 | return -EINVAL; | 
|---|
| 11873 |  | 
|---|
| 11874 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) | 
|---|
| 11875 | return -EINVAL; | 
|---|
| 11876 |  | 
|---|
| 11877 | err = nl80211_parse_key(info, k: &key); | 
|---|
| 11878 | if (err) | 
|---|
| 11879 | return err; | 
|---|
| 11880 |  | 
|---|
| 11881 | if (key.idx >= 0) { | 
|---|
| 11882 | if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP) | 
|---|
| 11883 | return -EINVAL; | 
|---|
| 11884 | if (!key.p.key || !key.p.key_len) | 
|---|
| 11885 | return -EINVAL; | 
|---|
| 11886 | if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 || | 
|---|
| 11887 | key.p.key_len != WLAN_KEY_LEN_WEP40) && | 
|---|
| 11888 | (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 || | 
|---|
| 11889 | key.p.key_len != WLAN_KEY_LEN_WEP104)) | 
|---|
| 11890 | return -EINVAL; | 
|---|
| 11891 | if (key.idx > 3) | 
|---|
| 11892 | return -EINVAL; | 
|---|
| 11893 | } else { | 
|---|
| 11894 | key.p.key_len = 0; | 
|---|
| 11895 | key.p.key = NULL; | 
|---|
| 11896 | } | 
|---|
| 11897 |  | 
|---|
| 11898 | if (key.idx >= 0) { | 
|---|
| 11899 | int i; | 
|---|
| 11900 | bool ok = false; | 
|---|
| 11901 |  | 
|---|
| 11902 | for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) { | 
|---|
| 11903 | if (key.p.cipher == rdev->wiphy.cipher_suites[i]) { | 
|---|
| 11904 | ok = true; | 
|---|
| 11905 | break; | 
|---|
| 11906 | } | 
|---|
| 11907 | } | 
|---|
| 11908 | if (!ok) | 
|---|
| 11909 | return -EINVAL; | 
|---|
| 11910 | } | 
|---|
| 11911 |  | 
|---|
| 11912 | if (!rdev->ops->auth) | 
|---|
| 11913 | return -EOPNOTSUPP; | 
|---|
| 11914 |  | 
|---|
| 11915 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && | 
|---|
| 11916 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) | 
|---|
| 11917 | return -EOPNOTSUPP; | 
|---|
| 11918 |  | 
|---|
| 11919 | bssid = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 11920 | freq = MHZ_TO_KHZ(nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); | 
|---|
| 11921 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]) | 
|---|
| 11922 | freq += | 
|---|
| 11923 | nla_get_u32(nla: info->attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]); | 
|---|
| 11924 |  | 
|---|
| 11925 | chan = nl80211_get_valid_chan(wiphy: &rdev->wiphy, freq); | 
|---|
| 11926 | if (!chan) | 
|---|
| 11927 | return -EINVAL; | 
|---|
| 11928 |  | 
|---|
| 11929 | ssid = nla_data(nla: info->attrs[NL80211_ATTR_SSID]); | 
|---|
| 11930 | ssid_len = nla_len(nla: info->attrs[NL80211_ATTR_SSID]); | 
|---|
| 11931 |  | 
|---|
| 11932 | if (info->attrs[NL80211_ATTR_IE]) { | 
|---|
| 11933 | req.ie = nla_data(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 11934 | req.ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 11935 | } | 
|---|
| 11936 |  | 
|---|
| 11937 | if (info->attrs[NL80211_ATTR_SUPPORTED_SELECTORS]) { | 
|---|
| 11938 | req.supported_selectors = | 
|---|
| 11939 | nla_data(nla: info->attrs[NL80211_ATTR_SUPPORTED_SELECTORS]); | 
|---|
| 11940 | req.supported_selectors_len = | 
|---|
| 11941 | nla_len(nla: info->attrs[NL80211_ATTR_SUPPORTED_SELECTORS]); | 
|---|
| 11942 | } | 
|---|
| 11943 |  | 
|---|
| 11944 | auth_type = nla_get_u32(nla: info->attrs[NL80211_ATTR_AUTH_TYPE]); | 
|---|
| 11945 | if (!nl80211_valid_auth_type(rdev, auth_type, NL80211_CMD_AUTHENTICATE)) | 
|---|
| 11946 | return -EINVAL; | 
|---|
| 11947 |  | 
|---|
| 11948 | if ((auth_type == NL80211_AUTHTYPE_SAE || | 
|---|
| 11949 | auth_type == NL80211_AUTHTYPE_FILS_SK || | 
|---|
| 11950 | auth_type == NL80211_AUTHTYPE_FILS_SK_PFS || | 
|---|
| 11951 | auth_type == NL80211_AUTHTYPE_FILS_PK) && | 
|---|
| 11952 | !info->attrs[NL80211_ATTR_AUTH_DATA]) | 
|---|
| 11953 | return -EINVAL; | 
|---|
| 11954 |  | 
|---|
| 11955 | if (info->attrs[NL80211_ATTR_AUTH_DATA]) { | 
|---|
| 11956 | if (auth_type != NL80211_AUTHTYPE_SAE && | 
|---|
| 11957 | auth_type != NL80211_AUTHTYPE_FILS_SK && | 
|---|
| 11958 | auth_type != NL80211_AUTHTYPE_FILS_SK_PFS && | 
|---|
| 11959 | auth_type != NL80211_AUTHTYPE_FILS_PK) | 
|---|
| 11960 | return -EINVAL; | 
|---|
| 11961 | req.auth_data = nla_data(nla: info->attrs[NL80211_ATTR_AUTH_DATA]); | 
|---|
| 11962 | req.auth_data_len = nla_len(nla: info->attrs[NL80211_ATTR_AUTH_DATA]); | 
|---|
| 11963 | } | 
|---|
| 11964 |  | 
|---|
| 11965 | local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; | 
|---|
| 11966 |  | 
|---|
| 11967 | /* | 
|---|
| 11968 | * Since we no longer track auth state, ignore | 
|---|
| 11969 | * requests to only change local state. | 
|---|
| 11970 | */ | 
|---|
| 11971 | if (local_state_change) | 
|---|
| 11972 | return 0; | 
|---|
| 11973 |  | 
|---|
| 11974 | req.auth_type = auth_type; | 
|---|
| 11975 | req.key = key.p.key; | 
|---|
| 11976 | req.key_len = key.p.key_len; | 
|---|
| 11977 | req.key_idx = key.idx; | 
|---|
| 11978 | req.link_id = nl80211_link_id_or_invalid(attrs: info->attrs); | 
|---|
| 11979 | if (req.link_id >= 0) { | 
|---|
| 11980 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_MLO)) | 
|---|
| 11981 | return -EINVAL; | 
|---|
| 11982 | if (!info->attrs[NL80211_ATTR_MLD_ADDR]) | 
|---|
| 11983 | return -EINVAL; | 
|---|
| 11984 | req.ap_mld_addr = nla_data(nla: info->attrs[NL80211_ATTR_MLD_ADDR]); | 
|---|
| 11985 | if (!is_valid_ether_addr(addr: req.ap_mld_addr)) | 
|---|
| 11986 | return -EINVAL; | 
|---|
| 11987 | } | 
|---|
| 11988 |  | 
|---|
| 11989 | req.bss = cfg80211_get_bss(wiphy: &rdev->wiphy, channel: chan, bssid, ssid, ssid_len, | 
|---|
| 11990 | bss_type: IEEE80211_BSS_TYPE_ESS, | 
|---|
| 11991 | privacy: IEEE80211_PRIVACY_ANY); | 
|---|
| 11992 | if (!req.bss) | 
|---|
| 11993 | return -ENOENT; | 
|---|
| 11994 |  | 
|---|
| 11995 | err = cfg80211_mlme_auth(rdev, dev, req: &req); | 
|---|
| 11996 |  | 
|---|
| 11997 | cfg80211_put_bss(wiphy: &rdev->wiphy, bss: req.bss); | 
|---|
| 11998 |  | 
|---|
| 11999 | return err; | 
|---|
| 12000 | } | 
|---|
| 12001 |  | 
|---|
| 12002 | static int validate_pae_over_nl80211(struct cfg80211_registered_device *rdev, | 
|---|
| 12003 | struct genl_info *info) | 
|---|
| 12004 | { | 
|---|
| 12005 | if (!info->attrs[NL80211_ATTR_SOCKET_OWNER]) { | 
|---|
| 12006 | GENL_SET_ERR_MSG(info, "SOCKET_OWNER not set"); | 
|---|
| 12007 | return -EINVAL; | 
|---|
| 12008 | } | 
|---|
| 12009 |  | 
|---|
| 12010 | if (!rdev->ops->tx_control_port || | 
|---|
| 12011 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 12012 | ftidx: NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211)) | 
|---|
| 12013 | return -EOPNOTSUPP; | 
|---|
| 12014 |  | 
|---|
| 12015 | return 0; | 
|---|
| 12016 | } | 
|---|
| 12017 |  | 
|---|
| 12018 | static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, | 
|---|
| 12019 | struct genl_info *info, | 
|---|
| 12020 | struct cfg80211_crypto_settings *settings, | 
|---|
| 12021 | int cipher_limit) | 
|---|
| 12022 | { | 
|---|
| 12023 | memset(s: settings, c: 0, n: sizeof(*settings)); | 
|---|
| 12024 |  | 
|---|
| 12025 | settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT]; | 
|---|
| 12026 |  | 
|---|
| 12027 | if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) { | 
|---|
| 12028 | u16 proto; | 
|---|
| 12029 |  | 
|---|
| 12030 | proto = nla_get_u16( | 
|---|
| 12031 | nla: info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]); | 
|---|
| 12032 | settings->control_port_ethertype = cpu_to_be16(proto); | 
|---|
| 12033 | if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) && | 
|---|
| 12034 | proto != ETH_P_PAE) | 
|---|
| 12035 | return -EINVAL; | 
|---|
| 12036 | if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT]) | 
|---|
| 12037 | settings->control_port_no_encrypt = true; | 
|---|
| 12038 | } else | 
|---|
| 12039 | settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE); | 
|---|
| 12040 |  | 
|---|
| 12041 | if (info->attrs[NL80211_ATTR_CONTROL_PORT_OVER_NL80211]) { | 
|---|
| 12042 | int r = validate_pae_over_nl80211(rdev, info); | 
|---|
| 12043 |  | 
|---|
| 12044 | if (r < 0) | 
|---|
| 12045 | return r; | 
|---|
| 12046 |  | 
|---|
| 12047 | settings->control_port_over_nl80211 = true; | 
|---|
| 12048 |  | 
|---|
| 12049 | if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_PREAUTH]) | 
|---|
| 12050 | settings->control_port_no_preauth = true; | 
|---|
| 12051 | } | 
|---|
| 12052 |  | 
|---|
| 12053 | if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) { | 
|---|
| 12054 | void *data; | 
|---|
| 12055 | int len, i; | 
|---|
| 12056 |  | 
|---|
| 12057 | data = nla_data(nla: info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); | 
|---|
| 12058 | len = nla_len(nla: info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); | 
|---|
| 12059 | settings->n_ciphers_pairwise = len / sizeof(u32); | 
|---|
| 12060 |  | 
|---|
| 12061 | if (len % sizeof(u32)) | 
|---|
| 12062 | return -EINVAL; | 
|---|
| 12063 |  | 
|---|
| 12064 | if (settings->n_ciphers_pairwise > cipher_limit) | 
|---|
| 12065 | return -EINVAL; | 
|---|
| 12066 |  | 
|---|
| 12067 | memcpy(to: settings->ciphers_pairwise, from: data, len); | 
|---|
| 12068 |  | 
|---|
| 12069 | for (i = 0; i < settings->n_ciphers_pairwise; i++) | 
|---|
| 12070 | if (!cfg80211_supported_cipher_suite( | 
|---|
| 12071 | wiphy: &rdev->wiphy, | 
|---|
| 12072 | cipher: settings->ciphers_pairwise[i])) | 
|---|
| 12073 | return -EINVAL; | 
|---|
| 12074 | } | 
|---|
| 12075 |  | 
|---|
| 12076 | if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) { | 
|---|
| 12077 | settings->cipher_group = | 
|---|
| 12078 | nla_get_u32(nla: info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]); | 
|---|
| 12079 | if (!cfg80211_supported_cipher_suite(wiphy: &rdev->wiphy, | 
|---|
| 12080 | cipher: settings->cipher_group)) | 
|---|
| 12081 | return -EINVAL; | 
|---|
| 12082 | } | 
|---|
| 12083 |  | 
|---|
| 12084 | if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) | 
|---|
| 12085 | settings->wpa_versions = | 
|---|
| 12086 | nla_get_u32(nla: info->attrs[NL80211_ATTR_WPA_VERSIONS]); | 
|---|
| 12087 |  | 
|---|
| 12088 | if (info->attrs[NL80211_ATTR_AKM_SUITES]) { | 
|---|
| 12089 | void *data; | 
|---|
| 12090 | int len; | 
|---|
| 12091 |  | 
|---|
| 12092 | data = nla_data(nla: info->attrs[NL80211_ATTR_AKM_SUITES]); | 
|---|
| 12093 | len = nla_len(nla: info->attrs[NL80211_ATTR_AKM_SUITES]); | 
|---|
| 12094 | settings->n_akm_suites = len / sizeof(u32); | 
|---|
| 12095 |  | 
|---|
| 12096 | if (len % sizeof(u32)) | 
|---|
| 12097 | return -EINVAL; | 
|---|
| 12098 |  | 
|---|
| 12099 | if (settings->n_akm_suites > rdev->wiphy.max_num_akm_suites) | 
|---|
| 12100 | return -EINVAL; | 
|---|
| 12101 |  | 
|---|
| 12102 | memcpy(to: settings->akm_suites, from: data, len); | 
|---|
| 12103 | } | 
|---|
| 12104 |  | 
|---|
| 12105 | if (info->attrs[NL80211_ATTR_PMK]) { | 
|---|
| 12106 | if (nla_len(nla: info->attrs[NL80211_ATTR_PMK]) != WLAN_PMK_LEN) | 
|---|
| 12107 | return -EINVAL; | 
|---|
| 12108 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 12109 | ftidx: NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK) && | 
|---|
| 12110 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 12111 | ftidx: NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK)) | 
|---|
| 12112 | return -EINVAL; | 
|---|
| 12113 | settings->psk = nla_data(nla: info->attrs[NL80211_ATTR_PMK]); | 
|---|
| 12114 | } | 
|---|
| 12115 |  | 
|---|
| 12116 | if (info->attrs[NL80211_ATTR_SAE_PASSWORD]) { | 
|---|
| 12117 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 12118 | ftidx: NL80211_EXT_FEATURE_SAE_OFFLOAD) && | 
|---|
| 12119 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 12120 | ftidx: NL80211_EXT_FEATURE_SAE_OFFLOAD_AP)) | 
|---|
| 12121 | return -EINVAL; | 
|---|
| 12122 | settings->sae_pwd = | 
|---|
| 12123 | nla_data(nla: info->attrs[NL80211_ATTR_SAE_PASSWORD]); | 
|---|
| 12124 | settings->sae_pwd_len = | 
|---|
| 12125 | nla_len(nla: info->attrs[NL80211_ATTR_SAE_PASSWORD]); | 
|---|
| 12126 | } | 
|---|
| 12127 |  | 
|---|
| 12128 | settings->sae_pwe = | 
|---|
| 12129 | nla_get_u8_default(nla: info->attrs[NL80211_ATTR_SAE_PWE], | 
|---|
| 12130 | defvalue: NL80211_SAE_PWE_UNSPECIFIED); | 
|---|
| 12131 |  | 
|---|
| 12132 | return 0; | 
|---|
| 12133 | } | 
|---|
| 12134 |  | 
|---|
| 12135 | static struct cfg80211_bss *nl80211_assoc_bss(struct cfg80211_registered_device *rdev, | 
|---|
| 12136 | const u8 *ssid, int ssid_len, | 
|---|
| 12137 | struct nlattr **attrs, | 
|---|
| 12138 | int assoc_link_id, int link_id) | 
|---|
| 12139 | { | 
|---|
| 12140 | struct ieee80211_channel *chan; | 
|---|
| 12141 | struct cfg80211_bss *bss; | 
|---|
| 12142 | const u8 *bssid; | 
|---|
| 12143 | u32 freq, use_for = 0; | 
|---|
| 12144 |  | 
|---|
| 12145 | if (!attrs[NL80211_ATTR_MAC] || !attrs[NL80211_ATTR_WIPHY_FREQ]) | 
|---|
| 12146 | return ERR_PTR(error: -EINVAL); | 
|---|
| 12147 |  | 
|---|
| 12148 | bssid = nla_data(nla: attrs[NL80211_ATTR_MAC]); | 
|---|
| 12149 |  | 
|---|
| 12150 | freq = MHZ_TO_KHZ(nla_get_u32(attrs[NL80211_ATTR_WIPHY_FREQ])); | 
|---|
| 12151 | if (attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]) | 
|---|
| 12152 | freq += nla_get_u32(nla: attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]); | 
|---|
| 12153 |  | 
|---|
| 12154 | chan = nl80211_get_valid_chan(wiphy: &rdev->wiphy, freq); | 
|---|
| 12155 | if (!chan) | 
|---|
| 12156 | return ERR_PTR(error: -EINVAL); | 
|---|
| 12157 |  | 
|---|
| 12158 | if (assoc_link_id >= 0) | 
|---|
| 12159 | use_for = NL80211_BSS_USE_FOR_MLD_LINK; | 
|---|
| 12160 | if (assoc_link_id == link_id) | 
|---|
| 12161 | use_for |= NL80211_BSS_USE_FOR_NORMAL; | 
|---|
| 12162 |  | 
|---|
| 12163 | bss = __cfg80211_get_bss(wiphy: &rdev->wiphy, channel: chan, bssid, | 
|---|
| 12164 | ssid, ssid_len, | 
|---|
| 12165 | bss_type: IEEE80211_BSS_TYPE_ESS, | 
|---|
| 12166 | privacy: IEEE80211_PRIVACY_ANY, | 
|---|
| 12167 | use_for); | 
|---|
| 12168 | if (!bss) | 
|---|
| 12169 | return ERR_PTR(error: -ENOENT); | 
|---|
| 12170 |  | 
|---|
| 12171 | return bss; | 
|---|
| 12172 | } | 
|---|
| 12173 |  | 
|---|
| 12174 | static int nl80211_process_links(struct cfg80211_registered_device *rdev, | 
|---|
| 12175 | struct cfg80211_assoc_link *links, | 
|---|
| 12176 | int assoc_link_id, | 
|---|
| 12177 | const u8 *ssid, int ssid_len, | 
|---|
| 12178 | struct genl_info *info) | 
|---|
| 12179 | { | 
|---|
| 12180 | unsigned int attrsize = NUM_NL80211_ATTR * sizeof(struct nlattr *); | 
|---|
| 12181 | struct nlattr **attrs __free(kfree) = kzalloc(attrsize, GFP_KERNEL); | 
|---|
| 12182 | struct nlattr *link; | 
|---|
| 12183 | unsigned int link_id; | 
|---|
| 12184 | int rem, err; | 
|---|
| 12185 |  | 
|---|
| 12186 | if (!attrs) | 
|---|
| 12187 | return -ENOMEM; | 
|---|
| 12188 |  | 
|---|
| 12189 | nla_for_each_nested(link, info->attrs[NL80211_ATTR_MLO_LINKS], rem) { | 
|---|
| 12190 | memset(s: attrs, c: 0, n: attrsize); | 
|---|
| 12191 |  | 
|---|
| 12192 | nla_parse_nested(tb: attrs, maxtype: NL80211_ATTR_MAX, nla: link, NULL, NULL); | 
|---|
| 12193 |  | 
|---|
| 12194 | if (!attrs[NL80211_ATTR_MLO_LINK_ID]) { | 
|---|
| 12195 | NL_SET_BAD_ATTR(info->extack, link); | 
|---|
| 12196 | return -EINVAL; | 
|---|
| 12197 | } | 
|---|
| 12198 |  | 
|---|
| 12199 | link_id = nla_get_u8(nla: attrs[NL80211_ATTR_MLO_LINK_ID]); | 
|---|
| 12200 | /* cannot use the same link ID again */ | 
|---|
| 12201 | if (links[link_id].bss) { | 
|---|
| 12202 | NL_SET_BAD_ATTR(info->extack, link); | 
|---|
| 12203 | return -EINVAL; | 
|---|
| 12204 | } | 
|---|
| 12205 | links[link_id].bss = | 
|---|
| 12206 | nl80211_assoc_bss(rdev, ssid, ssid_len, attrs, | 
|---|
| 12207 | assoc_link_id, link_id); | 
|---|
| 12208 | if (IS_ERR(ptr: links[link_id].bss)) { | 
|---|
| 12209 | err = PTR_ERR(ptr: links[link_id].bss); | 
|---|
| 12210 | links[link_id].bss = NULL; | 
|---|
| 12211 | NL_SET_ERR_MSG_ATTR(info->extack, link, | 
|---|
| 12212 | "Error fetching BSS for link"); | 
|---|
| 12213 | return err; | 
|---|
| 12214 | } | 
|---|
| 12215 |  | 
|---|
| 12216 | if (attrs[NL80211_ATTR_IE]) { | 
|---|
| 12217 | links[link_id].elems = nla_data(nla: attrs[NL80211_ATTR_IE]); | 
|---|
| 12218 | links[link_id].elems_len = | 
|---|
| 12219 | nla_len(nla: attrs[NL80211_ATTR_IE]); | 
|---|
| 12220 |  | 
|---|
| 12221 | if (cfg80211_find_elem(eid: WLAN_EID_FRAGMENT, | 
|---|
| 12222 | ies: links[link_id].elems, | 
|---|
| 12223 | len: links[link_id].elems_len)) { | 
|---|
| 12224 | NL_SET_ERR_MSG_ATTR(info->extack, | 
|---|
| 12225 | attrs[NL80211_ATTR_IE], | 
|---|
| 12226 | "cannot deal with fragmentation"); | 
|---|
| 12227 | return -EINVAL; | 
|---|
| 12228 | } | 
|---|
| 12229 |  | 
|---|
| 12230 | if (cfg80211_find_ext_elem(ext_eid: WLAN_EID_EXT_NON_INHERITANCE, | 
|---|
| 12231 | ies: links[link_id].elems, | 
|---|
| 12232 | len: links[link_id].elems_len)) { | 
|---|
| 12233 | NL_SET_ERR_MSG_ATTR(info->extack, | 
|---|
| 12234 | attrs[NL80211_ATTR_IE], | 
|---|
| 12235 | "cannot deal with non-inheritance"); | 
|---|
| 12236 | return -EINVAL; | 
|---|
| 12237 | } | 
|---|
| 12238 | } | 
|---|
| 12239 |  | 
|---|
| 12240 | links[link_id].disabled = | 
|---|
| 12241 | nla_get_flag(nla: attrs[NL80211_ATTR_MLO_LINK_DISABLED]); | 
|---|
| 12242 | } | 
|---|
| 12243 |  | 
|---|
| 12244 | return 0; | 
|---|
| 12245 | } | 
|---|
| 12246 |  | 
|---|
| 12247 | static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 12248 | { | 
|---|
| 12249 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 12250 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 12251 | struct cfg80211_assoc_request req = {}; | 
|---|
| 12252 | const u8 *ap_addr, *ssid; | 
|---|
| 12253 | unsigned int link_id; | 
|---|
| 12254 | int err, ssid_len; | 
|---|
| 12255 |  | 
|---|
| 12256 | if (dev->ieee80211_ptr->conn_owner_nlportid && | 
|---|
| 12257 | dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid) | 
|---|
| 12258 | return -EPERM; | 
|---|
| 12259 |  | 
|---|
| 12260 | if (!info->attrs[NL80211_ATTR_SSID]) | 
|---|
| 12261 | return -EINVAL; | 
|---|
| 12262 |  | 
|---|
| 12263 | if (!rdev->ops->assoc) | 
|---|
| 12264 | return -EOPNOTSUPP; | 
|---|
| 12265 |  | 
|---|
| 12266 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && | 
|---|
| 12267 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) | 
|---|
| 12268 | return -EOPNOTSUPP; | 
|---|
| 12269 |  | 
|---|
| 12270 | ssid = nla_data(nla: info->attrs[NL80211_ATTR_SSID]); | 
|---|
| 12271 | ssid_len = nla_len(nla: info->attrs[NL80211_ATTR_SSID]); | 
|---|
| 12272 |  | 
|---|
| 12273 | if (info->attrs[NL80211_ATTR_IE]) { | 
|---|
| 12274 | req.ie = nla_data(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 12275 | req.ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 12276 |  | 
|---|
| 12277 | if (cfg80211_find_ext_elem(ext_eid: WLAN_EID_EXT_NON_INHERITANCE, | 
|---|
| 12278 | ies: req.ie, len: req.ie_len)) { | 
|---|
| 12279 | NL_SET_ERR_MSG_ATTR(info->extack, | 
|---|
| 12280 | info->attrs[NL80211_ATTR_IE], | 
|---|
| 12281 | "non-inheritance makes no sense"); | 
|---|
| 12282 | return -EINVAL; | 
|---|
| 12283 | } | 
|---|
| 12284 | } | 
|---|
| 12285 |  | 
|---|
| 12286 | if (info->attrs[NL80211_ATTR_USE_MFP]) { | 
|---|
| 12287 | enum nl80211_mfp mfp = | 
|---|
| 12288 | nla_get_u32(nla: info->attrs[NL80211_ATTR_USE_MFP]); | 
|---|
| 12289 | if (mfp == NL80211_MFP_REQUIRED) | 
|---|
| 12290 | req.use_mfp = true; | 
|---|
| 12291 | else if (mfp != NL80211_MFP_NO) | 
|---|
| 12292 | return -EINVAL; | 
|---|
| 12293 | } | 
|---|
| 12294 |  | 
|---|
| 12295 | if (info->attrs[NL80211_ATTR_PREV_BSSID]) | 
|---|
| 12296 | req.prev_bssid = nla_data(nla: info->attrs[NL80211_ATTR_PREV_BSSID]); | 
|---|
| 12297 |  | 
|---|
| 12298 | if (info->attrs[NL80211_ATTR_SUPPORTED_SELECTORS]) { | 
|---|
| 12299 | req.supported_selectors = | 
|---|
| 12300 | nla_data(nla: info->attrs[NL80211_ATTR_SUPPORTED_SELECTORS]); | 
|---|
| 12301 | req.supported_selectors_len = | 
|---|
| 12302 | nla_len(nla: info->attrs[NL80211_ATTR_SUPPORTED_SELECTORS]); | 
|---|
| 12303 | } | 
|---|
| 12304 |  | 
|---|
| 12305 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_DISABLE_HT])) | 
|---|
| 12306 | req.flags |= ASSOC_REQ_DISABLE_HT; | 
|---|
| 12307 |  | 
|---|
| 12308 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) | 
|---|
| 12309 | memcpy(to: &req.ht_capa_mask, | 
|---|
| 12310 | from: nla_data(nla: info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]), | 
|---|
| 12311 | len: sizeof(req.ht_capa_mask)); | 
|---|
| 12312 |  | 
|---|
| 12313 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) { | 
|---|
| 12314 | if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) | 
|---|
| 12315 | return -EINVAL; | 
|---|
| 12316 | memcpy(to: &req.ht_capa, | 
|---|
| 12317 | from: nla_data(nla: info->attrs[NL80211_ATTR_HT_CAPABILITY]), | 
|---|
| 12318 | len: sizeof(req.ht_capa)); | 
|---|
| 12319 | } | 
|---|
| 12320 |  | 
|---|
| 12321 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_DISABLE_VHT])) | 
|---|
| 12322 | req.flags |= ASSOC_REQ_DISABLE_VHT; | 
|---|
| 12323 |  | 
|---|
| 12324 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_DISABLE_HE])) | 
|---|
| 12325 | req.flags |= ASSOC_REQ_DISABLE_HE; | 
|---|
| 12326 |  | 
|---|
| 12327 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_DISABLE_EHT])) | 
|---|
| 12328 | req.flags |= ASSOC_REQ_DISABLE_EHT; | 
|---|
| 12329 |  | 
|---|
| 12330 | if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) | 
|---|
| 12331 | memcpy(to: &req.vht_capa_mask, | 
|---|
| 12332 | from: nla_data(nla: info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]), | 
|---|
| 12333 | len: sizeof(req.vht_capa_mask)); | 
|---|
| 12334 |  | 
|---|
| 12335 | if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) { | 
|---|
| 12336 | if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) | 
|---|
| 12337 | return -EINVAL; | 
|---|
| 12338 | memcpy(to: &req.vht_capa, | 
|---|
| 12339 | from: nla_data(nla: info->attrs[NL80211_ATTR_VHT_CAPABILITY]), | 
|---|
| 12340 | len: sizeof(req.vht_capa)); | 
|---|
| 12341 | } | 
|---|
| 12342 |  | 
|---|
| 12343 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_USE_RRM])) { | 
|---|
| 12344 | if (!((rdev->wiphy.features & | 
|---|
| 12345 | NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) && | 
|---|
| 12346 | (rdev->wiphy.features & NL80211_FEATURE_QUIET)) && | 
|---|
| 12347 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 12348 | ftidx: NL80211_EXT_FEATURE_RRM)) | 
|---|
| 12349 | return -EINVAL; | 
|---|
| 12350 | req.flags |= ASSOC_REQ_USE_RRM; | 
|---|
| 12351 | } | 
|---|
| 12352 |  | 
|---|
| 12353 | if (info->attrs[NL80211_ATTR_FILS_KEK]) { | 
|---|
| 12354 | req.fils_kek = nla_data(nla: info->attrs[NL80211_ATTR_FILS_KEK]); | 
|---|
| 12355 | req.fils_kek_len = nla_len(nla: info->attrs[NL80211_ATTR_FILS_KEK]); | 
|---|
| 12356 | if (!info->attrs[NL80211_ATTR_FILS_NONCES]) | 
|---|
| 12357 | return -EINVAL; | 
|---|
| 12358 | req.fils_nonces = | 
|---|
| 12359 | nla_data(nla: info->attrs[NL80211_ATTR_FILS_NONCES]); | 
|---|
| 12360 | } | 
|---|
| 12361 |  | 
|---|
| 12362 | if (info->attrs[NL80211_ATTR_S1G_CAPABILITY_MASK]) { | 
|---|
| 12363 | if (!info->attrs[NL80211_ATTR_S1G_CAPABILITY]) | 
|---|
| 12364 | return -EINVAL; | 
|---|
| 12365 | memcpy(to: &req.s1g_capa_mask, | 
|---|
| 12366 | from: nla_data(nla: info->attrs[NL80211_ATTR_S1G_CAPABILITY_MASK]), | 
|---|
| 12367 | len: sizeof(req.s1g_capa_mask)); | 
|---|
| 12368 | } | 
|---|
| 12369 |  | 
|---|
| 12370 | if (info->attrs[NL80211_ATTR_S1G_CAPABILITY]) { | 
|---|
| 12371 | if (!info->attrs[NL80211_ATTR_S1G_CAPABILITY_MASK]) | 
|---|
| 12372 | return -EINVAL; | 
|---|
| 12373 | memcpy(to: &req.s1g_capa, | 
|---|
| 12374 | from: nla_data(nla: info->attrs[NL80211_ATTR_S1G_CAPABILITY]), | 
|---|
| 12375 | len: sizeof(req.s1g_capa)); | 
|---|
| 12376 | } | 
|---|
| 12377 |  | 
|---|
| 12378 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_ASSOC_SPP_AMSDU])) { | 
|---|
| 12379 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 12380 | ftidx: NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT)) { | 
|---|
| 12381 | GENL_SET_ERR_MSG(info, "SPP A-MSDUs not supported"); | 
|---|
| 12382 | return -EINVAL; | 
|---|
| 12383 | } | 
|---|
| 12384 | req.flags |= ASSOC_REQ_SPP_AMSDU; | 
|---|
| 12385 | } | 
|---|
| 12386 |  | 
|---|
| 12387 | req.link_id = nl80211_link_id_or_invalid(attrs: info->attrs); | 
|---|
| 12388 |  | 
|---|
| 12389 | if (info->attrs[NL80211_ATTR_MLO_LINKS]) { | 
|---|
| 12390 | if (req.link_id < 0) | 
|---|
| 12391 | return -EINVAL; | 
|---|
| 12392 |  | 
|---|
| 12393 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_MLO)) | 
|---|
| 12394 | return -EINVAL; | 
|---|
| 12395 |  | 
|---|
| 12396 | if (info->attrs[NL80211_ATTR_MAC] || | 
|---|
| 12397 | info->attrs[NL80211_ATTR_WIPHY_FREQ] || | 
|---|
| 12398 | !info->attrs[NL80211_ATTR_MLD_ADDR]) | 
|---|
| 12399 | return -EINVAL; | 
|---|
| 12400 |  | 
|---|
| 12401 | req.ap_mld_addr = nla_data(nla: info->attrs[NL80211_ATTR_MLD_ADDR]); | 
|---|
| 12402 | ap_addr = req.ap_mld_addr; | 
|---|
| 12403 |  | 
|---|
| 12404 | err = nl80211_process_links(rdev, links: req.links, assoc_link_id: req.link_id, | 
|---|
| 12405 | ssid, ssid_len, info); | 
|---|
| 12406 | if (err) | 
|---|
| 12407 | goto free; | 
|---|
| 12408 |  | 
|---|
| 12409 | if (!req.links[req.link_id].bss) { | 
|---|
| 12410 | err = -EINVAL; | 
|---|
| 12411 | goto free; | 
|---|
| 12412 | } | 
|---|
| 12413 |  | 
|---|
| 12414 | if (req.links[req.link_id].elems_len) { | 
|---|
| 12415 | GENL_SET_ERR_MSG(info, | 
|---|
| 12416 | "cannot have per-link elems on assoc link"); | 
|---|
| 12417 | err = -EINVAL; | 
|---|
| 12418 | goto free; | 
|---|
| 12419 | } | 
|---|
| 12420 |  | 
|---|
| 12421 | if (req.links[req.link_id].disabled) { | 
|---|
| 12422 | GENL_SET_ERR_MSG(info, | 
|---|
| 12423 | "cannot have assoc link disabled"); | 
|---|
| 12424 | err = -EINVAL; | 
|---|
| 12425 | goto free; | 
|---|
| 12426 | } | 
|---|
| 12427 |  | 
|---|
| 12428 | if (info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]) | 
|---|
| 12429 | req.ext_mld_capa_ops = | 
|---|
| 12430 | nla_get_u16(nla: info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]); | 
|---|
| 12431 | } else { | 
|---|
| 12432 | if (req.link_id >= 0) | 
|---|
| 12433 | return -EINVAL; | 
|---|
| 12434 |  | 
|---|
| 12435 | req.bss = nl80211_assoc_bss(rdev, ssid, ssid_len, attrs: info->attrs, | 
|---|
| 12436 | assoc_link_id: -1, link_id: -1); | 
|---|
| 12437 | if (IS_ERR(ptr: req.bss)) | 
|---|
| 12438 | return PTR_ERR(ptr: req.bss); | 
|---|
| 12439 | ap_addr = req.bss->bssid; | 
|---|
| 12440 |  | 
|---|
| 12441 | if (info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]) | 
|---|
| 12442 | return -EINVAL; | 
|---|
| 12443 | } | 
|---|
| 12444 |  | 
|---|
| 12445 | err = nl80211_crypto_settings(rdev, info, settings: &req.crypto, cipher_limit: 1); | 
|---|
| 12446 | if (!err) { | 
|---|
| 12447 | struct nlattr *link; | 
|---|
| 12448 | int rem = 0; | 
|---|
| 12449 |  | 
|---|
| 12450 | err = cfg80211_mlme_assoc(rdev, dev, req: &req, | 
|---|
| 12451 | extack: info->extack); | 
|---|
| 12452 |  | 
|---|
| 12453 | if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) { | 
|---|
| 12454 | dev->ieee80211_ptr->conn_owner_nlportid = | 
|---|
| 12455 | info->snd_portid; | 
|---|
| 12456 | memcpy(to: dev->ieee80211_ptr->disconnect_bssid, | 
|---|
| 12457 | from: ap_addr, ETH_ALEN); | 
|---|
| 12458 | } | 
|---|
| 12459 |  | 
|---|
| 12460 | /* Report error from first problematic link */ | 
|---|
| 12461 | if (info->attrs[NL80211_ATTR_MLO_LINKS]) { | 
|---|
| 12462 | nla_for_each_nested(link, | 
|---|
| 12463 | info->attrs[NL80211_ATTR_MLO_LINKS], | 
|---|
| 12464 | rem) { | 
|---|
| 12465 | struct nlattr *link_id_attr = | 
|---|
| 12466 | nla_find_nested(nla: link, attrtype: NL80211_ATTR_MLO_LINK_ID); | 
|---|
| 12467 |  | 
|---|
| 12468 | if (!link_id_attr) | 
|---|
| 12469 | continue; | 
|---|
| 12470 |  | 
|---|
| 12471 | link_id = nla_get_u8(nla: link_id_attr); | 
|---|
| 12472 |  | 
|---|
| 12473 | if (link_id == req.link_id) | 
|---|
| 12474 | continue; | 
|---|
| 12475 |  | 
|---|
| 12476 | if (!req.links[link_id].error || | 
|---|
| 12477 | WARN_ON(req.links[link_id].error > 0)) | 
|---|
| 12478 | continue; | 
|---|
| 12479 |  | 
|---|
| 12480 | WARN_ON(err >= 0); | 
|---|
| 12481 |  | 
|---|
| 12482 | NL_SET_BAD_ATTR(info->extack, link); | 
|---|
| 12483 | err = req.links[link_id].error; | 
|---|
| 12484 | break; | 
|---|
| 12485 | } | 
|---|
| 12486 | } | 
|---|
| 12487 | } | 
|---|
| 12488 |  | 
|---|
| 12489 | free: | 
|---|
| 12490 | for (link_id = 0; link_id < ARRAY_SIZE(req.links); link_id++) | 
|---|
| 12491 | cfg80211_put_bss(wiphy: &rdev->wiphy, bss: req.links[link_id].bss); | 
|---|
| 12492 | cfg80211_put_bss(wiphy: &rdev->wiphy, bss: req.bss); | 
|---|
| 12493 |  | 
|---|
| 12494 | return err; | 
|---|
| 12495 | } | 
|---|
| 12496 |  | 
|---|
| 12497 | static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 12498 | { | 
|---|
| 12499 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 12500 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 12501 | const u8 *ie = NULL, *bssid; | 
|---|
| 12502 | int ie_len = 0; | 
|---|
| 12503 | u16 reason_code; | 
|---|
| 12504 | bool local_state_change; | 
|---|
| 12505 |  | 
|---|
| 12506 | if (dev->ieee80211_ptr->conn_owner_nlportid && | 
|---|
| 12507 | dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid) | 
|---|
| 12508 | return -EPERM; | 
|---|
| 12509 |  | 
|---|
| 12510 | if (!info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 12511 | return -EINVAL; | 
|---|
| 12512 |  | 
|---|
| 12513 | if (!info->attrs[NL80211_ATTR_REASON_CODE]) | 
|---|
| 12514 | return -EINVAL; | 
|---|
| 12515 |  | 
|---|
| 12516 | if (!rdev->ops->deauth) | 
|---|
| 12517 | return -EOPNOTSUPP; | 
|---|
| 12518 |  | 
|---|
| 12519 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && | 
|---|
| 12520 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) | 
|---|
| 12521 | return -EOPNOTSUPP; | 
|---|
| 12522 |  | 
|---|
| 12523 | bssid = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 12524 |  | 
|---|
| 12525 | reason_code = nla_get_u16(nla: info->attrs[NL80211_ATTR_REASON_CODE]); | 
|---|
| 12526 | if (reason_code == 0) { | 
|---|
| 12527 | /* Reason Code 0 is reserved */ | 
|---|
| 12528 | return -EINVAL; | 
|---|
| 12529 | } | 
|---|
| 12530 |  | 
|---|
| 12531 | if (info->attrs[NL80211_ATTR_IE]) { | 
|---|
| 12532 | ie = nla_data(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 12533 | ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 12534 | } | 
|---|
| 12535 |  | 
|---|
| 12536 | local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; | 
|---|
| 12537 |  | 
|---|
| 12538 | return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason: reason_code, | 
|---|
| 12539 | local_state_change); | 
|---|
| 12540 | } | 
|---|
| 12541 |  | 
|---|
| 12542 | static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 12543 | { | 
|---|
| 12544 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 12545 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 12546 | const u8 *ie = NULL, *bssid; | 
|---|
| 12547 | int ie_len = 0; | 
|---|
| 12548 | u16 reason_code; | 
|---|
| 12549 | bool local_state_change; | 
|---|
| 12550 |  | 
|---|
| 12551 | if (dev->ieee80211_ptr->conn_owner_nlportid && | 
|---|
| 12552 | dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid) | 
|---|
| 12553 | return -EPERM; | 
|---|
| 12554 |  | 
|---|
| 12555 | if (!info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 12556 | return -EINVAL; | 
|---|
| 12557 |  | 
|---|
| 12558 | if (!info->attrs[NL80211_ATTR_REASON_CODE]) | 
|---|
| 12559 | return -EINVAL; | 
|---|
| 12560 |  | 
|---|
| 12561 | if (!rdev->ops->disassoc) | 
|---|
| 12562 | return -EOPNOTSUPP; | 
|---|
| 12563 |  | 
|---|
| 12564 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && | 
|---|
| 12565 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) | 
|---|
| 12566 | return -EOPNOTSUPP; | 
|---|
| 12567 |  | 
|---|
| 12568 | bssid = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 12569 |  | 
|---|
| 12570 | reason_code = nla_get_u16(nla: info->attrs[NL80211_ATTR_REASON_CODE]); | 
|---|
| 12571 | if (reason_code == 0) { | 
|---|
| 12572 | /* Reason Code 0 is reserved */ | 
|---|
| 12573 | return -EINVAL; | 
|---|
| 12574 | } | 
|---|
| 12575 |  | 
|---|
| 12576 | if (info->attrs[NL80211_ATTR_IE]) { | 
|---|
| 12577 | ie = nla_data(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 12578 | ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 12579 | } | 
|---|
| 12580 |  | 
|---|
| 12581 | local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; | 
|---|
| 12582 |  | 
|---|
| 12583 | return cfg80211_mlme_disassoc(rdev, dev, ap_addr: bssid, ie, ie_len, reason: reason_code, | 
|---|
| 12584 | local_state_change); | 
|---|
| 12585 | } | 
|---|
| 12586 |  | 
|---|
| 12587 | static bool | 
|---|
| 12588 | nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev, | 
|---|
| 12589 | int mcast_rate[NUM_NL80211_BANDS], | 
|---|
| 12590 | int rateval) | 
|---|
| 12591 | { | 
|---|
| 12592 | struct wiphy *wiphy = &rdev->wiphy; | 
|---|
| 12593 | bool found = false; | 
|---|
| 12594 | int band, i; | 
|---|
| 12595 |  | 
|---|
| 12596 | for (band = 0; band < NUM_NL80211_BANDS; band++) { | 
|---|
| 12597 | struct ieee80211_supported_band *sband; | 
|---|
| 12598 |  | 
|---|
| 12599 | sband = wiphy->bands[band]; | 
|---|
| 12600 | if (!sband) | 
|---|
| 12601 | continue; | 
|---|
| 12602 |  | 
|---|
| 12603 | for (i = 0; i < sband->n_bitrates; i++) { | 
|---|
| 12604 | if (sband->bitrates[i].bitrate == rateval) { | 
|---|
| 12605 | mcast_rate[band] = i + 1; | 
|---|
| 12606 | found = true; | 
|---|
| 12607 | break; | 
|---|
| 12608 | } | 
|---|
| 12609 | } | 
|---|
| 12610 | } | 
|---|
| 12611 |  | 
|---|
| 12612 | return found; | 
|---|
| 12613 | } | 
|---|
| 12614 |  | 
|---|
| 12615 | static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 12616 | { | 
|---|
| 12617 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 12618 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 12619 | struct cfg80211_ibss_params ibss; | 
|---|
| 12620 | struct wiphy *wiphy; | 
|---|
| 12621 | struct cfg80211_cached_keys *connkeys = NULL; | 
|---|
| 12622 | int err; | 
|---|
| 12623 |  | 
|---|
| 12624 | memset(s: &ibss, c: 0, n: sizeof(ibss)); | 
|---|
| 12625 |  | 
|---|
| 12626 | if (!info->attrs[NL80211_ATTR_SSID] || | 
|---|
| 12627 | !nla_len(nla: info->attrs[NL80211_ATTR_SSID])) | 
|---|
| 12628 | return -EINVAL; | 
|---|
| 12629 |  | 
|---|
| 12630 | ibss.beacon_interval = 100; | 
|---|
| 12631 |  | 
|---|
| 12632 | if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) | 
|---|
| 12633 | ibss.beacon_interval = | 
|---|
| 12634 | nla_get_u32(nla: info->attrs[NL80211_ATTR_BEACON_INTERVAL]); | 
|---|
| 12635 |  | 
|---|
| 12636 | err = cfg80211_validate_beacon_int(rdev, iftype: NL80211_IFTYPE_ADHOC, | 
|---|
| 12637 | beacon_int: ibss.beacon_interval); | 
|---|
| 12638 | if (err) | 
|---|
| 12639 | return err; | 
|---|
| 12640 |  | 
|---|
| 12641 | if (!rdev->ops->join_ibss) | 
|---|
| 12642 | return -EOPNOTSUPP; | 
|---|
| 12643 |  | 
|---|
| 12644 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) | 
|---|
| 12645 | return -EOPNOTSUPP; | 
|---|
| 12646 |  | 
|---|
| 12647 | wiphy = &rdev->wiphy; | 
|---|
| 12648 |  | 
|---|
| 12649 | if (info->attrs[NL80211_ATTR_MAC]) { | 
|---|
| 12650 | ibss.bssid = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 12651 |  | 
|---|
| 12652 | if (!is_valid_ether_addr(addr: ibss.bssid)) | 
|---|
| 12653 | return -EINVAL; | 
|---|
| 12654 | } | 
|---|
| 12655 | ibss.ssid = nla_data(nla: info->attrs[NL80211_ATTR_SSID]); | 
|---|
| 12656 | ibss.ssid_len = nla_len(nla: info->attrs[NL80211_ATTR_SSID]); | 
|---|
| 12657 |  | 
|---|
| 12658 | if (info->attrs[NL80211_ATTR_IE]) { | 
|---|
| 12659 | ibss.ie = nla_data(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 12660 | ibss.ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 12661 | } | 
|---|
| 12662 |  | 
|---|
| 12663 | err = nl80211_parse_chandef(rdev, info, chandef: &ibss.chandef); | 
|---|
| 12664 | if (err) | 
|---|
| 12665 | return err; | 
|---|
| 12666 |  | 
|---|
| 12667 | if (!cfg80211_reg_can_beacon(wiphy: &rdev->wiphy, chandef: &ibss.chandef, | 
|---|
| 12668 | iftype: NL80211_IFTYPE_ADHOC)) | 
|---|
| 12669 | return -EINVAL; | 
|---|
| 12670 |  | 
|---|
| 12671 | switch (ibss.chandef.width) { | 
|---|
| 12672 | case NL80211_CHAN_WIDTH_5: | 
|---|
| 12673 | case NL80211_CHAN_WIDTH_10: | 
|---|
| 12674 | case NL80211_CHAN_WIDTH_20_NOHT: | 
|---|
| 12675 | break; | 
|---|
| 12676 | case NL80211_CHAN_WIDTH_20: | 
|---|
| 12677 | case NL80211_CHAN_WIDTH_40: | 
|---|
| 12678 | if (!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)) | 
|---|
| 12679 | return -EINVAL; | 
|---|
| 12680 | break; | 
|---|
| 12681 | case NL80211_CHAN_WIDTH_80: | 
|---|
| 12682 | case NL80211_CHAN_WIDTH_80P80: | 
|---|
| 12683 | case NL80211_CHAN_WIDTH_160: | 
|---|
| 12684 | if (!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)) | 
|---|
| 12685 | return -EINVAL; | 
|---|
| 12686 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 12687 | ftidx: NL80211_EXT_FEATURE_VHT_IBSS)) | 
|---|
| 12688 | return -EINVAL; | 
|---|
| 12689 | break; | 
|---|
| 12690 | case NL80211_CHAN_WIDTH_320: | 
|---|
| 12691 | return -EINVAL; | 
|---|
| 12692 | default: | 
|---|
| 12693 | return -EINVAL; | 
|---|
| 12694 | } | 
|---|
| 12695 |  | 
|---|
| 12696 | ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; | 
|---|
| 12697 | ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; | 
|---|
| 12698 |  | 
|---|
| 12699 | if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { | 
|---|
| 12700 | u8 *rates = | 
|---|
| 12701 | nla_data(nla: info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); | 
|---|
| 12702 | int n_rates = | 
|---|
| 12703 | nla_len(nla: info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); | 
|---|
| 12704 | struct ieee80211_supported_band *sband = | 
|---|
| 12705 | wiphy->bands[ibss.chandef.chan->band]; | 
|---|
| 12706 |  | 
|---|
| 12707 | err = ieee80211_get_ratemask(sband, rates, n_rates, | 
|---|
| 12708 | mask: &ibss.basic_rates); | 
|---|
| 12709 | if (err) | 
|---|
| 12710 | return err; | 
|---|
| 12711 | } | 
|---|
| 12712 |  | 
|---|
| 12713 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) | 
|---|
| 12714 | memcpy(to: &ibss.ht_capa_mask, | 
|---|
| 12715 | from: nla_data(nla: info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]), | 
|---|
| 12716 | len: sizeof(ibss.ht_capa_mask)); | 
|---|
| 12717 |  | 
|---|
| 12718 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) { | 
|---|
| 12719 | if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) | 
|---|
| 12720 | return -EINVAL; | 
|---|
| 12721 | memcpy(to: &ibss.ht_capa, | 
|---|
| 12722 | from: nla_data(nla: info->attrs[NL80211_ATTR_HT_CAPABILITY]), | 
|---|
| 12723 | len: sizeof(ibss.ht_capa)); | 
|---|
| 12724 | } | 
|---|
| 12725 |  | 
|---|
| 12726 | if (info->attrs[NL80211_ATTR_MCAST_RATE] && | 
|---|
| 12727 | !nl80211_parse_mcast_rate(rdev, mcast_rate: ibss.mcast_rate, | 
|---|
| 12728 | rateval: nla_get_u32(nla: info->attrs[NL80211_ATTR_MCAST_RATE]))) | 
|---|
| 12729 | return -EINVAL; | 
|---|
| 12730 |  | 
|---|
| 12731 | if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) { | 
|---|
| 12732 | bool no_ht = false; | 
|---|
| 12733 |  | 
|---|
| 12734 | connkeys = nl80211_parse_connkeys(rdev, info, no_ht: &no_ht); | 
|---|
| 12735 | if (IS_ERR(ptr: connkeys)) | 
|---|
| 12736 | return PTR_ERR(ptr: connkeys); | 
|---|
| 12737 |  | 
|---|
| 12738 | if ((ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) && | 
|---|
| 12739 | no_ht) { | 
|---|
| 12740 | kfree_sensitive(objp: connkeys); | 
|---|
| 12741 | return -EINVAL; | 
|---|
| 12742 | } | 
|---|
| 12743 | } | 
|---|
| 12744 |  | 
|---|
| 12745 | ibss.control_port = | 
|---|
| 12746 | nla_get_flag(nla: info->attrs[NL80211_ATTR_CONTROL_PORT]); | 
|---|
| 12747 |  | 
|---|
| 12748 | if (info->attrs[NL80211_ATTR_CONTROL_PORT_OVER_NL80211]) { | 
|---|
| 12749 | int r = validate_pae_over_nl80211(rdev, info); | 
|---|
| 12750 |  | 
|---|
| 12751 | if (r < 0) { | 
|---|
| 12752 | kfree_sensitive(objp: connkeys); | 
|---|
| 12753 | return r; | 
|---|
| 12754 | } | 
|---|
| 12755 |  | 
|---|
| 12756 | ibss.control_port_over_nl80211 = true; | 
|---|
| 12757 | } | 
|---|
| 12758 |  | 
|---|
| 12759 | ibss.userspace_handles_dfs = | 
|---|
| 12760 | nla_get_flag(nla: info->attrs[NL80211_ATTR_HANDLE_DFS]); | 
|---|
| 12761 |  | 
|---|
| 12762 | err = __cfg80211_join_ibss(rdev, dev, params: &ibss, connkeys); | 
|---|
| 12763 | if (err) | 
|---|
| 12764 | kfree_sensitive(objp: connkeys); | 
|---|
| 12765 | else if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) | 
|---|
| 12766 | dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid; | 
|---|
| 12767 |  | 
|---|
| 12768 | return err; | 
|---|
| 12769 | } | 
|---|
| 12770 |  | 
|---|
| 12771 | static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 12772 | { | 
|---|
| 12773 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 12774 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 12775 |  | 
|---|
| 12776 | if (!rdev->ops->leave_ibss) | 
|---|
| 12777 | return -EOPNOTSUPP; | 
|---|
| 12778 |  | 
|---|
| 12779 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) | 
|---|
| 12780 | return -EOPNOTSUPP; | 
|---|
| 12781 |  | 
|---|
| 12782 | return cfg80211_leave_ibss(rdev, dev, nowext: false); | 
|---|
| 12783 | } | 
|---|
| 12784 |  | 
|---|
| 12785 | static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 12786 | { | 
|---|
| 12787 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 12788 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 12789 | int mcast_rate[NUM_NL80211_BANDS]; | 
|---|
| 12790 | u32 nla_rate; | 
|---|
| 12791 |  | 
|---|
| 12792 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && | 
|---|
| 12793 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && | 
|---|
| 12794 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB) | 
|---|
| 12795 | return -EOPNOTSUPP; | 
|---|
| 12796 |  | 
|---|
| 12797 | if (!rdev->ops->set_mcast_rate) | 
|---|
| 12798 | return -EOPNOTSUPP; | 
|---|
| 12799 |  | 
|---|
| 12800 | memset(s: mcast_rate, c: 0, n: sizeof(mcast_rate)); | 
|---|
| 12801 |  | 
|---|
| 12802 | if (!info->attrs[NL80211_ATTR_MCAST_RATE]) | 
|---|
| 12803 | return -EINVAL; | 
|---|
| 12804 |  | 
|---|
| 12805 | nla_rate = nla_get_u32(nla: info->attrs[NL80211_ATTR_MCAST_RATE]); | 
|---|
| 12806 | if (!nl80211_parse_mcast_rate(rdev, mcast_rate, rateval: nla_rate)) | 
|---|
| 12807 | return -EINVAL; | 
|---|
| 12808 |  | 
|---|
| 12809 | return rdev_set_mcast_rate(rdev, dev, mcast_rate); | 
|---|
| 12810 | } | 
|---|
| 12811 |  | 
|---|
| 12812 | static struct sk_buff * | 
|---|
| 12813 | __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev, | 
|---|
| 12814 | struct wireless_dev *wdev, int approxlen, | 
|---|
| 12815 | u32 portid, u32 seq, enum nl80211_commands cmd, | 
|---|
| 12816 | enum nl80211_attrs attr, | 
|---|
| 12817 | const struct nl80211_vendor_cmd_info *info, | 
|---|
| 12818 | gfp_t gfp) | 
|---|
| 12819 | { | 
|---|
| 12820 | struct sk_buff *skb; | 
|---|
| 12821 | void *hdr; | 
|---|
| 12822 | struct nlattr *data; | 
|---|
| 12823 |  | 
|---|
| 12824 | skb = nlmsg_new(payload: approxlen + 100, flags: gfp); | 
|---|
| 12825 | if (!skb) | 
|---|
| 12826 | return NULL; | 
|---|
| 12827 |  | 
|---|
| 12828 | hdr = nl80211hdr_put(skb, portid, seq, flags: 0, cmd); | 
|---|
| 12829 | if (!hdr) { | 
|---|
| 12830 | kfree_skb(skb); | 
|---|
| 12831 | return NULL; | 
|---|
| 12832 | } | 
|---|
| 12833 |  | 
|---|
| 12834 | if (nla_put_u32(skb, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx)) | 
|---|
| 12835 | goto nla_put_failure; | 
|---|
| 12836 |  | 
|---|
| 12837 | if (info) { | 
|---|
| 12838 | if (nla_put_u32(skb, attrtype: NL80211_ATTR_VENDOR_ID, | 
|---|
| 12839 | value: info->vendor_id)) | 
|---|
| 12840 | goto nla_put_failure; | 
|---|
| 12841 | if (nla_put_u32(skb, attrtype: NL80211_ATTR_VENDOR_SUBCMD, | 
|---|
| 12842 | value: info->subcmd)) | 
|---|
| 12843 | goto nla_put_failure; | 
|---|
| 12844 | } | 
|---|
| 12845 |  | 
|---|
| 12846 | if (wdev) { | 
|---|
| 12847 | if (nla_put_u64_64bit(skb, attrtype: NL80211_ATTR_WDEV, | 
|---|
| 12848 | value: wdev_id(wdev), padattr: NL80211_ATTR_PAD)) | 
|---|
| 12849 | goto nla_put_failure; | 
|---|
| 12850 | if (wdev->netdev && | 
|---|
| 12851 | nla_put_u32(skb, attrtype: NL80211_ATTR_IFINDEX, | 
|---|
| 12852 | value: wdev->netdev->ifindex)) | 
|---|
| 12853 | goto nla_put_failure; | 
|---|
| 12854 | } | 
|---|
| 12855 |  | 
|---|
| 12856 | data = nla_nest_start_noflag(skb, attrtype: attr); | 
|---|
| 12857 | if (!data) | 
|---|
| 12858 | goto nla_put_failure; | 
|---|
| 12859 |  | 
|---|
| 12860 | ((void **)skb->cb)[0] = rdev; | 
|---|
| 12861 | ((void **)skb->cb)[1] = hdr; | 
|---|
| 12862 | ((void **)skb->cb)[2] = data; | 
|---|
| 12863 |  | 
|---|
| 12864 | return skb; | 
|---|
| 12865 |  | 
|---|
| 12866 | nla_put_failure: | 
|---|
| 12867 | kfree_skb(skb); | 
|---|
| 12868 | return NULL; | 
|---|
| 12869 | } | 
|---|
| 12870 |  | 
|---|
| 12871 | struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, | 
|---|
| 12872 | struct wireless_dev *wdev, | 
|---|
| 12873 | enum nl80211_commands cmd, | 
|---|
| 12874 | enum nl80211_attrs attr, | 
|---|
| 12875 | unsigned int portid, | 
|---|
| 12876 | int vendor_event_idx, | 
|---|
| 12877 | int approxlen, gfp_t gfp) | 
|---|
| 12878 | { | 
|---|
| 12879 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 12880 | const struct nl80211_vendor_cmd_info *info; | 
|---|
| 12881 |  | 
|---|
| 12882 | switch (cmd) { | 
|---|
| 12883 | case NL80211_CMD_TESTMODE: | 
|---|
| 12884 | if (WARN_ON(vendor_event_idx != -1)) | 
|---|
| 12885 | return NULL; | 
|---|
| 12886 | info = NULL; | 
|---|
| 12887 | break; | 
|---|
| 12888 | case NL80211_CMD_VENDOR: | 
|---|
| 12889 | if (WARN_ON(vendor_event_idx < 0 || | 
|---|
| 12890 | vendor_event_idx >= wiphy->n_vendor_events)) | 
|---|
| 12891 | return NULL; | 
|---|
| 12892 | info = &wiphy->vendor_events[vendor_event_idx]; | 
|---|
| 12893 | break; | 
|---|
| 12894 | default: | 
|---|
| 12895 | WARN_ON(1); | 
|---|
| 12896 | return NULL; | 
|---|
| 12897 | } | 
|---|
| 12898 |  | 
|---|
| 12899 | return __cfg80211_alloc_vendor_skb(rdev, wdev, approxlen, portid, seq: 0, | 
|---|
| 12900 | cmd, attr, info, gfp); | 
|---|
| 12901 | } | 
|---|
| 12902 | EXPORT_SYMBOL(__cfg80211_alloc_event_skb); | 
|---|
| 12903 |  | 
|---|
| 12904 | void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp) | 
|---|
| 12905 | { | 
|---|
| 12906 | struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; | 
|---|
| 12907 | void *hdr = ((void **)skb->cb)[1]; | 
|---|
| 12908 | struct nlmsghdr *nlhdr = nlmsg_hdr(skb); | 
|---|
| 12909 | struct nlattr *data = ((void **)skb->cb)[2]; | 
|---|
| 12910 | enum nl80211_multicast_groups mcgrp = NL80211_MCGRP_TESTMODE; | 
|---|
| 12911 |  | 
|---|
| 12912 | /* clear CB data for netlink core to own from now on */ | 
|---|
| 12913 | memset(s: skb->cb, c: 0, n: sizeof(skb->cb)); | 
|---|
| 12914 |  | 
|---|
| 12915 | nla_nest_end(skb, start: data); | 
|---|
| 12916 | genlmsg_end(skb, hdr); | 
|---|
| 12917 |  | 
|---|
| 12918 | if (nlhdr->nlmsg_pid) { | 
|---|
| 12919 | genlmsg_unicast(net: wiphy_net(wiphy: &rdev->wiphy), skb, | 
|---|
| 12920 | portid: nlhdr->nlmsg_pid); | 
|---|
| 12921 | } else { | 
|---|
| 12922 | if (data->nla_type == NL80211_ATTR_VENDOR_DATA) | 
|---|
| 12923 | mcgrp = NL80211_MCGRP_VENDOR; | 
|---|
| 12924 |  | 
|---|
| 12925 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), | 
|---|
| 12926 | skb, portid: 0, group: mcgrp, flags: gfp); | 
|---|
| 12927 | } | 
|---|
| 12928 | } | 
|---|
| 12929 | EXPORT_SYMBOL(__cfg80211_send_event_skb); | 
|---|
| 12930 |  | 
|---|
| 12931 | #ifdef CONFIG_NL80211_TESTMODE | 
|---|
| 12932 | static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 12933 | { | 
|---|
| 12934 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 12935 | struct wireless_dev *wdev; | 
|---|
| 12936 | int err; | 
|---|
| 12937 |  | 
|---|
| 12938 | lockdep_assert_held(&rdev->wiphy.mtx); | 
|---|
| 12939 |  | 
|---|
| 12940 | wdev = __cfg80211_wdev_from_attrs(rdev, genl_info_net(info), | 
|---|
| 12941 | info->attrs); | 
|---|
| 12942 |  | 
|---|
| 12943 | if (!rdev->ops->testmode_cmd) | 
|---|
| 12944 | return -EOPNOTSUPP; | 
|---|
| 12945 |  | 
|---|
| 12946 | if (IS_ERR(wdev)) { | 
|---|
| 12947 | err = PTR_ERR(wdev); | 
|---|
| 12948 | if (err != -EINVAL) | 
|---|
| 12949 | return err; | 
|---|
| 12950 | wdev = NULL; | 
|---|
| 12951 | } else if (wdev->wiphy != &rdev->wiphy) { | 
|---|
| 12952 | return -EINVAL; | 
|---|
| 12953 | } | 
|---|
| 12954 |  | 
|---|
| 12955 | if (!info->attrs[NL80211_ATTR_TESTDATA]) | 
|---|
| 12956 | return -EINVAL; | 
|---|
| 12957 |  | 
|---|
| 12958 | rdev->cur_cmd_info = info; | 
|---|
| 12959 | err = rdev_testmode_cmd(rdev, wdev, | 
|---|
| 12960 | nla_data(info->attrs[NL80211_ATTR_TESTDATA]), | 
|---|
| 12961 | nla_len(info->attrs[NL80211_ATTR_TESTDATA])); | 
|---|
| 12962 | rdev->cur_cmd_info = NULL; | 
|---|
| 12963 |  | 
|---|
| 12964 | return err; | 
|---|
| 12965 | } | 
|---|
| 12966 |  | 
|---|
| 12967 | static int nl80211_testmode_dump(struct sk_buff *skb, | 
|---|
| 12968 | struct netlink_callback *cb) | 
|---|
| 12969 | { | 
|---|
| 12970 | struct cfg80211_registered_device *rdev; | 
|---|
| 12971 | struct nlattr **attrbuf = NULL; | 
|---|
| 12972 | int err; | 
|---|
| 12973 | long phy_idx; | 
|---|
| 12974 | void *data = NULL; | 
|---|
| 12975 | int data_len = 0; | 
|---|
| 12976 |  | 
|---|
| 12977 | rtnl_lock(); | 
|---|
| 12978 |  | 
|---|
| 12979 | if (cb->args[0]) { | 
|---|
| 12980 | /* | 
|---|
| 12981 | * 0 is a valid index, but not valid for args[0], | 
|---|
| 12982 | * so we need to offset by 1. | 
|---|
| 12983 | */ | 
|---|
| 12984 | phy_idx = cb->args[0] - 1; | 
|---|
| 12985 |  | 
|---|
| 12986 | rdev = cfg80211_rdev_by_wiphy_idx(phy_idx); | 
|---|
| 12987 | if (!rdev) { | 
|---|
| 12988 | err = -ENOENT; | 
|---|
| 12989 | goto out_err; | 
|---|
| 12990 | } | 
|---|
| 12991 | } else { | 
|---|
| 12992 | attrbuf = kcalloc(NUM_NL80211_ATTR, sizeof(*attrbuf), | 
|---|
| 12993 | GFP_KERNEL); | 
|---|
| 12994 | if (!attrbuf) { | 
|---|
| 12995 | err = -ENOMEM; | 
|---|
| 12996 | goto out_err; | 
|---|
| 12997 | } | 
|---|
| 12998 |  | 
|---|
| 12999 | err = nlmsg_parse_deprecated(cb->nlh, | 
|---|
| 13000 | GENL_HDRLEN + nl80211_fam.hdrsize, | 
|---|
| 13001 | attrbuf, nl80211_fam.maxattr, | 
|---|
| 13002 | nl80211_policy, NULL); | 
|---|
| 13003 | if (err) | 
|---|
| 13004 | goto out_err; | 
|---|
| 13005 |  | 
|---|
| 13006 | rdev = __cfg80211_rdev_from_attrs(sock_net(skb->sk), attrbuf); | 
|---|
| 13007 | if (IS_ERR(rdev)) { | 
|---|
| 13008 | err = PTR_ERR(rdev); | 
|---|
| 13009 | goto out_err; | 
|---|
| 13010 | } | 
|---|
| 13011 | phy_idx = rdev->wiphy_idx; | 
|---|
| 13012 |  | 
|---|
| 13013 | if (attrbuf[NL80211_ATTR_TESTDATA]) | 
|---|
| 13014 | cb->args[1] = (long)attrbuf[NL80211_ATTR_TESTDATA]; | 
|---|
| 13015 | } | 
|---|
| 13016 |  | 
|---|
| 13017 | if (cb->args[1]) { | 
|---|
| 13018 | data = nla_data((void *)cb->args[1]); | 
|---|
| 13019 | data_len = nla_len((void *)cb->args[1]); | 
|---|
| 13020 | } | 
|---|
| 13021 |  | 
|---|
| 13022 | if (!rdev->ops->testmode_dump) { | 
|---|
| 13023 | err = -EOPNOTSUPP; | 
|---|
| 13024 | goto out_err; | 
|---|
| 13025 | } | 
|---|
| 13026 |  | 
|---|
| 13027 | while (1) { | 
|---|
| 13028 | void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).portid, | 
|---|
| 13029 | cb->nlh->nlmsg_seq, NLM_F_MULTI, | 
|---|
| 13030 | NL80211_CMD_TESTMODE); | 
|---|
| 13031 | struct nlattr *tmdata; | 
|---|
| 13032 |  | 
|---|
| 13033 | if (!hdr) | 
|---|
| 13034 | break; | 
|---|
| 13035 |  | 
|---|
| 13036 | if (nla_put_u32(skb, NL80211_ATTR_WIPHY, phy_idx)) { | 
|---|
| 13037 | genlmsg_cancel(skb, hdr); | 
|---|
| 13038 | break; | 
|---|
| 13039 | } | 
|---|
| 13040 |  | 
|---|
| 13041 | tmdata = nla_nest_start_noflag(skb, NL80211_ATTR_TESTDATA); | 
|---|
| 13042 | if (!tmdata) { | 
|---|
| 13043 | genlmsg_cancel(skb, hdr); | 
|---|
| 13044 | break; | 
|---|
| 13045 | } | 
|---|
| 13046 | err = rdev_testmode_dump(rdev, skb, cb, data, data_len); | 
|---|
| 13047 | nla_nest_end(skb, tmdata); | 
|---|
| 13048 |  | 
|---|
| 13049 | if (err == -ENOBUFS || err == -ENOENT) { | 
|---|
| 13050 | genlmsg_cancel(skb, hdr); | 
|---|
| 13051 | break; | 
|---|
| 13052 | } else if (err) { | 
|---|
| 13053 | genlmsg_cancel(skb, hdr); | 
|---|
| 13054 | goto out_err; | 
|---|
| 13055 | } | 
|---|
| 13056 |  | 
|---|
| 13057 | genlmsg_end(skb, hdr); | 
|---|
| 13058 | } | 
|---|
| 13059 |  | 
|---|
| 13060 | err = skb->len; | 
|---|
| 13061 | /* see above */ | 
|---|
| 13062 | cb->args[0] = phy_idx + 1; | 
|---|
| 13063 | out_err: | 
|---|
| 13064 | kfree(attrbuf); | 
|---|
| 13065 | rtnl_unlock(); | 
|---|
| 13066 | return err; | 
|---|
| 13067 | } | 
|---|
| 13068 | #endif | 
|---|
| 13069 |  | 
|---|
| 13070 | static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 13071 | { | 
|---|
| 13072 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 13073 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 13074 | struct cfg80211_connect_params connect; | 
|---|
| 13075 | struct wiphy *wiphy; | 
|---|
| 13076 | struct cfg80211_cached_keys *connkeys = NULL; | 
|---|
| 13077 | u32 freq = 0; | 
|---|
| 13078 | int err; | 
|---|
| 13079 |  | 
|---|
| 13080 | memset(s: &connect, c: 0, n: sizeof(connect)); | 
|---|
| 13081 |  | 
|---|
| 13082 | if (!info->attrs[NL80211_ATTR_SSID] || | 
|---|
| 13083 | !nla_len(nla: info->attrs[NL80211_ATTR_SSID])) | 
|---|
| 13084 | return -EINVAL; | 
|---|
| 13085 |  | 
|---|
| 13086 | if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { | 
|---|
| 13087 | connect.auth_type = | 
|---|
| 13088 | nla_get_u32(nla: info->attrs[NL80211_ATTR_AUTH_TYPE]); | 
|---|
| 13089 | if (!nl80211_valid_auth_type(rdev, auth_type: connect.auth_type, | 
|---|
| 13090 | NL80211_CMD_CONNECT)) | 
|---|
| 13091 | return -EINVAL; | 
|---|
| 13092 | } else | 
|---|
| 13093 | connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; | 
|---|
| 13094 |  | 
|---|
| 13095 | connect.privacy = info->attrs[NL80211_ATTR_PRIVACY]; | 
|---|
| 13096 |  | 
|---|
| 13097 | if (info->attrs[NL80211_ATTR_WANT_1X_4WAY_HS] && | 
|---|
| 13098 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 13099 | ftidx: NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X)) | 
|---|
| 13100 | return -EINVAL; | 
|---|
| 13101 | connect.want_1x = info->attrs[NL80211_ATTR_WANT_1X_4WAY_HS]; | 
|---|
| 13102 |  | 
|---|
| 13103 | err = nl80211_crypto_settings(rdev, info, settings: &connect.crypto, | 
|---|
| 13104 | NL80211_MAX_NR_CIPHER_SUITES); | 
|---|
| 13105 | if (err) | 
|---|
| 13106 | return err; | 
|---|
| 13107 |  | 
|---|
| 13108 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && | 
|---|
| 13109 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) | 
|---|
| 13110 | return -EOPNOTSUPP; | 
|---|
| 13111 |  | 
|---|
| 13112 | wiphy = &rdev->wiphy; | 
|---|
| 13113 |  | 
|---|
| 13114 | connect.bg_scan_period = -1; | 
|---|
| 13115 | if (info->attrs[NL80211_ATTR_BG_SCAN_PERIOD] && | 
|---|
| 13116 | (wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)) { | 
|---|
| 13117 | connect.bg_scan_period = | 
|---|
| 13118 | nla_get_u16(nla: info->attrs[NL80211_ATTR_BG_SCAN_PERIOD]); | 
|---|
| 13119 | } | 
|---|
| 13120 |  | 
|---|
| 13121 | if (info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 13122 | connect.bssid = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 13123 | else if (info->attrs[NL80211_ATTR_MAC_HINT]) | 
|---|
| 13124 | connect.bssid_hint = | 
|---|
| 13125 | nla_data(nla: info->attrs[NL80211_ATTR_MAC_HINT]); | 
|---|
| 13126 | connect.ssid = nla_data(nla: info->attrs[NL80211_ATTR_SSID]); | 
|---|
| 13127 | connect.ssid_len = nla_len(nla: info->attrs[NL80211_ATTR_SSID]); | 
|---|
| 13128 |  | 
|---|
| 13129 | if (info->attrs[NL80211_ATTR_IE]) { | 
|---|
| 13130 | connect.ie = nla_data(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 13131 | connect.ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 13132 | } | 
|---|
| 13133 |  | 
|---|
| 13134 | if (info->attrs[NL80211_ATTR_USE_MFP]) { | 
|---|
| 13135 | connect.mfp = nla_get_u32(nla: info->attrs[NL80211_ATTR_USE_MFP]); | 
|---|
| 13136 | if (connect.mfp == NL80211_MFP_OPTIONAL && | 
|---|
| 13137 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 13138 | ftidx: NL80211_EXT_FEATURE_MFP_OPTIONAL)) | 
|---|
| 13139 | return -EOPNOTSUPP; | 
|---|
| 13140 | } else { | 
|---|
| 13141 | connect.mfp = NL80211_MFP_NO; | 
|---|
| 13142 | } | 
|---|
| 13143 |  | 
|---|
| 13144 | if (info->attrs[NL80211_ATTR_PREV_BSSID]) | 
|---|
| 13145 | connect.prev_bssid = | 
|---|
| 13146 | nla_data(nla: info->attrs[NL80211_ATTR_PREV_BSSID]); | 
|---|
| 13147 |  | 
|---|
| 13148 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) | 
|---|
| 13149 | freq = MHZ_TO_KHZ(nla_get_u32( | 
|---|
| 13150 | info->attrs[NL80211_ATTR_WIPHY_FREQ])); | 
|---|
| 13151 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]) | 
|---|
| 13152 | freq += | 
|---|
| 13153 | nla_get_u32(nla: info->attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]); | 
|---|
| 13154 |  | 
|---|
| 13155 | if (freq) { | 
|---|
| 13156 | connect.channel = nl80211_get_valid_chan(wiphy, freq); | 
|---|
| 13157 | if (!connect.channel) | 
|---|
| 13158 | return -EINVAL; | 
|---|
| 13159 | } else if (info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]) { | 
|---|
| 13160 | freq = nla_get_u32(nla: info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]); | 
|---|
| 13161 | freq = MHZ_TO_KHZ(freq); | 
|---|
| 13162 | connect.channel_hint = nl80211_get_valid_chan(wiphy, freq); | 
|---|
| 13163 | if (!connect.channel_hint) | 
|---|
| 13164 | return -EINVAL; | 
|---|
| 13165 | } | 
|---|
| 13166 |  | 
|---|
| 13167 | if (info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNELS]) { | 
|---|
| 13168 | connect.edmg.channels = | 
|---|
| 13169 | nla_get_u8(nla: info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNELS]); | 
|---|
| 13170 |  | 
|---|
| 13171 | if (info->attrs[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG]) | 
|---|
| 13172 | connect.edmg.bw_config = | 
|---|
| 13173 | nla_get_u8(nla: info->attrs[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG]); | 
|---|
| 13174 | } | 
|---|
| 13175 |  | 
|---|
| 13176 | if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) { | 
|---|
| 13177 | connkeys = nl80211_parse_connkeys(rdev, info, NULL); | 
|---|
| 13178 | if (IS_ERR(ptr: connkeys)) | 
|---|
| 13179 | return PTR_ERR(ptr: connkeys); | 
|---|
| 13180 | } | 
|---|
| 13181 |  | 
|---|
| 13182 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_DISABLE_HT])) | 
|---|
| 13183 | connect.flags |= ASSOC_REQ_DISABLE_HT; | 
|---|
| 13184 |  | 
|---|
| 13185 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) | 
|---|
| 13186 | memcpy(to: &connect.ht_capa_mask, | 
|---|
| 13187 | from: nla_data(nla: info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]), | 
|---|
| 13188 | len: sizeof(connect.ht_capa_mask)); | 
|---|
| 13189 |  | 
|---|
| 13190 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) { | 
|---|
| 13191 | if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) { | 
|---|
| 13192 | kfree_sensitive(objp: connkeys); | 
|---|
| 13193 | return -EINVAL; | 
|---|
| 13194 | } | 
|---|
| 13195 | memcpy(to: &connect.ht_capa, | 
|---|
| 13196 | from: nla_data(nla: info->attrs[NL80211_ATTR_HT_CAPABILITY]), | 
|---|
| 13197 | len: sizeof(connect.ht_capa)); | 
|---|
| 13198 | } | 
|---|
| 13199 |  | 
|---|
| 13200 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_DISABLE_VHT])) | 
|---|
| 13201 | connect.flags |= ASSOC_REQ_DISABLE_VHT; | 
|---|
| 13202 |  | 
|---|
| 13203 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_DISABLE_HE])) | 
|---|
| 13204 | connect.flags |= ASSOC_REQ_DISABLE_HE; | 
|---|
| 13205 |  | 
|---|
| 13206 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_DISABLE_EHT])) | 
|---|
| 13207 | connect.flags |= ASSOC_REQ_DISABLE_EHT; | 
|---|
| 13208 |  | 
|---|
| 13209 | if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) | 
|---|
| 13210 | memcpy(to: &connect.vht_capa_mask, | 
|---|
| 13211 | from: nla_data(nla: info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]), | 
|---|
| 13212 | len: sizeof(connect.vht_capa_mask)); | 
|---|
| 13213 |  | 
|---|
| 13214 | if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) { | 
|---|
| 13215 | if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) { | 
|---|
| 13216 | kfree_sensitive(objp: connkeys); | 
|---|
| 13217 | return -EINVAL; | 
|---|
| 13218 | } | 
|---|
| 13219 | memcpy(to: &connect.vht_capa, | 
|---|
| 13220 | from: nla_data(nla: info->attrs[NL80211_ATTR_VHT_CAPABILITY]), | 
|---|
| 13221 | len: sizeof(connect.vht_capa)); | 
|---|
| 13222 | } | 
|---|
| 13223 |  | 
|---|
| 13224 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_USE_RRM])) { | 
|---|
| 13225 | if (!((rdev->wiphy.features & | 
|---|
| 13226 | NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) && | 
|---|
| 13227 | (rdev->wiphy.features & NL80211_FEATURE_QUIET)) && | 
|---|
| 13228 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 13229 | ftidx: NL80211_EXT_FEATURE_RRM)) { | 
|---|
| 13230 | kfree_sensitive(objp: connkeys); | 
|---|
| 13231 | return -EINVAL; | 
|---|
| 13232 | } | 
|---|
| 13233 | connect.flags |= ASSOC_REQ_USE_RRM; | 
|---|
| 13234 | } | 
|---|
| 13235 |  | 
|---|
| 13236 | connect.pbss = nla_get_flag(nla: info->attrs[NL80211_ATTR_PBSS]); | 
|---|
| 13237 | if (connect.pbss && !rdev->wiphy.bands[NL80211_BAND_60GHZ]) { | 
|---|
| 13238 | kfree_sensitive(objp: connkeys); | 
|---|
| 13239 | return -EOPNOTSUPP; | 
|---|
| 13240 | } | 
|---|
| 13241 |  | 
|---|
| 13242 | if (info->attrs[NL80211_ATTR_BSS_SELECT]) { | 
|---|
| 13243 | /* bss selection makes no sense if bssid is set */ | 
|---|
| 13244 | if (connect.bssid) { | 
|---|
| 13245 | kfree_sensitive(objp: connkeys); | 
|---|
| 13246 | return -EINVAL; | 
|---|
| 13247 | } | 
|---|
| 13248 |  | 
|---|
| 13249 | err = parse_bss_select(nla: info->attrs[NL80211_ATTR_BSS_SELECT], | 
|---|
| 13250 | wiphy, bss_select: &connect.bss_select); | 
|---|
| 13251 | if (err) { | 
|---|
| 13252 | kfree_sensitive(objp: connkeys); | 
|---|
| 13253 | return err; | 
|---|
| 13254 | } | 
|---|
| 13255 | } | 
|---|
| 13256 |  | 
|---|
| 13257 | if (wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 13258 | ftidx: NL80211_EXT_FEATURE_FILS_SK_OFFLOAD) && | 
|---|
| 13259 | info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] && | 
|---|
| 13260 | info->attrs[NL80211_ATTR_FILS_ERP_REALM] && | 
|---|
| 13261 | info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] && | 
|---|
| 13262 | info->attrs[NL80211_ATTR_FILS_ERP_RRK]) { | 
|---|
| 13263 | connect.fils_erp_username = | 
|---|
| 13264 | nla_data(nla: info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]); | 
|---|
| 13265 | connect.fils_erp_username_len = | 
|---|
| 13266 | nla_len(nla: info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]); | 
|---|
| 13267 | connect.fils_erp_realm = | 
|---|
| 13268 | nla_data(nla: info->attrs[NL80211_ATTR_FILS_ERP_REALM]); | 
|---|
| 13269 | connect.fils_erp_realm_len = | 
|---|
| 13270 | nla_len(nla: info->attrs[NL80211_ATTR_FILS_ERP_REALM]); | 
|---|
| 13271 | connect.fils_erp_next_seq_num = | 
|---|
| 13272 | nla_get_u16( | 
|---|
| 13273 | nla: info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM]); | 
|---|
| 13274 | connect.fils_erp_rrk = | 
|---|
| 13275 | nla_data(nla: info->attrs[NL80211_ATTR_FILS_ERP_RRK]); | 
|---|
| 13276 | connect.fils_erp_rrk_len = | 
|---|
| 13277 | nla_len(nla: info->attrs[NL80211_ATTR_FILS_ERP_RRK]); | 
|---|
| 13278 | } else if (info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] || | 
|---|
| 13279 | info->attrs[NL80211_ATTR_FILS_ERP_REALM] || | 
|---|
| 13280 | info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] || | 
|---|
| 13281 | info->attrs[NL80211_ATTR_FILS_ERP_RRK]) { | 
|---|
| 13282 | kfree_sensitive(objp: connkeys); | 
|---|
| 13283 | return -EINVAL; | 
|---|
| 13284 | } | 
|---|
| 13285 |  | 
|---|
| 13286 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT])) { | 
|---|
| 13287 | if (!info->attrs[NL80211_ATTR_SOCKET_OWNER]) { | 
|---|
| 13288 | kfree_sensitive(objp: connkeys); | 
|---|
| 13289 | GENL_SET_ERR_MSG(info, | 
|---|
| 13290 | "external auth requires connection ownership"); | 
|---|
| 13291 | return -EINVAL; | 
|---|
| 13292 | } | 
|---|
| 13293 | connect.flags |= CONNECT_REQ_EXTERNAL_AUTH_SUPPORT; | 
|---|
| 13294 | } | 
|---|
| 13295 |  | 
|---|
| 13296 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_MLO_SUPPORT])) | 
|---|
| 13297 | connect.flags |= CONNECT_REQ_MLO_SUPPORT; | 
|---|
| 13298 |  | 
|---|
| 13299 | err = cfg80211_connect(rdev, dev, connect: &connect, connkeys, | 
|---|
| 13300 | prev_bssid: connect.prev_bssid); | 
|---|
| 13301 | if (err) | 
|---|
| 13302 | kfree_sensitive(objp: connkeys); | 
|---|
| 13303 |  | 
|---|
| 13304 | if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) { | 
|---|
| 13305 | dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid; | 
|---|
| 13306 | if (connect.bssid) | 
|---|
| 13307 | memcpy(to: dev->ieee80211_ptr->disconnect_bssid, | 
|---|
| 13308 | from: connect.bssid, ETH_ALEN); | 
|---|
| 13309 | else | 
|---|
| 13310 | eth_zero_addr(addr: dev->ieee80211_ptr->disconnect_bssid); | 
|---|
| 13311 | } | 
|---|
| 13312 |  | 
|---|
| 13313 | return err; | 
|---|
| 13314 | } | 
|---|
| 13315 |  | 
|---|
| 13316 | static int nl80211_update_connect_params(struct sk_buff *skb, | 
|---|
| 13317 | struct genl_info *info) | 
|---|
| 13318 | { | 
|---|
| 13319 | struct cfg80211_connect_params connect = {}; | 
|---|
| 13320 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 13321 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 13322 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 13323 | bool fils_sk_offload; | 
|---|
| 13324 | u32 auth_type; | 
|---|
| 13325 | u32 changed = 0; | 
|---|
| 13326 |  | 
|---|
| 13327 | if (!rdev->ops->update_connect_params) | 
|---|
| 13328 | return -EOPNOTSUPP; | 
|---|
| 13329 |  | 
|---|
| 13330 | if (info->attrs[NL80211_ATTR_IE]) { | 
|---|
| 13331 | connect.ie = nla_data(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 13332 | connect.ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 13333 | changed |= UPDATE_ASSOC_IES; | 
|---|
| 13334 | } | 
|---|
| 13335 |  | 
|---|
| 13336 | fils_sk_offload = wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 13337 | ftidx: NL80211_EXT_FEATURE_FILS_SK_OFFLOAD); | 
|---|
| 13338 |  | 
|---|
| 13339 | /* | 
|---|
| 13340 | * when driver supports fils-sk offload all attributes must be | 
|---|
| 13341 | * provided. So the else covers "fils-sk-not-all" and | 
|---|
| 13342 | * "no-fils-sk-any". | 
|---|
| 13343 | */ | 
|---|
| 13344 | if (fils_sk_offload && | 
|---|
| 13345 | info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] && | 
|---|
| 13346 | info->attrs[NL80211_ATTR_FILS_ERP_REALM] && | 
|---|
| 13347 | info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] && | 
|---|
| 13348 | info->attrs[NL80211_ATTR_FILS_ERP_RRK]) { | 
|---|
| 13349 | connect.fils_erp_username = | 
|---|
| 13350 | nla_data(nla: info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]); | 
|---|
| 13351 | connect.fils_erp_username_len = | 
|---|
| 13352 | nla_len(nla: info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]); | 
|---|
| 13353 | connect.fils_erp_realm = | 
|---|
| 13354 | nla_data(nla: info->attrs[NL80211_ATTR_FILS_ERP_REALM]); | 
|---|
| 13355 | connect.fils_erp_realm_len = | 
|---|
| 13356 | nla_len(nla: info->attrs[NL80211_ATTR_FILS_ERP_REALM]); | 
|---|
| 13357 | connect.fils_erp_next_seq_num = | 
|---|
| 13358 | nla_get_u16( | 
|---|
| 13359 | nla: info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM]); | 
|---|
| 13360 | connect.fils_erp_rrk = | 
|---|
| 13361 | nla_data(nla: info->attrs[NL80211_ATTR_FILS_ERP_RRK]); | 
|---|
| 13362 | connect.fils_erp_rrk_len = | 
|---|
| 13363 | nla_len(nla: info->attrs[NL80211_ATTR_FILS_ERP_RRK]); | 
|---|
| 13364 | changed |= UPDATE_FILS_ERP_INFO; | 
|---|
| 13365 | } else if (info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] || | 
|---|
| 13366 | info->attrs[NL80211_ATTR_FILS_ERP_REALM] || | 
|---|
| 13367 | info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] || | 
|---|
| 13368 | info->attrs[NL80211_ATTR_FILS_ERP_RRK]) { | 
|---|
| 13369 | return -EINVAL; | 
|---|
| 13370 | } | 
|---|
| 13371 |  | 
|---|
| 13372 | if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { | 
|---|
| 13373 | auth_type = nla_get_u32(nla: info->attrs[NL80211_ATTR_AUTH_TYPE]); | 
|---|
| 13374 | if (!nl80211_valid_auth_type(rdev, auth_type, | 
|---|
| 13375 | NL80211_CMD_CONNECT)) | 
|---|
| 13376 | return -EINVAL; | 
|---|
| 13377 |  | 
|---|
| 13378 | if (auth_type == NL80211_AUTHTYPE_FILS_SK && | 
|---|
| 13379 | fils_sk_offload && !(changed & UPDATE_FILS_ERP_INFO)) | 
|---|
| 13380 | return -EINVAL; | 
|---|
| 13381 |  | 
|---|
| 13382 | connect.auth_type = auth_type; | 
|---|
| 13383 | changed |= UPDATE_AUTH_TYPE; | 
|---|
| 13384 | } | 
|---|
| 13385 |  | 
|---|
| 13386 | if (!wdev->connected) | 
|---|
| 13387 | return -ENOLINK; | 
|---|
| 13388 |  | 
|---|
| 13389 | return rdev_update_connect_params(rdev, dev, sme: &connect, changed); | 
|---|
| 13390 | } | 
|---|
| 13391 |  | 
|---|
| 13392 | static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 13393 | { | 
|---|
| 13394 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 13395 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 13396 | u16 reason; | 
|---|
| 13397 |  | 
|---|
| 13398 | if (dev->ieee80211_ptr->conn_owner_nlportid && | 
|---|
| 13399 | dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid) | 
|---|
| 13400 | return -EPERM; | 
|---|
| 13401 |  | 
|---|
| 13402 | reason = nla_get_u16_default(nla: info->attrs[NL80211_ATTR_REASON_CODE], | 
|---|
| 13403 | defvalue: WLAN_REASON_DEAUTH_LEAVING); | 
|---|
| 13404 |  | 
|---|
| 13405 | if (reason == 0) | 
|---|
| 13406 | return -EINVAL; | 
|---|
| 13407 |  | 
|---|
| 13408 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && | 
|---|
| 13409 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) | 
|---|
| 13410 | return -EOPNOTSUPP; | 
|---|
| 13411 |  | 
|---|
| 13412 | return cfg80211_disconnect(rdev, dev, reason, wextev: true); | 
|---|
| 13413 | } | 
|---|
| 13414 |  | 
|---|
| 13415 | static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 13416 | { | 
|---|
| 13417 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 13418 | struct net *net; | 
|---|
| 13419 | int err; | 
|---|
| 13420 |  | 
|---|
| 13421 | if (info->attrs[NL80211_ATTR_PID]) { | 
|---|
| 13422 | u32 pid = nla_get_u32(nla: info->attrs[NL80211_ATTR_PID]); | 
|---|
| 13423 |  | 
|---|
| 13424 | net = get_net_ns_by_pid(pid); | 
|---|
| 13425 | } else if (info->attrs[NL80211_ATTR_NETNS_FD]) { | 
|---|
| 13426 | u32 fd = nla_get_u32(nla: info->attrs[NL80211_ATTR_NETNS_FD]); | 
|---|
| 13427 |  | 
|---|
| 13428 | net = get_net_ns_by_fd(fd); | 
|---|
| 13429 | } else { | 
|---|
| 13430 | return -EINVAL; | 
|---|
| 13431 | } | 
|---|
| 13432 |  | 
|---|
| 13433 | if (IS_ERR(ptr: net)) | 
|---|
| 13434 | return PTR_ERR(ptr: net); | 
|---|
| 13435 |  | 
|---|
| 13436 | err = 0; | 
|---|
| 13437 |  | 
|---|
| 13438 | /* check if anything to do */ | 
|---|
| 13439 | if (!net_eq(net1: wiphy_net(wiphy: &rdev->wiphy), net2: net)) | 
|---|
| 13440 | err = cfg80211_switch_netns(rdev, net); | 
|---|
| 13441 |  | 
|---|
| 13442 | put_net(net); | 
|---|
| 13443 | return err; | 
|---|
| 13444 | } | 
|---|
| 13445 |  | 
|---|
| 13446 | static int nl80211_set_pmksa(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 13447 | { | 
|---|
| 13448 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 13449 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 13450 | struct cfg80211_pmksa pmksa; | 
|---|
| 13451 | bool ap_pmksa_caching_support = false; | 
|---|
| 13452 |  | 
|---|
| 13453 | memset(s: &pmksa, c: 0, n: sizeof(struct cfg80211_pmksa)); | 
|---|
| 13454 |  | 
|---|
| 13455 | ap_pmksa_caching_support = wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 13456 | ftidx: NL80211_EXT_FEATURE_AP_PMKSA_CACHING); | 
|---|
| 13457 |  | 
|---|
| 13458 | if (!info->attrs[NL80211_ATTR_PMKID]) | 
|---|
| 13459 | return -EINVAL; | 
|---|
| 13460 |  | 
|---|
| 13461 | pmksa.pmkid = nla_data(nla: info->attrs[NL80211_ATTR_PMKID]); | 
|---|
| 13462 |  | 
|---|
| 13463 | if (info->attrs[NL80211_ATTR_MAC]) { | 
|---|
| 13464 | pmksa.bssid = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 13465 | } else if (info->attrs[NL80211_ATTR_SSID] && | 
|---|
| 13466 | info->attrs[NL80211_ATTR_FILS_CACHE_ID] && | 
|---|
| 13467 | info->attrs[NL80211_ATTR_PMK]) { | 
|---|
| 13468 | pmksa.ssid = nla_data(nla: info->attrs[NL80211_ATTR_SSID]); | 
|---|
| 13469 | pmksa.ssid_len = nla_len(nla: info->attrs[NL80211_ATTR_SSID]); | 
|---|
| 13470 | pmksa.cache_id = nla_data(nla: info->attrs[NL80211_ATTR_FILS_CACHE_ID]); | 
|---|
| 13471 | } else { | 
|---|
| 13472 | return -EINVAL; | 
|---|
| 13473 | } | 
|---|
| 13474 |  | 
|---|
| 13475 | if (info->attrs[NL80211_ATTR_PMK]) { | 
|---|
| 13476 | pmksa.pmk = nla_data(nla: info->attrs[NL80211_ATTR_PMK]); | 
|---|
| 13477 | pmksa.pmk_len = nla_len(nla: info->attrs[NL80211_ATTR_PMK]); | 
|---|
| 13478 | } | 
|---|
| 13479 |  | 
|---|
| 13480 | if (info->attrs[NL80211_ATTR_PMK_LIFETIME]) | 
|---|
| 13481 | pmksa.pmk_lifetime = | 
|---|
| 13482 | nla_get_u32(nla: info->attrs[NL80211_ATTR_PMK_LIFETIME]); | 
|---|
| 13483 |  | 
|---|
| 13484 | if (info->attrs[NL80211_ATTR_PMK_REAUTH_THRESHOLD]) | 
|---|
| 13485 | pmksa.pmk_reauth_threshold = | 
|---|
| 13486 | nla_get_u8(nla: info->attrs[NL80211_ATTR_PMK_REAUTH_THRESHOLD]); | 
|---|
| 13487 |  | 
|---|
| 13488 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && | 
|---|
| 13489 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && | 
|---|
| 13490 | !((dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP || | 
|---|
| 13491 | dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) && | 
|---|
| 13492 | ap_pmksa_caching_support)) | 
|---|
| 13493 | return -EOPNOTSUPP; | 
|---|
| 13494 |  | 
|---|
| 13495 | if (!rdev->ops->set_pmksa) | 
|---|
| 13496 | return -EOPNOTSUPP; | 
|---|
| 13497 |  | 
|---|
| 13498 | return rdev_set_pmksa(rdev, netdev: dev, pmksa: &pmksa); | 
|---|
| 13499 | } | 
|---|
| 13500 |  | 
|---|
| 13501 | static int nl80211_del_pmksa(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 13502 | { | 
|---|
| 13503 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 13504 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 13505 | struct cfg80211_pmksa pmksa; | 
|---|
| 13506 | bool sae_offload_support = false; | 
|---|
| 13507 | bool owe_offload_support = false; | 
|---|
| 13508 | bool ap_pmksa_caching_support = false; | 
|---|
| 13509 |  | 
|---|
| 13510 | memset(s: &pmksa, c: 0, n: sizeof(struct cfg80211_pmksa)); | 
|---|
| 13511 |  | 
|---|
| 13512 | sae_offload_support = wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 13513 | ftidx: NL80211_EXT_FEATURE_SAE_OFFLOAD); | 
|---|
| 13514 | owe_offload_support = wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 13515 | ftidx: NL80211_EXT_FEATURE_OWE_OFFLOAD); | 
|---|
| 13516 | ap_pmksa_caching_support = wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 13517 | ftidx: NL80211_EXT_FEATURE_AP_PMKSA_CACHING); | 
|---|
| 13518 |  | 
|---|
| 13519 | if (info->attrs[NL80211_ATTR_PMKID]) | 
|---|
| 13520 | pmksa.pmkid = nla_data(nla: info->attrs[NL80211_ATTR_PMKID]); | 
|---|
| 13521 |  | 
|---|
| 13522 | if (info->attrs[NL80211_ATTR_MAC]) { | 
|---|
| 13523 | pmksa.bssid = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 13524 | } else if (info->attrs[NL80211_ATTR_SSID]) { | 
|---|
| 13525 | /* SSID based pmksa flush supported only for FILS, | 
|---|
| 13526 | * OWE/SAE OFFLOAD cases | 
|---|
| 13527 | */ | 
|---|
| 13528 | if (info->attrs[NL80211_ATTR_FILS_CACHE_ID] && | 
|---|
| 13529 | info->attrs[NL80211_ATTR_PMK]) { | 
|---|
| 13530 | pmksa.cache_id = nla_data(nla: info->attrs[NL80211_ATTR_FILS_CACHE_ID]); | 
|---|
| 13531 | } else if (!sae_offload_support && !owe_offload_support) { | 
|---|
| 13532 | return -EINVAL; | 
|---|
| 13533 | } | 
|---|
| 13534 | pmksa.ssid = nla_data(nla: info->attrs[NL80211_ATTR_SSID]); | 
|---|
| 13535 | pmksa.ssid_len = nla_len(nla: info->attrs[NL80211_ATTR_SSID]); | 
|---|
| 13536 | } else { | 
|---|
| 13537 | return -EINVAL; | 
|---|
| 13538 | } | 
|---|
| 13539 |  | 
|---|
| 13540 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && | 
|---|
| 13541 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && | 
|---|
| 13542 | !((dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP || | 
|---|
| 13543 | dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) && | 
|---|
| 13544 | ap_pmksa_caching_support)) | 
|---|
| 13545 | return -EOPNOTSUPP; | 
|---|
| 13546 |  | 
|---|
| 13547 | if (!rdev->ops->del_pmksa) | 
|---|
| 13548 | return -EOPNOTSUPP; | 
|---|
| 13549 |  | 
|---|
| 13550 | return rdev_del_pmksa(rdev, netdev: dev, pmksa: &pmksa); | 
|---|
| 13551 | } | 
|---|
| 13552 |  | 
|---|
| 13553 | static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 13554 | { | 
|---|
| 13555 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 13556 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 13557 |  | 
|---|
| 13558 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && | 
|---|
| 13559 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) | 
|---|
| 13560 | return -EOPNOTSUPP; | 
|---|
| 13561 |  | 
|---|
| 13562 | if (!rdev->ops->flush_pmksa) | 
|---|
| 13563 | return -EOPNOTSUPP; | 
|---|
| 13564 |  | 
|---|
| 13565 | return rdev_flush_pmksa(rdev, netdev: dev); | 
|---|
| 13566 | } | 
|---|
| 13567 |  | 
|---|
| 13568 | static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 13569 | { | 
|---|
| 13570 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 13571 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 13572 | u8 action_code, dialog_token; | 
|---|
| 13573 | u32 peer_capability = 0; | 
|---|
| 13574 | u16 status_code; | 
|---|
| 13575 | u8 *peer; | 
|---|
| 13576 | int link_id; | 
|---|
| 13577 | bool initiator; | 
|---|
| 13578 |  | 
|---|
| 13579 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || | 
|---|
| 13580 | !rdev->ops->tdls_mgmt) | 
|---|
| 13581 | return -EOPNOTSUPP; | 
|---|
| 13582 |  | 
|---|
| 13583 | if (!info->attrs[NL80211_ATTR_TDLS_ACTION] || | 
|---|
| 13584 | !info->attrs[NL80211_ATTR_STATUS_CODE] || | 
|---|
| 13585 | !info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] || | 
|---|
| 13586 | !info->attrs[NL80211_ATTR_IE] || | 
|---|
| 13587 | !info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 13588 | return -EINVAL; | 
|---|
| 13589 |  | 
|---|
| 13590 | peer = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 13591 | action_code = nla_get_u8(nla: info->attrs[NL80211_ATTR_TDLS_ACTION]); | 
|---|
| 13592 | status_code = nla_get_u16(nla: info->attrs[NL80211_ATTR_STATUS_CODE]); | 
|---|
| 13593 | dialog_token = nla_get_u8(nla: info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]); | 
|---|
| 13594 | initiator = nla_get_flag(nla: info->attrs[NL80211_ATTR_TDLS_INITIATOR]); | 
|---|
| 13595 | if (info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]) | 
|---|
| 13596 | peer_capability = | 
|---|
| 13597 | nla_get_u32(nla: info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]); | 
|---|
| 13598 | link_id = nl80211_link_id_or_invalid(attrs: info->attrs); | 
|---|
| 13599 |  | 
|---|
| 13600 | return rdev_tdls_mgmt(rdev, dev, peer, link_id, action_code, | 
|---|
| 13601 | dialog_token, status_code, peer_capability, | 
|---|
| 13602 | initiator, | 
|---|
| 13603 | buf: nla_data(nla: info->attrs[NL80211_ATTR_IE]), | 
|---|
| 13604 | len: nla_len(nla: info->attrs[NL80211_ATTR_IE])); | 
|---|
| 13605 | } | 
|---|
| 13606 |  | 
|---|
| 13607 | static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 13608 | { | 
|---|
| 13609 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 13610 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 13611 | enum nl80211_tdls_operation operation; | 
|---|
| 13612 | u8 *peer; | 
|---|
| 13613 |  | 
|---|
| 13614 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || | 
|---|
| 13615 | !rdev->ops->tdls_oper) | 
|---|
| 13616 | return -EOPNOTSUPP; | 
|---|
| 13617 |  | 
|---|
| 13618 | if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] || | 
|---|
| 13619 | !info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 13620 | return -EINVAL; | 
|---|
| 13621 |  | 
|---|
| 13622 | operation = nla_get_u8(nla: info->attrs[NL80211_ATTR_TDLS_OPERATION]); | 
|---|
| 13623 | peer = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 13624 |  | 
|---|
| 13625 | return rdev_tdls_oper(rdev, dev, peer, oper: operation); | 
|---|
| 13626 | } | 
|---|
| 13627 |  | 
|---|
| 13628 | static int nl80211_remain_on_channel(struct sk_buff *skb, | 
|---|
| 13629 | struct genl_info *info) | 
|---|
| 13630 | { | 
|---|
| 13631 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 13632 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); | 
|---|
| 13633 | struct wireless_dev *wdev = info->user_ptr[1]; | 
|---|
| 13634 | struct cfg80211_chan_def chandef; | 
|---|
| 13635 | struct sk_buff *msg; | 
|---|
| 13636 | void *hdr; | 
|---|
| 13637 | u64 cookie; | 
|---|
| 13638 | u32 duration; | 
|---|
| 13639 | int err; | 
|---|
| 13640 |  | 
|---|
| 13641 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || | 
|---|
| 13642 | !info->attrs[NL80211_ATTR_DURATION]) | 
|---|
| 13643 | return -EINVAL; | 
|---|
| 13644 |  | 
|---|
| 13645 | duration = nla_get_u32(nla: info->attrs[NL80211_ATTR_DURATION]); | 
|---|
| 13646 |  | 
|---|
| 13647 | if (!rdev->ops->remain_on_channel || | 
|---|
| 13648 | !(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)) | 
|---|
| 13649 | return -EOPNOTSUPP; | 
|---|
| 13650 |  | 
|---|
| 13651 | /* | 
|---|
| 13652 | * We should be on that channel for at least a minimum amount of | 
|---|
| 13653 | * time (10ms) but no longer than the driver supports. | 
|---|
| 13654 | */ | 
|---|
| 13655 | if (duration < NL80211_MIN_REMAIN_ON_CHANNEL_TIME || | 
|---|
| 13656 | duration > rdev->wiphy.max_remain_on_channel_duration) | 
|---|
| 13657 | return -EINVAL; | 
|---|
| 13658 |  | 
|---|
| 13659 | err = nl80211_parse_chandef(rdev, info, chandef: &chandef); | 
|---|
| 13660 | if (err) | 
|---|
| 13661 | return err; | 
|---|
| 13662 |  | 
|---|
| 13663 | if (!cfg80211_off_channel_oper_allowed(wdev, chan: chandef.chan)) { | 
|---|
| 13664 | const struct cfg80211_chan_def *oper_chandef, *compat_chandef; | 
|---|
| 13665 |  | 
|---|
| 13666 | oper_chandef = wdev_chandef(wdev, link_id); | 
|---|
| 13667 |  | 
|---|
| 13668 | if (WARN_ON(!oper_chandef)) { | 
|---|
| 13669 | /* cannot happen since we must beacon to get here */ | 
|---|
| 13670 | WARN_ON(1); | 
|---|
| 13671 | return -EBUSY; | 
|---|
| 13672 | } | 
|---|
| 13673 |  | 
|---|
| 13674 | /* note: returns first one if identical chandefs */ | 
|---|
| 13675 | compat_chandef = cfg80211_chandef_compatible(chandef1: &chandef, | 
|---|
| 13676 | chandef2: oper_chandef); | 
|---|
| 13677 |  | 
|---|
| 13678 | if (compat_chandef != &chandef) | 
|---|
| 13679 | return -EBUSY; | 
|---|
| 13680 | } | 
|---|
| 13681 |  | 
|---|
| 13682 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 13683 | if (!msg) | 
|---|
| 13684 | return -ENOMEM; | 
|---|
| 13685 |  | 
|---|
| 13686 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, | 
|---|
| 13687 | cmd: NL80211_CMD_REMAIN_ON_CHANNEL); | 
|---|
| 13688 | if (!hdr) { | 
|---|
| 13689 | err = -ENOBUFS; | 
|---|
| 13690 | goto free_msg; | 
|---|
| 13691 | } | 
|---|
| 13692 |  | 
|---|
| 13693 | err = rdev_remain_on_channel(rdev, wdev, chan: chandef.chan, | 
|---|
| 13694 | duration, cookie: &cookie); | 
|---|
| 13695 |  | 
|---|
| 13696 | if (err) | 
|---|
| 13697 | goto free_msg; | 
|---|
| 13698 |  | 
|---|
| 13699 | if (nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: cookie, | 
|---|
| 13700 | padattr: NL80211_ATTR_PAD)) | 
|---|
| 13701 | goto nla_put_failure; | 
|---|
| 13702 |  | 
|---|
| 13703 | genlmsg_end(skb: msg, hdr); | 
|---|
| 13704 |  | 
|---|
| 13705 | return genlmsg_reply(skb: msg, info); | 
|---|
| 13706 |  | 
|---|
| 13707 | nla_put_failure: | 
|---|
| 13708 | err = -ENOBUFS; | 
|---|
| 13709 | free_msg: | 
|---|
| 13710 | nlmsg_free(skb: msg); | 
|---|
| 13711 | return err; | 
|---|
| 13712 | } | 
|---|
| 13713 |  | 
|---|
| 13714 | static int nl80211_cancel_remain_on_channel(struct sk_buff *skb, | 
|---|
| 13715 | struct genl_info *info) | 
|---|
| 13716 | { | 
|---|
| 13717 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 13718 | struct wireless_dev *wdev = info->user_ptr[1]; | 
|---|
| 13719 | u64 cookie; | 
|---|
| 13720 |  | 
|---|
| 13721 | if (!info->attrs[NL80211_ATTR_COOKIE]) | 
|---|
| 13722 | return -EINVAL; | 
|---|
| 13723 |  | 
|---|
| 13724 | if (!rdev->ops->cancel_remain_on_channel) | 
|---|
| 13725 | return -EOPNOTSUPP; | 
|---|
| 13726 |  | 
|---|
| 13727 | cookie = nla_get_u64(nla: info->attrs[NL80211_ATTR_COOKIE]); | 
|---|
| 13728 |  | 
|---|
| 13729 | return rdev_cancel_remain_on_channel(rdev, wdev, cookie); | 
|---|
| 13730 | } | 
|---|
| 13731 |  | 
|---|
| 13732 | static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, | 
|---|
| 13733 | struct genl_info *info) | 
|---|
| 13734 | { | 
|---|
| 13735 | struct cfg80211_bitrate_mask mask; | 
|---|
| 13736 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); | 
|---|
| 13737 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 13738 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 13739 | int err; | 
|---|
| 13740 |  | 
|---|
| 13741 | if (!rdev->ops->set_bitrate_mask) | 
|---|
| 13742 | return -EOPNOTSUPP; | 
|---|
| 13743 |  | 
|---|
| 13744 | err = nl80211_parse_tx_bitrate_mask(info, attrs: info->attrs, | 
|---|
| 13745 | attr: NL80211_ATTR_TX_RATES, mask: &mask, | 
|---|
| 13746 | dev, default_all_enabled: true, link_id); | 
|---|
| 13747 | if (err) | 
|---|
| 13748 | return err; | 
|---|
| 13749 |  | 
|---|
| 13750 | return rdev_set_bitrate_mask(rdev, dev, link_id, NULL, mask: &mask); | 
|---|
| 13751 | } | 
|---|
| 13752 |  | 
|---|
| 13753 | static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 13754 | { | 
|---|
| 13755 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 13756 | struct wireless_dev *wdev = info->user_ptr[1]; | 
|---|
| 13757 | u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION; | 
|---|
| 13758 |  | 
|---|
| 13759 | if (!info->attrs[NL80211_ATTR_FRAME_MATCH]) | 
|---|
| 13760 | return -EINVAL; | 
|---|
| 13761 |  | 
|---|
| 13762 | if (info->attrs[NL80211_ATTR_FRAME_TYPE]) | 
|---|
| 13763 | frame_type = nla_get_u16(nla: info->attrs[NL80211_ATTR_FRAME_TYPE]); | 
|---|
| 13764 |  | 
|---|
| 13765 | switch (wdev->iftype) { | 
|---|
| 13766 | case NL80211_IFTYPE_STATION: | 
|---|
| 13767 | case NL80211_IFTYPE_ADHOC: | 
|---|
| 13768 | case NL80211_IFTYPE_P2P_CLIENT: | 
|---|
| 13769 | case NL80211_IFTYPE_AP: | 
|---|
| 13770 | case NL80211_IFTYPE_AP_VLAN: | 
|---|
| 13771 | case NL80211_IFTYPE_MESH_POINT: | 
|---|
| 13772 | case NL80211_IFTYPE_P2P_GO: | 
|---|
| 13773 | case NL80211_IFTYPE_P2P_DEVICE: | 
|---|
| 13774 | break; | 
|---|
| 13775 | case NL80211_IFTYPE_NAN: | 
|---|
| 13776 | if (!wiphy_ext_feature_isset(wiphy: wdev->wiphy, | 
|---|
| 13777 | ftidx: NL80211_EXT_FEATURE_SECURE_NAN) && | 
|---|
| 13778 | !(wdev->wiphy->nan_capa.flags & | 
|---|
| 13779 | WIPHY_NAN_FLAGS_USERSPACE_DE)) | 
|---|
| 13780 | return -EOPNOTSUPP; | 
|---|
| 13781 | break; | 
|---|
| 13782 | default: | 
|---|
| 13783 | return -EOPNOTSUPP; | 
|---|
| 13784 | } | 
|---|
| 13785 |  | 
|---|
| 13786 | /* not much point in registering if we can't reply */ | 
|---|
| 13787 | if (!rdev->ops->mgmt_tx) | 
|---|
| 13788 | return -EOPNOTSUPP; | 
|---|
| 13789 |  | 
|---|
| 13790 | if (info->attrs[NL80211_ATTR_RECEIVE_MULTICAST] && | 
|---|
| 13791 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 13792 | ftidx: NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS)) { | 
|---|
| 13793 | GENL_SET_ERR_MSG(info, | 
|---|
| 13794 | "multicast RX registrations are not supported"); | 
|---|
| 13795 | return -EOPNOTSUPP; | 
|---|
| 13796 | } | 
|---|
| 13797 |  | 
|---|
| 13798 | return cfg80211_mlme_register_mgmt(wdev, snd_pid: info->snd_portid, frame_type, | 
|---|
| 13799 | match_data: nla_data(nla: info->attrs[NL80211_ATTR_FRAME_MATCH]), | 
|---|
| 13800 | match_len: nla_len(nla: info->attrs[NL80211_ATTR_FRAME_MATCH]), | 
|---|
| 13801 | multicast_rx: info->attrs[NL80211_ATTR_RECEIVE_MULTICAST], | 
|---|
| 13802 | extack: info->extack); | 
|---|
| 13803 | } | 
|---|
| 13804 |  | 
|---|
| 13805 | static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 13806 | { | 
|---|
| 13807 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 13808 | struct wireless_dev *wdev = info->user_ptr[1]; | 
|---|
| 13809 | struct cfg80211_chan_def chandef; | 
|---|
| 13810 | int err; | 
|---|
| 13811 | void *hdr = NULL; | 
|---|
| 13812 | u64 cookie; | 
|---|
| 13813 | struct sk_buff *msg = NULL; | 
|---|
| 13814 | struct cfg80211_mgmt_tx_params params = { | 
|---|
| 13815 | .dont_wait_for_ack = | 
|---|
| 13816 | info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK], | 
|---|
| 13817 | }; | 
|---|
| 13818 |  | 
|---|
| 13819 | if (!info->attrs[NL80211_ATTR_FRAME]) | 
|---|
| 13820 | return -EINVAL; | 
|---|
| 13821 |  | 
|---|
| 13822 | if (!rdev->ops->mgmt_tx) | 
|---|
| 13823 | return -EOPNOTSUPP; | 
|---|
| 13824 |  | 
|---|
| 13825 | switch (wdev->iftype) { | 
|---|
| 13826 | case NL80211_IFTYPE_P2P_DEVICE: | 
|---|
| 13827 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) | 
|---|
| 13828 | return -EINVAL; | 
|---|
| 13829 | break; | 
|---|
| 13830 | case NL80211_IFTYPE_STATION: | 
|---|
| 13831 | case NL80211_IFTYPE_ADHOC: | 
|---|
| 13832 | case NL80211_IFTYPE_P2P_CLIENT: | 
|---|
| 13833 | case NL80211_IFTYPE_AP: | 
|---|
| 13834 | case NL80211_IFTYPE_AP_VLAN: | 
|---|
| 13835 | case NL80211_IFTYPE_MESH_POINT: | 
|---|
| 13836 | case NL80211_IFTYPE_P2P_GO: | 
|---|
| 13837 | break; | 
|---|
| 13838 | case NL80211_IFTYPE_NAN: | 
|---|
| 13839 | if (!wiphy_ext_feature_isset(wiphy: wdev->wiphy, | 
|---|
| 13840 | ftidx: NL80211_EXT_FEATURE_SECURE_NAN) && | 
|---|
| 13841 | !(wdev->wiphy->nan_capa.flags & | 
|---|
| 13842 | WIPHY_NAN_FLAGS_USERSPACE_DE)) | 
|---|
| 13843 | return -EOPNOTSUPP; | 
|---|
| 13844 | break; | 
|---|
| 13845 | default: | 
|---|
| 13846 | return -EOPNOTSUPP; | 
|---|
| 13847 | } | 
|---|
| 13848 |  | 
|---|
| 13849 | if (info->attrs[NL80211_ATTR_DURATION]) { | 
|---|
| 13850 | if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) | 
|---|
| 13851 | return -EINVAL; | 
|---|
| 13852 | params.wait = nla_get_u32(nla: info->attrs[NL80211_ATTR_DURATION]); | 
|---|
| 13853 |  | 
|---|
| 13854 | /* | 
|---|
| 13855 | * We should wait on the channel for at least a minimum amount | 
|---|
| 13856 | * of time (10ms) but no longer than the driver supports. | 
|---|
| 13857 | */ | 
|---|
| 13858 | if (params.wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME || | 
|---|
| 13859 | params.wait > rdev->wiphy.max_remain_on_channel_duration) | 
|---|
| 13860 | return -EINVAL; | 
|---|
| 13861 | } | 
|---|
| 13862 |  | 
|---|
| 13863 | params.offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK]; | 
|---|
| 13864 |  | 
|---|
| 13865 | if (params.offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) | 
|---|
| 13866 | return -EINVAL; | 
|---|
| 13867 |  | 
|---|
| 13868 | params.no_cck = nla_get_flag(nla: info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); | 
|---|
| 13869 |  | 
|---|
| 13870 | /* get the channel if any has been specified, otherwise pass NULL to | 
|---|
| 13871 | * the driver. The latter will use the current one | 
|---|
| 13872 | */ | 
|---|
| 13873 | chandef.chan = NULL; | 
|---|
| 13874 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { | 
|---|
| 13875 | err = nl80211_parse_chandef(rdev, info, chandef: &chandef); | 
|---|
| 13876 | if (err) | 
|---|
| 13877 | return err; | 
|---|
| 13878 | } | 
|---|
| 13879 |  | 
|---|
| 13880 | if (!chandef.chan && params.offchan) | 
|---|
| 13881 | return -EINVAL; | 
|---|
| 13882 |  | 
|---|
| 13883 | if (params.offchan && | 
|---|
| 13884 | !cfg80211_off_channel_oper_allowed(wdev, chan: chandef.chan)) | 
|---|
| 13885 | return -EBUSY; | 
|---|
| 13886 |  | 
|---|
| 13887 | params.link_id = nl80211_link_id_or_invalid(attrs: info->attrs); | 
|---|
| 13888 | /* | 
|---|
| 13889 | * This now races due to the unlock, but we cannot check | 
|---|
| 13890 | * the valid links for the _station_ anyway, so that's up | 
|---|
| 13891 | * to the driver. | 
|---|
| 13892 | */ | 
|---|
| 13893 | if (params.link_id >= 0 && | 
|---|
| 13894 | !(wdev->valid_links & BIT(params.link_id))) | 
|---|
| 13895 | return -EINVAL; | 
|---|
| 13896 |  | 
|---|
| 13897 | params.buf = nla_data(nla: info->attrs[NL80211_ATTR_FRAME]); | 
|---|
| 13898 | params.len = nla_len(nla: info->attrs[NL80211_ATTR_FRAME]); | 
|---|
| 13899 |  | 
|---|
| 13900 | err = nl80211_parse_counter_offsets(rdev, NULL, datalen: params.len, first_count: -1, | 
|---|
| 13901 | attr: info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX], | 
|---|
| 13902 | offsets: ¶ms.csa_offsets, | 
|---|
| 13903 | n_offsets: ¶ms.n_csa_offsets); | 
|---|
| 13904 | if (err) | 
|---|
| 13905 | return err; | 
|---|
| 13906 |  | 
|---|
| 13907 | if (!params.dont_wait_for_ack) { | 
|---|
| 13908 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 13909 | if (!msg) | 
|---|
| 13910 | return -ENOMEM; | 
|---|
| 13911 |  | 
|---|
| 13912 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, | 
|---|
| 13913 | cmd: NL80211_CMD_FRAME); | 
|---|
| 13914 | if (!hdr) { | 
|---|
| 13915 | err = -ENOBUFS; | 
|---|
| 13916 | goto free_msg; | 
|---|
| 13917 | } | 
|---|
| 13918 | } | 
|---|
| 13919 |  | 
|---|
| 13920 | params.chan = chandef.chan; | 
|---|
| 13921 | err = cfg80211_mlme_mgmt_tx(rdev, wdev, params: ¶ms, cookie: &cookie); | 
|---|
| 13922 | if (err) | 
|---|
| 13923 | goto free_msg; | 
|---|
| 13924 |  | 
|---|
| 13925 | if (msg) { | 
|---|
| 13926 | if (nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: cookie, | 
|---|
| 13927 | padattr: NL80211_ATTR_PAD)) | 
|---|
| 13928 | goto nla_put_failure; | 
|---|
| 13929 |  | 
|---|
| 13930 | genlmsg_end(skb: msg, hdr); | 
|---|
| 13931 | return genlmsg_reply(skb: msg, info); | 
|---|
| 13932 | } | 
|---|
| 13933 |  | 
|---|
| 13934 | return 0; | 
|---|
| 13935 |  | 
|---|
| 13936 | nla_put_failure: | 
|---|
| 13937 | err = -ENOBUFS; | 
|---|
| 13938 | free_msg: | 
|---|
| 13939 | nlmsg_free(skb: msg); | 
|---|
| 13940 | return err; | 
|---|
| 13941 | } | 
|---|
| 13942 |  | 
|---|
| 13943 | static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 13944 | { | 
|---|
| 13945 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 13946 | struct wireless_dev *wdev = info->user_ptr[1]; | 
|---|
| 13947 | u64 cookie; | 
|---|
| 13948 |  | 
|---|
| 13949 | if (!info->attrs[NL80211_ATTR_COOKIE]) | 
|---|
| 13950 | return -EINVAL; | 
|---|
| 13951 |  | 
|---|
| 13952 | if (!rdev->ops->mgmt_tx_cancel_wait) | 
|---|
| 13953 | return -EOPNOTSUPP; | 
|---|
| 13954 |  | 
|---|
| 13955 | switch (wdev->iftype) { | 
|---|
| 13956 | case NL80211_IFTYPE_STATION: | 
|---|
| 13957 | case NL80211_IFTYPE_ADHOC: | 
|---|
| 13958 | case NL80211_IFTYPE_P2P_CLIENT: | 
|---|
| 13959 | case NL80211_IFTYPE_AP: | 
|---|
| 13960 | case NL80211_IFTYPE_AP_VLAN: | 
|---|
| 13961 | case NL80211_IFTYPE_P2P_GO: | 
|---|
| 13962 | case NL80211_IFTYPE_P2P_DEVICE: | 
|---|
| 13963 | break; | 
|---|
| 13964 | case NL80211_IFTYPE_NAN: | 
|---|
| 13965 | if (!wiphy_ext_feature_isset(wiphy: wdev->wiphy, | 
|---|
| 13966 | ftidx: NL80211_EXT_FEATURE_SECURE_NAN)) | 
|---|
| 13967 | return -EOPNOTSUPP; | 
|---|
| 13968 | break; | 
|---|
| 13969 | default: | 
|---|
| 13970 | return -EOPNOTSUPP; | 
|---|
| 13971 | } | 
|---|
| 13972 |  | 
|---|
| 13973 | cookie = nla_get_u64(nla: info->attrs[NL80211_ATTR_COOKIE]); | 
|---|
| 13974 |  | 
|---|
| 13975 | return rdev_mgmt_tx_cancel_wait(rdev, wdev, cookie); | 
|---|
| 13976 | } | 
|---|
| 13977 |  | 
|---|
| 13978 | static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 13979 | { | 
|---|
| 13980 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 13981 | struct wireless_dev *wdev; | 
|---|
| 13982 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 13983 | u8 ps_state; | 
|---|
| 13984 | bool state; | 
|---|
| 13985 | int err; | 
|---|
| 13986 |  | 
|---|
| 13987 | if (!info->attrs[NL80211_ATTR_PS_STATE]) | 
|---|
| 13988 | return -EINVAL; | 
|---|
| 13989 |  | 
|---|
| 13990 | ps_state = nla_get_u32(nla: info->attrs[NL80211_ATTR_PS_STATE]); | 
|---|
| 13991 |  | 
|---|
| 13992 | wdev = dev->ieee80211_ptr; | 
|---|
| 13993 |  | 
|---|
| 13994 | if (!rdev->ops->set_power_mgmt) | 
|---|
| 13995 | return -EOPNOTSUPP; | 
|---|
| 13996 |  | 
|---|
| 13997 | state = (ps_state == NL80211_PS_ENABLED) ? true : false; | 
|---|
| 13998 |  | 
|---|
| 13999 | if (state == wdev->ps) | 
|---|
| 14000 | return 0; | 
|---|
| 14001 |  | 
|---|
| 14002 | err = rdev_set_power_mgmt(rdev, dev, enabled: state, timeout: wdev->ps_timeout); | 
|---|
| 14003 | if (!err) | 
|---|
| 14004 | wdev->ps = state; | 
|---|
| 14005 | return err; | 
|---|
| 14006 | } | 
|---|
| 14007 |  | 
|---|
| 14008 | static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 14009 | { | 
|---|
| 14010 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 14011 | enum nl80211_ps_state ps_state; | 
|---|
| 14012 | struct wireless_dev *wdev; | 
|---|
| 14013 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 14014 | struct sk_buff *msg; | 
|---|
| 14015 | void *hdr; | 
|---|
| 14016 | int err; | 
|---|
| 14017 |  | 
|---|
| 14018 | wdev = dev->ieee80211_ptr; | 
|---|
| 14019 |  | 
|---|
| 14020 | if (!rdev->ops->set_power_mgmt) | 
|---|
| 14021 | return -EOPNOTSUPP; | 
|---|
| 14022 |  | 
|---|
| 14023 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 14024 | if (!msg) | 
|---|
| 14025 | return -ENOMEM; | 
|---|
| 14026 |  | 
|---|
| 14027 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, | 
|---|
| 14028 | cmd: NL80211_CMD_GET_POWER_SAVE); | 
|---|
| 14029 | if (!hdr) { | 
|---|
| 14030 | err = -ENOBUFS; | 
|---|
| 14031 | goto free_msg; | 
|---|
| 14032 | } | 
|---|
| 14033 |  | 
|---|
| 14034 | if (wdev->ps) | 
|---|
| 14035 | ps_state = NL80211_PS_ENABLED; | 
|---|
| 14036 | else | 
|---|
| 14037 | ps_state = NL80211_PS_DISABLED; | 
|---|
| 14038 |  | 
|---|
| 14039 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_PS_STATE, value: ps_state)) | 
|---|
| 14040 | goto nla_put_failure; | 
|---|
| 14041 |  | 
|---|
| 14042 | genlmsg_end(skb: msg, hdr); | 
|---|
| 14043 | return genlmsg_reply(skb: msg, info); | 
|---|
| 14044 |  | 
|---|
| 14045 | nla_put_failure: | 
|---|
| 14046 | err = -ENOBUFS; | 
|---|
| 14047 | free_msg: | 
|---|
| 14048 | nlmsg_free(skb: msg); | 
|---|
| 14049 | return err; | 
|---|
| 14050 | } | 
|---|
| 14051 |  | 
|---|
| 14052 | static const struct nla_policy | 
|---|
| 14053 | nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] = { | 
|---|
| 14054 | [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_BINARY }, | 
|---|
| 14055 | [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 }, | 
|---|
| 14056 | [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, | 
|---|
| 14057 | [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 }, | 
|---|
| 14058 | [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 }, | 
|---|
| 14059 | [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 }, | 
|---|
| 14060 | [NL80211_ATTR_CQM_RSSI_LEVEL] = { .type = NLA_S32 }, | 
|---|
| 14061 | }; | 
|---|
| 14062 |  | 
|---|
| 14063 | static int nl80211_set_cqm_txe(struct genl_info *info, | 
|---|
| 14064 | u32 rate, u32 pkts, u32 intvl) | 
|---|
| 14065 | { | 
|---|
| 14066 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 14067 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 14068 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 14069 |  | 
|---|
| 14070 | if (rate > 100 || intvl > NL80211_CQM_TXE_MAX_INTVL) | 
|---|
| 14071 | return -EINVAL; | 
|---|
| 14072 |  | 
|---|
| 14073 | if (!rdev->ops->set_cqm_txe_config) | 
|---|
| 14074 | return -EOPNOTSUPP; | 
|---|
| 14075 |  | 
|---|
| 14076 | if (wdev->iftype != NL80211_IFTYPE_STATION && | 
|---|
| 14077 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) | 
|---|
| 14078 | return -EOPNOTSUPP; | 
|---|
| 14079 |  | 
|---|
| 14080 | return rdev_set_cqm_txe_config(rdev, dev, rate, pkts, intvl); | 
|---|
| 14081 | } | 
|---|
| 14082 |  | 
|---|
| 14083 | static int (struct cfg80211_registered_device *rdev, | 
|---|
| 14084 | struct net_device *dev, | 
|---|
| 14085 | struct cfg80211_cqm_config *cqm_config) | 
|---|
| 14086 | { | 
|---|
| 14087 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 14088 | s32 last, low, high; | 
|---|
| 14089 | u32 hyst; | 
|---|
| 14090 | int i, n, low_index; | 
|---|
| 14091 | int err; | 
|---|
| 14092 |  | 
|---|
| 14093 | /* | 
|---|
| 14094 | * Obtain current RSSI value if possible, if not and no RSSI threshold | 
|---|
| 14095 | * event has been received yet, we should receive an event after a | 
|---|
| 14096 | * connection is established and enough beacons received to calculate | 
|---|
| 14097 | * the average. | 
|---|
| 14098 | */ | 
|---|
| 14099 | if (!cqm_config->last_rssi_event_value && | 
|---|
| 14100 | wdev->links[0].client.current_bss && | 
|---|
| 14101 | rdev->ops->get_station) { | 
|---|
| 14102 | struct station_info sinfo = {}; | 
|---|
| 14103 | u8 *mac_addr; | 
|---|
| 14104 |  | 
|---|
| 14105 | mac_addr = wdev->links[0].client.current_bss->pub.bssid; | 
|---|
| 14106 |  | 
|---|
| 14107 | err = rdev_get_station(rdev, dev, mac: mac_addr, sinfo: &sinfo); | 
|---|
| 14108 | if (err) | 
|---|
| 14109 | return err; | 
|---|
| 14110 |  | 
|---|
| 14111 | cfg80211_sinfo_release_content(sinfo: &sinfo); | 
|---|
| 14112 | if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_BEACON_SIGNAL_AVG)) | 
|---|
| 14113 | cqm_config->last_rssi_event_value = | 
|---|
| 14114 | (s8) sinfo.rx_beacon_signal_avg; | 
|---|
| 14115 | } | 
|---|
| 14116 |  | 
|---|
| 14117 | last = cqm_config->last_rssi_event_value; | 
|---|
| 14118 | hyst = cqm_config->rssi_hyst; | 
|---|
| 14119 | n = cqm_config->n_rssi_thresholds; | 
|---|
| 14120 |  | 
|---|
| 14121 | for (i = 0; i < n; i++) { | 
|---|
| 14122 | i = array_index_nospec(i, n); | 
|---|
| 14123 | if (last < cqm_config->rssi_thresholds[i]) | 
|---|
| 14124 | break; | 
|---|
| 14125 | } | 
|---|
| 14126 |  | 
|---|
| 14127 | low_index = i - 1; | 
|---|
| 14128 | if (low_index >= 0) { | 
|---|
| 14129 | low_index = array_index_nospec(low_index, n); | 
|---|
| 14130 | low = cqm_config->rssi_thresholds[low_index] - hyst; | 
|---|
| 14131 | } else { | 
|---|
| 14132 | low = S32_MIN; | 
|---|
| 14133 | } | 
|---|
| 14134 | if (i < n) { | 
|---|
| 14135 | i = array_index_nospec(i, n); | 
|---|
| 14136 | high = cqm_config->rssi_thresholds[i] + hyst - 1; | 
|---|
| 14137 | } else { | 
|---|
| 14138 | high = S32_MAX; | 
|---|
| 14139 | } | 
|---|
| 14140 |  | 
|---|
| 14141 | return rdev_set_cqm_rssi_range_config(rdev, dev, low, high); | 
|---|
| 14142 | } | 
|---|
| 14143 |  | 
|---|
| 14144 | static int (struct genl_info *info, | 
|---|
| 14145 | const s32 *thresholds, int n_thresholds, | 
|---|
| 14146 | u32 hysteresis) | 
|---|
| 14147 | { | 
|---|
| 14148 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 14149 | struct cfg80211_cqm_config *cqm_config = NULL, *old; | 
|---|
| 14150 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 14151 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 14152 | s32 prev = S32_MIN; | 
|---|
| 14153 | int i, err; | 
|---|
| 14154 |  | 
|---|
| 14155 | /* Check all values negative and sorted */ | 
|---|
| 14156 | for (i = 0; i < n_thresholds; i++) { | 
|---|
| 14157 | if (thresholds[i] > 0 || thresholds[i] <= prev) | 
|---|
| 14158 | return -EINVAL; | 
|---|
| 14159 |  | 
|---|
| 14160 | prev = thresholds[i]; | 
|---|
| 14161 | } | 
|---|
| 14162 |  | 
|---|
| 14163 | if (wdev->iftype != NL80211_IFTYPE_STATION && | 
|---|
| 14164 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) | 
|---|
| 14165 | return -EOPNOTSUPP; | 
|---|
| 14166 |  | 
|---|
| 14167 | if (n_thresholds == 1 && thresholds[0] == 0) /* Disabling */ | 
|---|
| 14168 | n_thresholds = 0; | 
|---|
| 14169 |  | 
|---|
| 14170 | old = wiphy_dereference(wdev->wiphy, wdev->cqm_config); | 
|---|
| 14171 |  | 
|---|
| 14172 | /* if already disabled just succeed */ | 
|---|
| 14173 | if (!n_thresholds && !old) | 
|---|
| 14174 | return 0; | 
|---|
| 14175 |  | 
|---|
| 14176 | if (n_thresholds > 1) { | 
|---|
| 14177 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 14178 | ftidx: NL80211_EXT_FEATURE_CQM_RSSI_LIST) || | 
|---|
| 14179 | !rdev->ops->set_cqm_rssi_range_config) | 
|---|
| 14180 | return -EOPNOTSUPP; | 
|---|
| 14181 | } else { | 
|---|
| 14182 | if (!rdev->ops->set_cqm_rssi_config) | 
|---|
| 14183 | return -EOPNOTSUPP; | 
|---|
| 14184 | } | 
|---|
| 14185 |  | 
|---|
| 14186 | if (n_thresholds) { | 
|---|
| 14187 | cqm_config = kzalloc(struct_size(cqm_config, rssi_thresholds, | 
|---|
| 14188 | n_thresholds), | 
|---|
| 14189 | GFP_KERNEL); | 
|---|
| 14190 | if (!cqm_config) | 
|---|
| 14191 | return -ENOMEM; | 
|---|
| 14192 |  | 
|---|
| 14193 | cqm_config->rssi_hyst = hysteresis; | 
|---|
| 14194 | cqm_config->n_rssi_thresholds = n_thresholds; | 
|---|
| 14195 | memcpy(to: cqm_config->rssi_thresholds, from: thresholds, | 
|---|
| 14196 | flex_array_size(cqm_config, rssi_thresholds, | 
|---|
| 14197 | n_thresholds)); | 
|---|
| 14198 | cqm_config->use_range_api = n_thresholds > 1 || | 
|---|
| 14199 | !rdev->ops->set_cqm_rssi_config; | 
|---|
| 14200 |  | 
|---|
| 14201 | rcu_assign_pointer(wdev->cqm_config, cqm_config); | 
|---|
| 14202 |  | 
|---|
| 14203 | if (cqm_config->use_range_api) | 
|---|
| 14204 | err = cfg80211_cqm_rssi_update(rdev, dev, cqm_config); | 
|---|
| 14205 | else | 
|---|
| 14206 | err = rdev_set_cqm_rssi_config(rdev, dev, | 
|---|
| 14207 | rssi_thold: thresholds[0], | 
|---|
| 14208 | rssi_hyst: hysteresis); | 
|---|
| 14209 | } else { | 
|---|
| 14210 | RCU_INIT_POINTER(wdev->cqm_config, NULL); | 
|---|
| 14211 | /* if enabled as range also disable via range */ | 
|---|
| 14212 | if (old->use_range_api) | 
|---|
| 14213 | err = rdev_set_cqm_rssi_range_config(rdev, dev, low: 0, high: 0); | 
|---|
| 14214 | else | 
|---|
| 14215 | err = rdev_set_cqm_rssi_config(rdev, dev, rssi_thold: 0, rssi_hyst: 0); | 
|---|
| 14216 | } | 
|---|
| 14217 |  | 
|---|
| 14218 | if (err) { | 
|---|
| 14219 | rcu_assign_pointer(wdev->cqm_config, old); | 
|---|
| 14220 | kfree_rcu(cqm_config, rcu_head); | 
|---|
| 14221 | } else { | 
|---|
| 14222 | kfree_rcu(old, rcu_head); | 
|---|
| 14223 | } | 
|---|
| 14224 |  | 
|---|
| 14225 | return err; | 
|---|
| 14226 | } | 
|---|
| 14227 |  | 
|---|
| 14228 | static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 14229 | { | 
|---|
| 14230 | struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1]; | 
|---|
| 14231 | struct nlattr *cqm; | 
|---|
| 14232 | int err; | 
|---|
| 14233 |  | 
|---|
| 14234 | cqm = info->attrs[NL80211_ATTR_CQM]; | 
|---|
| 14235 | if (!cqm) | 
|---|
| 14236 | return -EINVAL; | 
|---|
| 14237 |  | 
|---|
| 14238 | err = nla_parse_nested_deprecated(tb: attrs, maxtype: NL80211_ATTR_CQM_MAX, nla: cqm, | 
|---|
| 14239 | policy: nl80211_attr_cqm_policy, | 
|---|
| 14240 | extack: info->extack); | 
|---|
| 14241 | if (err) | 
|---|
| 14242 | return err; | 
|---|
| 14243 |  | 
|---|
| 14244 | if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] && | 
|---|
| 14245 | attrs[NL80211_ATTR_CQM_RSSI_HYST]) { | 
|---|
| 14246 | const s32 *thresholds = | 
|---|
| 14247 | nla_data(nla: attrs[NL80211_ATTR_CQM_RSSI_THOLD]); | 
|---|
| 14248 | int len = nla_len(nla: attrs[NL80211_ATTR_CQM_RSSI_THOLD]); | 
|---|
| 14249 | u32 hysteresis = nla_get_u32(nla: attrs[NL80211_ATTR_CQM_RSSI_HYST]); | 
|---|
| 14250 |  | 
|---|
| 14251 | if (len % 4) | 
|---|
| 14252 | return -EINVAL; | 
|---|
| 14253 |  | 
|---|
| 14254 | return nl80211_set_cqm_rssi(info, thresholds, n_thresholds: len / 4, | 
|---|
| 14255 | hysteresis); | 
|---|
| 14256 | } | 
|---|
| 14257 |  | 
|---|
| 14258 | if (attrs[NL80211_ATTR_CQM_TXE_RATE] && | 
|---|
| 14259 | attrs[NL80211_ATTR_CQM_TXE_PKTS] && | 
|---|
| 14260 | attrs[NL80211_ATTR_CQM_TXE_INTVL]) { | 
|---|
| 14261 | u32 rate = nla_get_u32(nla: attrs[NL80211_ATTR_CQM_TXE_RATE]); | 
|---|
| 14262 | u32 pkts = nla_get_u32(nla: attrs[NL80211_ATTR_CQM_TXE_PKTS]); | 
|---|
| 14263 | u32 intvl = nla_get_u32(nla: attrs[NL80211_ATTR_CQM_TXE_INTVL]); | 
|---|
| 14264 |  | 
|---|
| 14265 | return nl80211_set_cqm_txe(info, rate, pkts, intvl); | 
|---|
| 14266 | } | 
|---|
| 14267 |  | 
|---|
| 14268 | return -EINVAL; | 
|---|
| 14269 | } | 
|---|
| 14270 |  | 
|---|
| 14271 | static int nl80211_join_ocb(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 14272 | { | 
|---|
| 14273 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 14274 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 14275 | struct ocb_setup setup = {}; | 
|---|
| 14276 | int err; | 
|---|
| 14277 |  | 
|---|
| 14278 | err = nl80211_parse_chandef(rdev, info, chandef: &setup.chandef); | 
|---|
| 14279 | if (err) | 
|---|
| 14280 | return err; | 
|---|
| 14281 |  | 
|---|
| 14282 | return cfg80211_join_ocb(rdev, dev, setup: &setup); | 
|---|
| 14283 | } | 
|---|
| 14284 |  | 
|---|
| 14285 | static int nl80211_leave_ocb(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 14286 | { | 
|---|
| 14287 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 14288 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 14289 |  | 
|---|
| 14290 | return cfg80211_leave_ocb(rdev, dev); | 
|---|
| 14291 | } | 
|---|
| 14292 |  | 
|---|
| 14293 | static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 14294 | { | 
|---|
| 14295 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 14296 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 14297 | struct mesh_config cfg; | 
|---|
| 14298 | struct mesh_setup setup; | 
|---|
| 14299 | int err; | 
|---|
| 14300 |  | 
|---|
| 14301 | /* start with default */ | 
|---|
| 14302 | memcpy(to: &cfg, from: &default_mesh_config, len: sizeof(cfg)); | 
|---|
| 14303 | memcpy(to: &setup, from: &default_mesh_setup, len: sizeof(setup)); | 
|---|
| 14304 |  | 
|---|
| 14305 | if (info->attrs[NL80211_ATTR_MESH_CONFIG]) { | 
|---|
| 14306 | /* and parse parameters if given */ | 
|---|
| 14307 | err = nl80211_parse_mesh_config(info, cfg: &cfg, NULL); | 
|---|
| 14308 | if (err) | 
|---|
| 14309 | return err; | 
|---|
| 14310 | } | 
|---|
| 14311 |  | 
|---|
| 14312 | if (!info->attrs[NL80211_ATTR_MESH_ID] || | 
|---|
| 14313 | !nla_len(nla: info->attrs[NL80211_ATTR_MESH_ID])) | 
|---|
| 14314 | return -EINVAL; | 
|---|
| 14315 |  | 
|---|
| 14316 | setup.mesh_id = nla_data(nla: info->attrs[NL80211_ATTR_MESH_ID]); | 
|---|
| 14317 | setup.mesh_id_len = nla_len(nla: info->attrs[NL80211_ATTR_MESH_ID]); | 
|---|
| 14318 |  | 
|---|
| 14319 | if (info->attrs[NL80211_ATTR_MCAST_RATE] && | 
|---|
| 14320 | !nl80211_parse_mcast_rate(rdev, mcast_rate: setup.mcast_rate, | 
|---|
| 14321 | rateval: nla_get_u32(nla: info->attrs[NL80211_ATTR_MCAST_RATE]))) | 
|---|
| 14322 | return -EINVAL; | 
|---|
| 14323 |  | 
|---|
| 14324 | if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) { | 
|---|
| 14325 | setup.beacon_interval = | 
|---|
| 14326 | nla_get_u32(nla: info->attrs[NL80211_ATTR_BEACON_INTERVAL]); | 
|---|
| 14327 |  | 
|---|
| 14328 | err = cfg80211_validate_beacon_int(rdev, | 
|---|
| 14329 | iftype: NL80211_IFTYPE_MESH_POINT, | 
|---|
| 14330 | beacon_int: setup.beacon_interval); | 
|---|
| 14331 | if (err) | 
|---|
| 14332 | return err; | 
|---|
| 14333 | } | 
|---|
| 14334 |  | 
|---|
| 14335 | if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) { | 
|---|
| 14336 | setup.dtim_period = | 
|---|
| 14337 | nla_get_u32(nla: info->attrs[NL80211_ATTR_DTIM_PERIOD]); | 
|---|
| 14338 | if (setup.dtim_period < 1 || setup.dtim_period > 100) | 
|---|
| 14339 | return -EINVAL; | 
|---|
| 14340 | } | 
|---|
| 14341 |  | 
|---|
| 14342 | if (info->attrs[NL80211_ATTR_MESH_SETUP]) { | 
|---|
| 14343 | /* parse additional setup parameters if given */ | 
|---|
| 14344 | err = nl80211_parse_mesh_setup(info, setup: &setup); | 
|---|
| 14345 | if (err) | 
|---|
| 14346 | return err; | 
|---|
| 14347 | } | 
|---|
| 14348 |  | 
|---|
| 14349 | if (setup.user_mpm) | 
|---|
| 14350 | cfg.auto_open_plinks = false; | 
|---|
| 14351 |  | 
|---|
| 14352 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { | 
|---|
| 14353 | err = nl80211_parse_chandef(rdev, info, chandef: &setup.chandef); | 
|---|
| 14354 | if (err) | 
|---|
| 14355 | return err; | 
|---|
| 14356 | } else { | 
|---|
| 14357 | /* __cfg80211_join_mesh() will sort it out */ | 
|---|
| 14358 | setup.chandef.chan = NULL; | 
|---|
| 14359 | } | 
|---|
| 14360 |  | 
|---|
| 14361 | if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { | 
|---|
| 14362 | u8 *rates = nla_data(nla: info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); | 
|---|
| 14363 | int n_rates = | 
|---|
| 14364 | nla_len(nla: info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); | 
|---|
| 14365 | struct ieee80211_supported_band *sband; | 
|---|
| 14366 |  | 
|---|
| 14367 | if (!setup.chandef.chan) | 
|---|
| 14368 | return -EINVAL; | 
|---|
| 14369 |  | 
|---|
| 14370 | sband = rdev->wiphy.bands[setup.chandef.chan->band]; | 
|---|
| 14371 |  | 
|---|
| 14372 | err = ieee80211_get_ratemask(sband, rates, n_rates, | 
|---|
| 14373 | mask: &setup.basic_rates); | 
|---|
| 14374 | if (err) | 
|---|
| 14375 | return err; | 
|---|
| 14376 | } | 
|---|
| 14377 |  | 
|---|
| 14378 | if (info->attrs[NL80211_ATTR_TX_RATES]) { | 
|---|
| 14379 | err = nl80211_parse_tx_bitrate_mask(info, attrs: info->attrs, | 
|---|
| 14380 | attr: NL80211_ATTR_TX_RATES, | 
|---|
| 14381 | mask: &setup.beacon_rate, | 
|---|
| 14382 | dev, default_all_enabled: false, link_id: 0); | 
|---|
| 14383 | if (err) | 
|---|
| 14384 | return err; | 
|---|
| 14385 |  | 
|---|
| 14386 | if (!setup.chandef.chan) | 
|---|
| 14387 | return -EINVAL; | 
|---|
| 14388 |  | 
|---|
| 14389 | err = validate_beacon_tx_rate(rdev, band: setup.chandef.chan->band, | 
|---|
| 14390 | beacon_rate: &setup.beacon_rate); | 
|---|
| 14391 | if (err) | 
|---|
| 14392 | return err; | 
|---|
| 14393 | } | 
|---|
| 14394 |  | 
|---|
| 14395 | setup.userspace_handles_dfs = | 
|---|
| 14396 | nla_get_flag(nla: info->attrs[NL80211_ATTR_HANDLE_DFS]); | 
|---|
| 14397 |  | 
|---|
| 14398 | if (info->attrs[NL80211_ATTR_CONTROL_PORT_OVER_NL80211]) { | 
|---|
| 14399 | int r = validate_pae_over_nl80211(rdev, info); | 
|---|
| 14400 |  | 
|---|
| 14401 | if (r < 0) | 
|---|
| 14402 | return r; | 
|---|
| 14403 |  | 
|---|
| 14404 | setup.control_port_over_nl80211 = true; | 
|---|
| 14405 | } | 
|---|
| 14406 |  | 
|---|
| 14407 | err = __cfg80211_join_mesh(rdev, dev, setup: &setup, conf: &cfg); | 
|---|
| 14408 | if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) | 
|---|
| 14409 | dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid; | 
|---|
| 14410 |  | 
|---|
| 14411 | return err; | 
|---|
| 14412 | } | 
|---|
| 14413 |  | 
|---|
| 14414 | static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 14415 | { | 
|---|
| 14416 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 14417 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 14418 |  | 
|---|
| 14419 | return cfg80211_leave_mesh(rdev, dev); | 
|---|
| 14420 | } | 
|---|
| 14421 |  | 
|---|
| 14422 | #ifdef CONFIG_PM | 
|---|
| 14423 | static int nl80211_send_wowlan_patterns(struct sk_buff *msg, | 
|---|
| 14424 | struct cfg80211_registered_device *rdev) | 
|---|
| 14425 | { | 
|---|
| 14426 | struct cfg80211_wowlan *wowlan = rdev->wiphy.wowlan_config; | 
|---|
| 14427 | struct nlattr *nl_pats, *nl_pat; | 
|---|
| 14428 | int i, pat_len; | 
|---|
| 14429 |  | 
|---|
| 14430 | if (!wowlan->n_patterns) | 
|---|
| 14431 | return 0; | 
|---|
| 14432 |  | 
|---|
| 14433 | nl_pats = nla_nest_start_noflag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_PKT_PATTERN); | 
|---|
| 14434 | if (!nl_pats) | 
|---|
| 14435 | return -ENOBUFS; | 
|---|
| 14436 |  | 
|---|
| 14437 | for (i = 0; i < wowlan->n_patterns; i++) { | 
|---|
| 14438 | nl_pat = nla_nest_start_noflag(skb: msg, attrtype: i + 1); | 
|---|
| 14439 | if (!nl_pat) | 
|---|
| 14440 | return -ENOBUFS; | 
|---|
| 14441 | pat_len = wowlan->patterns[i].pattern_len; | 
|---|
| 14442 | if (nla_put(skb: msg, attrtype: NL80211_PKTPAT_MASK, DIV_ROUND_UP(pat_len, 8), | 
|---|
| 14443 | data: wowlan->patterns[i].mask) || | 
|---|
| 14444 | nla_put(skb: msg, attrtype: NL80211_PKTPAT_PATTERN, attrlen: pat_len, | 
|---|
| 14445 | data: wowlan->patterns[i].pattern) || | 
|---|
| 14446 | nla_put_u32(skb: msg, attrtype: NL80211_PKTPAT_OFFSET, | 
|---|
| 14447 | value: wowlan->patterns[i].pkt_offset)) | 
|---|
| 14448 | return -ENOBUFS; | 
|---|
| 14449 | nla_nest_end(skb: msg, start: nl_pat); | 
|---|
| 14450 | } | 
|---|
| 14451 | nla_nest_end(skb: msg, start: nl_pats); | 
|---|
| 14452 |  | 
|---|
| 14453 | return 0; | 
|---|
| 14454 | } | 
|---|
| 14455 |  | 
|---|
| 14456 | static int nl80211_send_wowlan_tcp(struct sk_buff *msg, | 
|---|
| 14457 | struct cfg80211_wowlan_tcp *tcp) | 
|---|
| 14458 | { | 
|---|
| 14459 | struct nlattr *nl_tcp; | 
|---|
| 14460 |  | 
|---|
| 14461 | if (!tcp) | 
|---|
| 14462 | return 0; | 
|---|
| 14463 |  | 
|---|
| 14464 | nl_tcp = nla_nest_start_noflag(skb: msg, | 
|---|
| 14465 | attrtype: NL80211_WOWLAN_TRIG_TCP_CONNECTION); | 
|---|
| 14466 | if (!nl_tcp) | 
|---|
| 14467 | return -ENOBUFS; | 
|---|
| 14468 |  | 
|---|
| 14469 | if (nla_put_in_addr(skb: msg, attrtype: NL80211_WOWLAN_TCP_SRC_IPV4, addr: tcp->src) || | 
|---|
| 14470 | nla_put_in_addr(skb: msg, attrtype: NL80211_WOWLAN_TCP_DST_IPV4, addr: tcp->dst) || | 
|---|
| 14471 | nla_put(skb: msg, attrtype: NL80211_WOWLAN_TCP_DST_MAC, ETH_ALEN, data: tcp->dst_mac) || | 
|---|
| 14472 | nla_put_u16(skb: msg, attrtype: NL80211_WOWLAN_TCP_SRC_PORT, value: tcp->src_port) || | 
|---|
| 14473 | nla_put_u16(skb: msg, attrtype: NL80211_WOWLAN_TCP_DST_PORT, value: tcp->dst_port) || | 
|---|
| 14474 | nla_put(skb: msg, attrtype: NL80211_WOWLAN_TCP_DATA_PAYLOAD, | 
|---|
| 14475 | attrlen: tcp->payload_len, data: tcp->payload) || | 
|---|
| 14476 | nla_put_u32(skb: msg, attrtype: NL80211_WOWLAN_TCP_DATA_INTERVAL, | 
|---|
| 14477 | value: tcp->data_interval) || | 
|---|
| 14478 | nla_put(skb: msg, attrtype: NL80211_WOWLAN_TCP_WAKE_PAYLOAD, | 
|---|
| 14479 | attrlen: tcp->wake_len, data: tcp->wake_data) || | 
|---|
| 14480 | nla_put(skb: msg, attrtype: NL80211_WOWLAN_TCP_WAKE_MASK, | 
|---|
| 14481 | DIV_ROUND_UP(tcp->wake_len, 8), data: tcp->wake_mask)) | 
|---|
| 14482 | return -ENOBUFS; | 
|---|
| 14483 |  | 
|---|
| 14484 | if (tcp->payload_seq.len && | 
|---|
| 14485 | nla_put(skb: msg, attrtype: NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ, | 
|---|
| 14486 | attrlen: sizeof(tcp->payload_seq), data: &tcp->payload_seq)) | 
|---|
| 14487 | return -ENOBUFS; | 
|---|
| 14488 |  | 
|---|
| 14489 | if (tcp->payload_tok.len && | 
|---|
| 14490 | nla_put(skb: msg, attrtype: NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, | 
|---|
| 14491 | attrlen: sizeof(tcp->payload_tok) + tcp->tokens_size, | 
|---|
| 14492 | data: &tcp->payload_tok)) | 
|---|
| 14493 | return -ENOBUFS; | 
|---|
| 14494 |  | 
|---|
| 14495 | nla_nest_end(skb: msg, start: nl_tcp); | 
|---|
| 14496 |  | 
|---|
| 14497 | return 0; | 
|---|
| 14498 | } | 
|---|
| 14499 |  | 
|---|
| 14500 | static int nl80211_send_wowlan_nd(struct sk_buff *msg, | 
|---|
| 14501 | struct cfg80211_sched_scan_request *req) | 
|---|
| 14502 | { | 
|---|
| 14503 | struct nlattr *nd, *freqs, *matches, *match, *scan_plans, *scan_plan; | 
|---|
| 14504 | int i; | 
|---|
| 14505 |  | 
|---|
| 14506 | if (!req) | 
|---|
| 14507 | return 0; | 
|---|
| 14508 |  | 
|---|
| 14509 | nd = nla_nest_start_noflag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_NET_DETECT); | 
|---|
| 14510 | if (!nd) | 
|---|
| 14511 | return -ENOBUFS; | 
|---|
| 14512 |  | 
|---|
| 14513 | if (req->n_scan_plans == 1 && | 
|---|
| 14514 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_SCHED_SCAN_INTERVAL, | 
|---|
| 14515 | value: req->scan_plans[0].interval * 1000)) | 
|---|
| 14516 | return -ENOBUFS; | 
|---|
| 14517 |  | 
|---|
| 14518 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_SCHED_SCAN_DELAY, value: req->delay)) | 
|---|
| 14519 | return -ENOBUFS; | 
|---|
| 14520 |  | 
|---|
| 14521 | if (req->relative_rssi_set) { | 
|---|
| 14522 | struct nl80211_bss_select_rssi_adjust ; | 
|---|
| 14523 |  | 
|---|
| 14524 | if (nla_put_s8(skb: msg, attrtype: NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI, | 
|---|
| 14525 | value: req->relative_rssi)) | 
|---|
| 14526 | return -ENOBUFS; | 
|---|
| 14527 |  | 
|---|
| 14528 | rssi_adjust.band = req->rssi_adjust.band; | 
|---|
| 14529 | rssi_adjust.delta = req->rssi_adjust.delta; | 
|---|
| 14530 | if (nla_put(skb: msg, attrtype: NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST, | 
|---|
| 14531 | attrlen: sizeof(rssi_adjust), data: &rssi_adjust)) | 
|---|
| 14532 | return -ENOBUFS; | 
|---|
| 14533 | } | 
|---|
| 14534 |  | 
|---|
| 14535 | freqs = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_SCAN_FREQUENCIES); | 
|---|
| 14536 | if (!freqs) | 
|---|
| 14537 | return -ENOBUFS; | 
|---|
| 14538 |  | 
|---|
| 14539 | for (i = 0; i < req->n_channels; i++) { | 
|---|
| 14540 | if (nla_put_u32(skb: msg, attrtype: i, value: req->channels[i]->center_freq)) | 
|---|
| 14541 | return -ENOBUFS; | 
|---|
| 14542 | } | 
|---|
| 14543 |  | 
|---|
| 14544 | nla_nest_end(skb: msg, start: freqs); | 
|---|
| 14545 |  | 
|---|
| 14546 | if (req->n_match_sets) { | 
|---|
| 14547 | matches = nla_nest_start_noflag(skb: msg, | 
|---|
| 14548 | attrtype: NL80211_ATTR_SCHED_SCAN_MATCH); | 
|---|
| 14549 | if (!matches) | 
|---|
| 14550 | return -ENOBUFS; | 
|---|
| 14551 |  | 
|---|
| 14552 | for (i = 0; i < req->n_match_sets; i++) { | 
|---|
| 14553 | match = nla_nest_start_noflag(skb: msg, attrtype: i); | 
|---|
| 14554 | if (!match) | 
|---|
| 14555 | return -ENOBUFS; | 
|---|
| 14556 |  | 
|---|
| 14557 | if (nla_put(skb: msg, attrtype: NL80211_SCHED_SCAN_MATCH_ATTR_SSID, | 
|---|
| 14558 | attrlen: req->match_sets[i].ssid.ssid_len, | 
|---|
| 14559 | data: req->match_sets[i].ssid.ssid)) | 
|---|
| 14560 | return -ENOBUFS; | 
|---|
| 14561 | nla_nest_end(skb: msg, start: match); | 
|---|
| 14562 | } | 
|---|
| 14563 | nla_nest_end(skb: msg, start: matches); | 
|---|
| 14564 | } | 
|---|
| 14565 |  | 
|---|
| 14566 | scan_plans = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_SCHED_SCAN_PLANS); | 
|---|
| 14567 | if (!scan_plans) | 
|---|
| 14568 | return -ENOBUFS; | 
|---|
| 14569 |  | 
|---|
| 14570 | for (i = 0; i < req->n_scan_plans; i++) { | 
|---|
| 14571 | scan_plan = nla_nest_start_noflag(skb: msg, attrtype: i + 1); | 
|---|
| 14572 | if (!scan_plan) | 
|---|
| 14573 | return -ENOBUFS; | 
|---|
| 14574 |  | 
|---|
| 14575 | if (nla_put_u32(skb: msg, attrtype: NL80211_SCHED_SCAN_PLAN_INTERVAL, | 
|---|
| 14576 | value: req->scan_plans[i].interval) || | 
|---|
| 14577 | (req->scan_plans[i].iterations && | 
|---|
| 14578 | nla_put_u32(skb: msg, attrtype: NL80211_SCHED_SCAN_PLAN_ITERATIONS, | 
|---|
| 14579 | value: req->scan_plans[i].iterations))) | 
|---|
| 14580 | return -ENOBUFS; | 
|---|
| 14581 | nla_nest_end(skb: msg, start: scan_plan); | 
|---|
| 14582 | } | 
|---|
| 14583 | nla_nest_end(skb: msg, start: scan_plans); | 
|---|
| 14584 |  | 
|---|
| 14585 | nla_nest_end(skb: msg, start: nd); | 
|---|
| 14586 |  | 
|---|
| 14587 | return 0; | 
|---|
| 14588 | } | 
|---|
| 14589 |  | 
|---|
| 14590 | static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 14591 | { | 
|---|
| 14592 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 14593 | struct sk_buff *msg; | 
|---|
| 14594 | void *hdr; | 
|---|
| 14595 | u32 size = NLMSG_DEFAULT_SIZE; | 
|---|
| 14596 |  | 
|---|
| 14597 | if (!rdev->wiphy.wowlan) | 
|---|
| 14598 | return -EOPNOTSUPP; | 
|---|
| 14599 |  | 
|---|
| 14600 | if (rdev->wiphy.wowlan_config && rdev->wiphy.wowlan_config->tcp) { | 
|---|
| 14601 | /* adjust size to have room for all the data */ | 
|---|
| 14602 | size += rdev->wiphy.wowlan_config->tcp->tokens_size + | 
|---|
| 14603 | rdev->wiphy.wowlan_config->tcp->payload_len + | 
|---|
| 14604 | rdev->wiphy.wowlan_config->tcp->wake_len + | 
|---|
| 14605 | rdev->wiphy.wowlan_config->tcp->wake_len / 8; | 
|---|
| 14606 | } | 
|---|
| 14607 |  | 
|---|
| 14608 | msg = nlmsg_new(payload: size, GFP_KERNEL); | 
|---|
| 14609 | if (!msg) | 
|---|
| 14610 | return -ENOMEM; | 
|---|
| 14611 |  | 
|---|
| 14612 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, | 
|---|
| 14613 | cmd: NL80211_CMD_GET_WOWLAN); | 
|---|
| 14614 | if (!hdr) | 
|---|
| 14615 | goto nla_put_failure; | 
|---|
| 14616 |  | 
|---|
| 14617 | if (rdev->wiphy.wowlan_config) { | 
|---|
| 14618 | struct nlattr *nl_wowlan; | 
|---|
| 14619 |  | 
|---|
| 14620 | nl_wowlan = nla_nest_start_noflag(skb: msg, | 
|---|
| 14621 | attrtype: NL80211_ATTR_WOWLAN_TRIGGERS); | 
|---|
| 14622 | if (!nl_wowlan) | 
|---|
| 14623 | goto nla_put_failure; | 
|---|
| 14624 |  | 
|---|
| 14625 | if ((rdev->wiphy.wowlan_config->any && | 
|---|
| 14626 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_ANY)) || | 
|---|
| 14627 | (rdev->wiphy.wowlan_config->disconnect && | 
|---|
| 14628 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_DISCONNECT)) || | 
|---|
| 14629 | (rdev->wiphy.wowlan_config->magic_pkt && | 
|---|
| 14630 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_MAGIC_PKT)) || | 
|---|
| 14631 | (rdev->wiphy.wowlan_config->gtk_rekey_failure && | 
|---|
| 14632 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) || | 
|---|
| 14633 | (rdev->wiphy.wowlan_config->eap_identity_req && | 
|---|
| 14634 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) || | 
|---|
| 14635 | (rdev->wiphy.wowlan_config->four_way_handshake && | 
|---|
| 14636 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) || | 
|---|
| 14637 | (rdev->wiphy.wowlan_config->rfkill_release && | 
|---|
| 14638 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) | 
|---|
| 14639 | goto nla_put_failure; | 
|---|
| 14640 |  | 
|---|
| 14641 | if (nl80211_send_wowlan_patterns(msg, rdev)) | 
|---|
| 14642 | goto nla_put_failure; | 
|---|
| 14643 |  | 
|---|
| 14644 | if (nl80211_send_wowlan_tcp(msg, | 
|---|
| 14645 | tcp: rdev->wiphy.wowlan_config->tcp)) | 
|---|
| 14646 | goto nla_put_failure; | 
|---|
| 14647 |  | 
|---|
| 14648 | if (nl80211_send_wowlan_nd( | 
|---|
| 14649 | msg, | 
|---|
| 14650 | req: rdev->wiphy.wowlan_config->nd_config)) | 
|---|
| 14651 | goto nla_put_failure; | 
|---|
| 14652 |  | 
|---|
| 14653 | nla_nest_end(skb: msg, start: nl_wowlan); | 
|---|
| 14654 | } | 
|---|
| 14655 |  | 
|---|
| 14656 | genlmsg_end(skb: msg, hdr); | 
|---|
| 14657 | return genlmsg_reply(skb: msg, info); | 
|---|
| 14658 |  | 
|---|
| 14659 | nla_put_failure: | 
|---|
| 14660 | nlmsg_free(skb: msg); | 
|---|
| 14661 | return -ENOBUFS; | 
|---|
| 14662 | } | 
|---|
| 14663 |  | 
|---|
| 14664 | static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev, | 
|---|
| 14665 | struct nlattr *attr, | 
|---|
| 14666 | struct cfg80211_wowlan *trig) | 
|---|
| 14667 | { | 
|---|
| 14668 | struct nlattr *tb[NUM_NL80211_WOWLAN_TCP]; | 
|---|
| 14669 | struct cfg80211_wowlan_tcp *cfg; | 
|---|
| 14670 | struct nl80211_wowlan_tcp_data_token *tok = NULL; | 
|---|
| 14671 | struct nl80211_wowlan_tcp_data_seq *seq = NULL; | 
|---|
| 14672 | u32 size; | 
|---|
| 14673 | u32 data_size, wake_size, tokens_size = 0, wake_mask_size; | 
|---|
| 14674 | int err, port; | 
|---|
| 14675 |  | 
|---|
| 14676 | if (!rdev->wiphy.wowlan->tcp) | 
|---|
| 14677 | return -EINVAL; | 
|---|
| 14678 |  | 
|---|
| 14679 | err = nla_parse_nested_deprecated(tb, maxtype: MAX_NL80211_WOWLAN_TCP, nla: attr, | 
|---|
| 14680 | policy: nl80211_wowlan_tcp_policy, NULL); | 
|---|
| 14681 | if (err) | 
|---|
| 14682 | return err; | 
|---|
| 14683 |  | 
|---|
| 14684 | if (!tb[NL80211_WOWLAN_TCP_SRC_IPV4] || | 
|---|
| 14685 | !tb[NL80211_WOWLAN_TCP_DST_IPV4] || | 
|---|
| 14686 | !tb[NL80211_WOWLAN_TCP_DST_MAC] || | 
|---|
| 14687 | !tb[NL80211_WOWLAN_TCP_DST_PORT] || | 
|---|
| 14688 | !tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD] || | 
|---|
| 14689 | !tb[NL80211_WOWLAN_TCP_DATA_INTERVAL] || | 
|---|
| 14690 | !tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD] || | 
|---|
| 14691 | !tb[NL80211_WOWLAN_TCP_WAKE_MASK]) | 
|---|
| 14692 | return -EINVAL; | 
|---|
| 14693 |  | 
|---|
| 14694 | data_size = nla_len(nla: tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]); | 
|---|
| 14695 | if (data_size > rdev->wiphy.wowlan->tcp->data_payload_max) | 
|---|
| 14696 | return -EINVAL; | 
|---|
| 14697 |  | 
|---|
| 14698 | if (nla_get_u32(nla: tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) > | 
|---|
| 14699 | rdev->wiphy.wowlan->tcp->data_interval_max || | 
|---|
| 14700 | nla_get_u32(nla: tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) == 0) | 
|---|
| 14701 | return -EINVAL; | 
|---|
| 14702 |  | 
|---|
| 14703 | wake_size = nla_len(nla: tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]); | 
|---|
| 14704 | if (wake_size > rdev->wiphy.wowlan->tcp->wake_payload_max) | 
|---|
| 14705 | return -EINVAL; | 
|---|
| 14706 |  | 
|---|
| 14707 | wake_mask_size = nla_len(nla: tb[NL80211_WOWLAN_TCP_WAKE_MASK]); | 
|---|
| 14708 | if (wake_mask_size != DIV_ROUND_UP(wake_size, 8)) | 
|---|
| 14709 | return -EINVAL; | 
|---|
| 14710 |  | 
|---|
| 14711 | if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]) { | 
|---|
| 14712 | u32 tokln = nla_len(nla: tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]); | 
|---|
| 14713 |  | 
|---|
| 14714 | tok = nla_data(nla: tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]); | 
|---|
| 14715 | tokens_size = tokln - sizeof(*tok); | 
|---|
| 14716 |  | 
|---|
| 14717 | if (!tok->len || tokens_size % tok->len) | 
|---|
| 14718 | return -EINVAL; | 
|---|
| 14719 | if (!rdev->wiphy.wowlan->tcp->tok) | 
|---|
| 14720 | return -EINVAL; | 
|---|
| 14721 | if (tok->len > rdev->wiphy.wowlan->tcp->tok->max_len) | 
|---|
| 14722 | return -EINVAL; | 
|---|
| 14723 | if (tok->len < rdev->wiphy.wowlan->tcp->tok->min_len) | 
|---|
| 14724 | return -EINVAL; | 
|---|
| 14725 | if (tokens_size > rdev->wiphy.wowlan->tcp->tok->bufsize) | 
|---|
| 14726 | return -EINVAL; | 
|---|
| 14727 | if (tok->offset + tok->len > data_size) | 
|---|
| 14728 | return -EINVAL; | 
|---|
| 14729 | } | 
|---|
| 14730 |  | 
|---|
| 14731 | if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]) { | 
|---|
| 14732 | seq = nla_data(nla: tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]); | 
|---|
| 14733 | if (!rdev->wiphy.wowlan->tcp->seq) | 
|---|
| 14734 | return -EINVAL; | 
|---|
| 14735 | if (seq->len == 0 || seq->len > 4) | 
|---|
| 14736 | return -EINVAL; | 
|---|
| 14737 | if (seq->len + seq->offset > data_size) | 
|---|
| 14738 | return -EINVAL; | 
|---|
| 14739 | } | 
|---|
| 14740 |  | 
|---|
| 14741 | size = sizeof(*cfg); | 
|---|
| 14742 | size += data_size; | 
|---|
| 14743 | size += wake_size + wake_mask_size; | 
|---|
| 14744 | size += tokens_size; | 
|---|
| 14745 |  | 
|---|
| 14746 | cfg = kzalloc(size, GFP_KERNEL); | 
|---|
| 14747 | if (!cfg) | 
|---|
| 14748 | return -ENOMEM; | 
|---|
| 14749 | cfg->src = nla_get_in_addr(nla: tb[NL80211_WOWLAN_TCP_SRC_IPV4]); | 
|---|
| 14750 | cfg->dst = nla_get_in_addr(nla: tb[NL80211_WOWLAN_TCP_DST_IPV4]); | 
|---|
| 14751 | memcpy(to: cfg->dst_mac, from: nla_data(nla: tb[NL80211_WOWLAN_TCP_DST_MAC]), | 
|---|
| 14752 | ETH_ALEN); | 
|---|
| 14753 | port = nla_get_u16_default(nla: tb[NL80211_WOWLAN_TCP_SRC_PORT], defvalue: 0); | 
|---|
| 14754 | #ifdef CONFIG_INET | 
|---|
| 14755 | /* allocate a socket and port for it and use it */ | 
|---|
| 14756 | err = __sock_create(net: wiphy_net(wiphy: &rdev->wiphy), PF_INET, type: SOCK_STREAM, | 
|---|
| 14757 | IPPROTO_TCP, res: &cfg->sock, kern: 1); | 
|---|
| 14758 | if (err) { | 
|---|
| 14759 | kfree(objp: cfg); | 
|---|
| 14760 | return err; | 
|---|
| 14761 | } | 
|---|
| 14762 | if (inet_csk_get_port(sk: cfg->sock->sk, snum: port)) { | 
|---|
| 14763 | sock_release(sock: cfg->sock); | 
|---|
| 14764 | kfree(objp: cfg); | 
|---|
| 14765 | return -EADDRINUSE; | 
|---|
| 14766 | } | 
|---|
| 14767 | cfg->src_port = inet_sk(cfg->sock->sk)->inet_num; | 
|---|
| 14768 | #else | 
|---|
| 14769 | if (!port) { | 
|---|
| 14770 | kfree(cfg); | 
|---|
| 14771 | return -EINVAL; | 
|---|
| 14772 | } | 
|---|
| 14773 | cfg->src_port = port; | 
|---|
| 14774 | #endif | 
|---|
| 14775 |  | 
|---|
| 14776 | cfg->dst_port = nla_get_u16(nla: tb[NL80211_WOWLAN_TCP_DST_PORT]); | 
|---|
| 14777 | cfg->payload_len = data_size; | 
|---|
| 14778 | cfg->payload = (u8 *)cfg + sizeof(*cfg) + tokens_size; | 
|---|
| 14779 | memcpy(to: (void *)cfg->payload, | 
|---|
| 14780 | from: nla_data(nla: tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]), | 
|---|
| 14781 | len: data_size); | 
|---|
| 14782 | if (seq) | 
|---|
| 14783 | cfg->payload_seq = *seq; | 
|---|
| 14784 | cfg->data_interval = nla_get_u32(nla: tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]); | 
|---|
| 14785 | cfg->wake_len = wake_size; | 
|---|
| 14786 | cfg->wake_data = (u8 *)cfg + sizeof(*cfg) + tokens_size + data_size; | 
|---|
| 14787 | memcpy(to: (void *)cfg->wake_data, | 
|---|
| 14788 | from: nla_data(nla: tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]), | 
|---|
| 14789 | len: wake_size); | 
|---|
| 14790 | cfg->wake_mask = (u8 *)cfg + sizeof(*cfg) + tokens_size + | 
|---|
| 14791 | data_size + wake_size; | 
|---|
| 14792 | memcpy(to: (void *)cfg->wake_mask, | 
|---|
| 14793 | from: nla_data(nla: tb[NL80211_WOWLAN_TCP_WAKE_MASK]), | 
|---|
| 14794 | len: wake_mask_size); | 
|---|
| 14795 | if (tok) { | 
|---|
| 14796 | cfg->tokens_size = tokens_size; | 
|---|
| 14797 | cfg->payload_tok = *tok; | 
|---|
| 14798 | memcpy(to: cfg->payload_tok.token_stream, from: tok->token_stream, | 
|---|
| 14799 | len: tokens_size); | 
|---|
| 14800 | } | 
|---|
| 14801 |  | 
|---|
| 14802 | trig->tcp = cfg; | 
|---|
| 14803 |  | 
|---|
| 14804 | return 0; | 
|---|
| 14805 | } | 
|---|
| 14806 |  | 
|---|
| 14807 | static int nl80211_parse_wowlan_nd(struct cfg80211_registered_device *rdev, | 
|---|
| 14808 | const struct wiphy_wowlan_support *wowlan, | 
|---|
| 14809 | struct nlattr *attr, | 
|---|
| 14810 | struct cfg80211_wowlan *trig) | 
|---|
| 14811 | { | 
|---|
| 14812 | struct nlattr **tb; | 
|---|
| 14813 | int err; | 
|---|
| 14814 |  | 
|---|
| 14815 | tb = kcalloc(NUM_NL80211_ATTR, sizeof(*tb), GFP_KERNEL); | 
|---|
| 14816 | if (!tb) | 
|---|
| 14817 | return -ENOMEM; | 
|---|
| 14818 |  | 
|---|
| 14819 | if (!(wowlan->flags & WIPHY_WOWLAN_NET_DETECT)) { | 
|---|
| 14820 | err = -EOPNOTSUPP; | 
|---|
| 14821 | goto out; | 
|---|
| 14822 | } | 
|---|
| 14823 |  | 
|---|
| 14824 | err = nla_parse_nested_deprecated(tb, maxtype: NL80211_ATTR_MAX, nla: attr, | 
|---|
| 14825 | policy: nl80211_policy, NULL); | 
|---|
| 14826 | if (err) | 
|---|
| 14827 | goto out; | 
|---|
| 14828 |  | 
|---|
| 14829 | trig->nd_config = nl80211_parse_sched_scan(wiphy: &rdev->wiphy, NULL, attrs: tb, | 
|---|
| 14830 | max_match_sets: wowlan->max_nd_match_sets); | 
|---|
| 14831 | err = PTR_ERR_OR_ZERO(ptr: trig->nd_config); | 
|---|
| 14832 | if (err) | 
|---|
| 14833 | trig->nd_config = NULL; | 
|---|
| 14834 |  | 
|---|
| 14835 | out: | 
|---|
| 14836 | kfree(objp: tb); | 
|---|
| 14837 | return err; | 
|---|
| 14838 | } | 
|---|
| 14839 |  | 
|---|
| 14840 | static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 14841 | { | 
|---|
| 14842 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 14843 | struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG]; | 
|---|
| 14844 | struct cfg80211_wowlan new_triggers = {}; | 
|---|
| 14845 | struct cfg80211_wowlan *ntrig; | 
|---|
| 14846 | const struct wiphy_wowlan_support *wowlan = rdev->wiphy.wowlan; | 
|---|
| 14847 | int err, i; | 
|---|
| 14848 | bool prev_enabled = rdev->wiphy.wowlan_config; | 
|---|
| 14849 | bool regular = false; | 
|---|
| 14850 |  | 
|---|
| 14851 | if (!wowlan) | 
|---|
| 14852 | return -EOPNOTSUPP; | 
|---|
| 14853 |  | 
|---|
| 14854 | if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) { | 
|---|
| 14855 | cfg80211_rdev_free_wowlan(rdev); | 
|---|
| 14856 | rdev->wiphy.wowlan_config = NULL; | 
|---|
| 14857 | goto set_wakeup; | 
|---|
| 14858 | } | 
|---|
| 14859 |  | 
|---|
| 14860 | err = nla_parse_nested_deprecated(tb, maxtype: MAX_NL80211_WOWLAN_TRIG, | 
|---|
| 14861 | nla: info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS], | 
|---|
| 14862 | policy: nl80211_wowlan_policy, extack: info->extack); | 
|---|
| 14863 | if (err) | 
|---|
| 14864 | return err; | 
|---|
| 14865 |  | 
|---|
| 14866 | if (tb[NL80211_WOWLAN_TRIG_ANY]) { | 
|---|
| 14867 | if (!(wowlan->flags & WIPHY_WOWLAN_ANY)) | 
|---|
| 14868 | return -EINVAL; | 
|---|
| 14869 | new_triggers.any = true; | 
|---|
| 14870 | } | 
|---|
| 14871 |  | 
|---|
| 14872 | if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) { | 
|---|
| 14873 | if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT)) | 
|---|
| 14874 | return -EINVAL; | 
|---|
| 14875 | new_triggers.disconnect = true; | 
|---|
| 14876 | regular = true; | 
|---|
| 14877 | } | 
|---|
| 14878 |  | 
|---|
| 14879 | if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) { | 
|---|
| 14880 | if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT)) | 
|---|
| 14881 | return -EINVAL; | 
|---|
| 14882 | new_triggers.magic_pkt = true; | 
|---|
| 14883 | regular = true; | 
|---|
| 14884 | } | 
|---|
| 14885 |  | 
|---|
| 14886 | if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED]) | 
|---|
| 14887 | return -EINVAL; | 
|---|
| 14888 |  | 
|---|
| 14889 | if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) { | 
|---|
| 14890 | if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE)) | 
|---|
| 14891 | return -EINVAL; | 
|---|
| 14892 | new_triggers.gtk_rekey_failure = true; | 
|---|
| 14893 | regular = true; | 
|---|
| 14894 | } | 
|---|
| 14895 |  | 
|---|
| 14896 | if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) { | 
|---|
| 14897 | if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ)) | 
|---|
| 14898 | return -EINVAL; | 
|---|
| 14899 | new_triggers.eap_identity_req = true; | 
|---|
| 14900 | regular = true; | 
|---|
| 14901 | } | 
|---|
| 14902 |  | 
|---|
| 14903 | if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) { | 
|---|
| 14904 | if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE)) | 
|---|
| 14905 | return -EINVAL; | 
|---|
| 14906 | new_triggers.four_way_handshake = true; | 
|---|
| 14907 | regular = true; | 
|---|
| 14908 | } | 
|---|
| 14909 |  | 
|---|
| 14910 | if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) { | 
|---|
| 14911 | if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE)) | 
|---|
| 14912 | return -EINVAL; | 
|---|
| 14913 | new_triggers.rfkill_release = true; | 
|---|
| 14914 | regular = true; | 
|---|
| 14915 | } | 
|---|
| 14916 |  | 
|---|
| 14917 | if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) { | 
|---|
| 14918 | struct nlattr *pat; | 
|---|
| 14919 | int n_patterns = 0; | 
|---|
| 14920 | int rem, pat_len, mask_len, pkt_offset; | 
|---|
| 14921 | struct nlattr *pat_tb[NUM_NL80211_PKTPAT]; | 
|---|
| 14922 |  | 
|---|
| 14923 | regular = true; | 
|---|
| 14924 |  | 
|---|
| 14925 | nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], | 
|---|
| 14926 | rem) | 
|---|
| 14927 | n_patterns++; | 
|---|
| 14928 | if (n_patterns > wowlan->n_patterns) | 
|---|
| 14929 | return -EINVAL; | 
|---|
| 14930 |  | 
|---|
| 14931 | new_triggers.patterns = kcalloc(n_patterns, | 
|---|
| 14932 | sizeof(new_triggers.patterns[0]), | 
|---|
| 14933 | GFP_KERNEL); | 
|---|
| 14934 | if (!new_triggers.patterns) | 
|---|
| 14935 | return -ENOMEM; | 
|---|
| 14936 |  | 
|---|
| 14937 | new_triggers.n_patterns = n_patterns; | 
|---|
| 14938 | i = 0; | 
|---|
| 14939 |  | 
|---|
| 14940 | nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], | 
|---|
| 14941 | rem) { | 
|---|
| 14942 | u8 *mask_pat; | 
|---|
| 14943 |  | 
|---|
| 14944 | err = nla_parse_nested_deprecated(tb: pat_tb, | 
|---|
| 14945 | maxtype: MAX_NL80211_PKTPAT, | 
|---|
| 14946 | nla: pat, | 
|---|
| 14947 | policy: nl80211_packet_pattern_policy, | 
|---|
| 14948 | extack: info->extack); | 
|---|
| 14949 | if (err) | 
|---|
| 14950 | goto error; | 
|---|
| 14951 |  | 
|---|
| 14952 | err = -EINVAL; | 
|---|
| 14953 | if (!pat_tb[NL80211_PKTPAT_MASK] || | 
|---|
| 14954 | !pat_tb[NL80211_PKTPAT_PATTERN]) | 
|---|
| 14955 | goto error; | 
|---|
| 14956 | pat_len = nla_len(nla: pat_tb[NL80211_PKTPAT_PATTERN]); | 
|---|
| 14957 | mask_len = DIV_ROUND_UP(pat_len, 8); | 
|---|
| 14958 | if (nla_len(nla: pat_tb[NL80211_PKTPAT_MASK]) != mask_len) | 
|---|
| 14959 | goto error; | 
|---|
| 14960 | if (pat_len > wowlan->pattern_max_len || | 
|---|
| 14961 | pat_len < wowlan->pattern_min_len) | 
|---|
| 14962 | goto error; | 
|---|
| 14963 |  | 
|---|
| 14964 | pkt_offset = | 
|---|
| 14965 | nla_get_u32_default(nla: pat_tb[NL80211_PKTPAT_OFFSET], | 
|---|
| 14966 | defvalue: 0); | 
|---|
| 14967 | if (pkt_offset > wowlan->max_pkt_offset) | 
|---|
| 14968 | goto error; | 
|---|
| 14969 | new_triggers.patterns[i].pkt_offset = pkt_offset; | 
|---|
| 14970 |  | 
|---|
| 14971 | mask_pat = kmalloc(mask_len + pat_len, GFP_KERNEL); | 
|---|
| 14972 | if (!mask_pat) { | 
|---|
| 14973 | err = -ENOMEM; | 
|---|
| 14974 | goto error; | 
|---|
| 14975 | } | 
|---|
| 14976 | new_triggers.patterns[i].mask = mask_pat; | 
|---|
| 14977 | memcpy(to: mask_pat, from: nla_data(nla: pat_tb[NL80211_PKTPAT_MASK]), | 
|---|
| 14978 | len: mask_len); | 
|---|
| 14979 | mask_pat += mask_len; | 
|---|
| 14980 | new_triggers.patterns[i].pattern = mask_pat; | 
|---|
| 14981 | new_triggers.patterns[i].pattern_len = pat_len; | 
|---|
| 14982 | memcpy(to: mask_pat, | 
|---|
| 14983 | from: nla_data(nla: pat_tb[NL80211_PKTPAT_PATTERN]), | 
|---|
| 14984 | len: pat_len); | 
|---|
| 14985 | i++; | 
|---|
| 14986 | } | 
|---|
| 14987 | } | 
|---|
| 14988 |  | 
|---|
| 14989 | if (tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION]) { | 
|---|
| 14990 | regular = true; | 
|---|
| 14991 | err = nl80211_parse_wowlan_tcp( | 
|---|
| 14992 | rdev, attr: tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION], | 
|---|
| 14993 | trig: &new_triggers); | 
|---|
| 14994 | if (err) | 
|---|
| 14995 | goto error; | 
|---|
| 14996 | } | 
|---|
| 14997 |  | 
|---|
| 14998 | if (tb[NL80211_WOWLAN_TRIG_NET_DETECT]) { | 
|---|
| 14999 | regular = true; | 
|---|
| 15000 | err = nl80211_parse_wowlan_nd( | 
|---|
| 15001 | rdev, wowlan, attr: tb[NL80211_WOWLAN_TRIG_NET_DETECT], | 
|---|
| 15002 | trig: &new_triggers); | 
|---|
| 15003 | if (err) | 
|---|
| 15004 | goto error; | 
|---|
| 15005 | } | 
|---|
| 15006 |  | 
|---|
| 15007 | /* The 'any' trigger means the device continues operating more or less | 
|---|
| 15008 | * as in its normal operation mode and wakes up the host on most of the | 
|---|
| 15009 | * normal interrupts (like packet RX, ...) | 
|---|
| 15010 | * It therefore makes little sense to combine with the more constrained | 
|---|
| 15011 | * wakeup trigger modes. | 
|---|
| 15012 | */ | 
|---|
| 15013 | if (new_triggers.any && regular) { | 
|---|
| 15014 | err = -EINVAL; | 
|---|
| 15015 | goto error; | 
|---|
| 15016 | } | 
|---|
| 15017 |  | 
|---|
| 15018 | ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL); | 
|---|
| 15019 | if (!ntrig) { | 
|---|
| 15020 | err = -ENOMEM; | 
|---|
| 15021 | goto error; | 
|---|
| 15022 | } | 
|---|
| 15023 | cfg80211_rdev_free_wowlan(rdev); | 
|---|
| 15024 | rdev->wiphy.wowlan_config = ntrig; | 
|---|
| 15025 |  | 
|---|
| 15026 | set_wakeup: | 
|---|
| 15027 | if (rdev->ops->set_wakeup && | 
|---|
| 15028 | prev_enabled != !!rdev->wiphy.wowlan_config) | 
|---|
| 15029 | rdev_set_wakeup(rdev, enabled: rdev->wiphy.wowlan_config); | 
|---|
| 15030 |  | 
|---|
| 15031 | return 0; | 
|---|
| 15032 | error: | 
|---|
| 15033 | for (i = 0; i < new_triggers.n_patterns; i++) | 
|---|
| 15034 | kfree(objp: new_triggers.patterns[i].mask); | 
|---|
| 15035 | kfree(objp: new_triggers.patterns); | 
|---|
| 15036 | if (new_triggers.tcp && new_triggers.tcp->sock) | 
|---|
| 15037 | sock_release(sock: new_triggers.tcp->sock); | 
|---|
| 15038 | kfree(objp: new_triggers.tcp); | 
|---|
| 15039 | kfree(objp: new_triggers.nd_config); | 
|---|
| 15040 | return err; | 
|---|
| 15041 | } | 
|---|
| 15042 | #endif | 
|---|
| 15043 |  | 
|---|
| 15044 | static int nl80211_send_coalesce_rules(struct sk_buff *msg, | 
|---|
| 15045 | struct cfg80211_registered_device *rdev) | 
|---|
| 15046 | { | 
|---|
| 15047 | struct nlattr *nl_pats, *nl_pat, *nl_rule, *nl_rules; | 
|---|
| 15048 | int i, j, pat_len; | 
|---|
| 15049 | struct cfg80211_coalesce_rules *rule; | 
|---|
| 15050 |  | 
|---|
| 15051 | if (!rdev->coalesce->n_rules) | 
|---|
| 15052 | return 0; | 
|---|
| 15053 |  | 
|---|
| 15054 | nl_rules = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_COALESCE_RULE); | 
|---|
| 15055 | if (!nl_rules) | 
|---|
| 15056 | return -ENOBUFS; | 
|---|
| 15057 |  | 
|---|
| 15058 | for (i = 0; i < rdev->coalesce->n_rules; i++) { | 
|---|
| 15059 | nl_rule = nla_nest_start_noflag(skb: msg, attrtype: i + 1); | 
|---|
| 15060 | if (!nl_rule) | 
|---|
| 15061 | return -ENOBUFS; | 
|---|
| 15062 |  | 
|---|
| 15063 | rule = &rdev->coalesce->rules[i]; | 
|---|
| 15064 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_COALESCE_RULE_DELAY, | 
|---|
| 15065 | value: rule->delay)) | 
|---|
| 15066 | return -ENOBUFS; | 
|---|
| 15067 |  | 
|---|
| 15068 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_COALESCE_RULE_CONDITION, | 
|---|
| 15069 | value: rule->condition)) | 
|---|
| 15070 | return -ENOBUFS; | 
|---|
| 15071 |  | 
|---|
| 15072 | nl_pats = nla_nest_start_noflag(skb: msg, | 
|---|
| 15073 | attrtype: NL80211_ATTR_COALESCE_RULE_PKT_PATTERN); | 
|---|
| 15074 | if (!nl_pats) | 
|---|
| 15075 | return -ENOBUFS; | 
|---|
| 15076 |  | 
|---|
| 15077 | for (j = 0; j < rule->n_patterns; j++) { | 
|---|
| 15078 | nl_pat = nla_nest_start_noflag(skb: msg, attrtype: j + 1); | 
|---|
| 15079 | if (!nl_pat) | 
|---|
| 15080 | return -ENOBUFS; | 
|---|
| 15081 | pat_len = rule->patterns[j].pattern_len; | 
|---|
| 15082 | if (nla_put(skb: msg, attrtype: NL80211_PKTPAT_MASK, | 
|---|
| 15083 | DIV_ROUND_UP(pat_len, 8), | 
|---|
| 15084 | data: rule->patterns[j].mask) || | 
|---|
| 15085 | nla_put(skb: msg, attrtype: NL80211_PKTPAT_PATTERN, attrlen: pat_len, | 
|---|
| 15086 | data: rule->patterns[j].pattern) || | 
|---|
| 15087 | nla_put_u32(skb: msg, attrtype: NL80211_PKTPAT_OFFSET, | 
|---|
| 15088 | value: rule->patterns[j].pkt_offset)) | 
|---|
| 15089 | return -ENOBUFS; | 
|---|
| 15090 | nla_nest_end(skb: msg, start: nl_pat); | 
|---|
| 15091 | } | 
|---|
| 15092 | nla_nest_end(skb: msg, start: nl_pats); | 
|---|
| 15093 | nla_nest_end(skb: msg, start: nl_rule); | 
|---|
| 15094 | } | 
|---|
| 15095 | nla_nest_end(skb: msg, start: nl_rules); | 
|---|
| 15096 |  | 
|---|
| 15097 | return 0; | 
|---|
| 15098 | } | 
|---|
| 15099 |  | 
|---|
| 15100 | static int nl80211_get_coalesce(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 15101 | { | 
|---|
| 15102 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 15103 | struct sk_buff *msg; | 
|---|
| 15104 | void *hdr; | 
|---|
| 15105 |  | 
|---|
| 15106 | if (!rdev->wiphy.coalesce) | 
|---|
| 15107 | return -EOPNOTSUPP; | 
|---|
| 15108 |  | 
|---|
| 15109 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 15110 | if (!msg) | 
|---|
| 15111 | return -ENOMEM; | 
|---|
| 15112 |  | 
|---|
| 15113 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, | 
|---|
| 15114 | cmd: NL80211_CMD_GET_COALESCE); | 
|---|
| 15115 | if (!hdr) | 
|---|
| 15116 | goto nla_put_failure; | 
|---|
| 15117 |  | 
|---|
| 15118 | if (rdev->coalesce && nl80211_send_coalesce_rules(msg, rdev)) | 
|---|
| 15119 | goto nla_put_failure; | 
|---|
| 15120 |  | 
|---|
| 15121 | genlmsg_end(skb: msg, hdr); | 
|---|
| 15122 | return genlmsg_reply(skb: msg, info); | 
|---|
| 15123 |  | 
|---|
| 15124 | nla_put_failure: | 
|---|
| 15125 | nlmsg_free(skb: msg); | 
|---|
| 15126 | return -ENOBUFS; | 
|---|
| 15127 | } | 
|---|
| 15128 |  | 
|---|
| 15129 | void cfg80211_free_coalesce(struct cfg80211_coalesce *coalesce) | 
|---|
| 15130 | { | 
|---|
| 15131 | int i, j; | 
|---|
| 15132 | struct cfg80211_coalesce_rules *rule; | 
|---|
| 15133 |  | 
|---|
| 15134 | if (!coalesce) | 
|---|
| 15135 | return; | 
|---|
| 15136 |  | 
|---|
| 15137 | for (i = 0; i < coalesce->n_rules; i++) { | 
|---|
| 15138 | rule = &coalesce->rules[i]; | 
|---|
| 15139 | for (j = 0; j < rule->n_patterns; j++) | 
|---|
| 15140 | kfree(objp: rule->patterns[j].mask); | 
|---|
| 15141 | kfree(objp: rule->patterns); | 
|---|
| 15142 | } | 
|---|
| 15143 | kfree(objp: coalesce); | 
|---|
| 15144 | } | 
|---|
| 15145 |  | 
|---|
| 15146 | static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev, | 
|---|
| 15147 | struct nlattr *rule, | 
|---|
| 15148 | struct cfg80211_coalesce_rules *new_rule) | 
|---|
| 15149 | { | 
|---|
| 15150 | int err, i; | 
|---|
| 15151 | const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce; | 
|---|
| 15152 | struct nlattr *tb[NUM_NL80211_ATTR_COALESCE_RULE], *pat; | 
|---|
| 15153 | int rem, pat_len, mask_len, pkt_offset, n_patterns = 0; | 
|---|
| 15154 | struct nlattr *pat_tb[NUM_NL80211_PKTPAT]; | 
|---|
| 15155 |  | 
|---|
| 15156 | err = nla_parse_nested_deprecated(tb, maxtype: NL80211_ATTR_COALESCE_RULE_MAX, | 
|---|
| 15157 | nla: rule, policy: nl80211_coalesce_policy, NULL); | 
|---|
| 15158 | if (err) | 
|---|
| 15159 | return err; | 
|---|
| 15160 |  | 
|---|
| 15161 | if (tb[NL80211_ATTR_COALESCE_RULE_DELAY]) | 
|---|
| 15162 | new_rule->delay = | 
|---|
| 15163 | nla_get_u32(nla: tb[NL80211_ATTR_COALESCE_RULE_DELAY]); | 
|---|
| 15164 | if (new_rule->delay > coalesce->max_delay) | 
|---|
| 15165 | return -EINVAL; | 
|---|
| 15166 |  | 
|---|
| 15167 | if (tb[NL80211_ATTR_COALESCE_RULE_CONDITION]) | 
|---|
| 15168 | new_rule->condition = | 
|---|
| 15169 | nla_get_u32(nla: tb[NL80211_ATTR_COALESCE_RULE_CONDITION]); | 
|---|
| 15170 |  | 
|---|
| 15171 | if (!tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN]) | 
|---|
| 15172 | return -EINVAL; | 
|---|
| 15173 |  | 
|---|
| 15174 | nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN], | 
|---|
| 15175 | rem) | 
|---|
| 15176 | n_patterns++; | 
|---|
| 15177 | if (n_patterns > coalesce->n_patterns) | 
|---|
| 15178 | return -EINVAL; | 
|---|
| 15179 |  | 
|---|
| 15180 | new_rule->patterns = kcalloc(n_patterns, sizeof(new_rule->patterns[0]), | 
|---|
| 15181 | GFP_KERNEL); | 
|---|
| 15182 | if (!new_rule->patterns) | 
|---|
| 15183 | return -ENOMEM; | 
|---|
| 15184 |  | 
|---|
| 15185 | new_rule->n_patterns = n_patterns; | 
|---|
| 15186 | i = 0; | 
|---|
| 15187 |  | 
|---|
| 15188 | nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN], | 
|---|
| 15189 | rem) { | 
|---|
| 15190 | u8 *mask_pat; | 
|---|
| 15191 |  | 
|---|
| 15192 | err = nla_parse_nested_deprecated(tb: pat_tb, maxtype: MAX_NL80211_PKTPAT, | 
|---|
| 15193 | nla: pat, | 
|---|
| 15194 | policy: nl80211_packet_pattern_policy, | 
|---|
| 15195 | NULL); | 
|---|
| 15196 | if (err) | 
|---|
| 15197 | return err; | 
|---|
| 15198 |  | 
|---|
| 15199 | if (!pat_tb[NL80211_PKTPAT_MASK] || | 
|---|
| 15200 | !pat_tb[NL80211_PKTPAT_PATTERN]) | 
|---|
| 15201 | return -EINVAL; | 
|---|
| 15202 | pat_len = nla_len(nla: pat_tb[NL80211_PKTPAT_PATTERN]); | 
|---|
| 15203 | mask_len = DIV_ROUND_UP(pat_len, 8); | 
|---|
| 15204 | if (nla_len(nla: pat_tb[NL80211_PKTPAT_MASK]) != mask_len) | 
|---|
| 15205 | return -EINVAL; | 
|---|
| 15206 | if (pat_len > coalesce->pattern_max_len || | 
|---|
| 15207 | pat_len < coalesce->pattern_min_len) | 
|---|
| 15208 | return -EINVAL; | 
|---|
| 15209 |  | 
|---|
| 15210 | pkt_offset = nla_get_u32_default(nla: pat_tb[NL80211_PKTPAT_OFFSET], | 
|---|
| 15211 | defvalue: 0); | 
|---|
| 15212 | if (pkt_offset > coalesce->max_pkt_offset) | 
|---|
| 15213 | return -EINVAL; | 
|---|
| 15214 | new_rule->patterns[i].pkt_offset = pkt_offset; | 
|---|
| 15215 |  | 
|---|
| 15216 | mask_pat = kmalloc(mask_len + pat_len, GFP_KERNEL); | 
|---|
| 15217 | if (!mask_pat) | 
|---|
| 15218 | return -ENOMEM; | 
|---|
| 15219 |  | 
|---|
| 15220 | new_rule->patterns[i].mask = mask_pat; | 
|---|
| 15221 | memcpy(to: mask_pat, from: nla_data(nla: pat_tb[NL80211_PKTPAT_MASK]), | 
|---|
| 15222 | len: mask_len); | 
|---|
| 15223 |  | 
|---|
| 15224 | mask_pat += mask_len; | 
|---|
| 15225 | new_rule->patterns[i].pattern = mask_pat; | 
|---|
| 15226 | new_rule->patterns[i].pattern_len = pat_len; | 
|---|
| 15227 | memcpy(to: mask_pat, from: nla_data(nla: pat_tb[NL80211_PKTPAT_PATTERN]), | 
|---|
| 15228 | len: pat_len); | 
|---|
| 15229 | i++; | 
|---|
| 15230 | } | 
|---|
| 15231 |  | 
|---|
| 15232 | return 0; | 
|---|
| 15233 | } | 
|---|
| 15234 |  | 
|---|
| 15235 | static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 15236 | { | 
|---|
| 15237 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 15238 | const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce; | 
|---|
| 15239 | struct cfg80211_coalesce *new_coalesce; | 
|---|
| 15240 | int err, rem_rule, n_rules = 0, i; | 
|---|
| 15241 | struct nlattr *rule; | 
|---|
| 15242 |  | 
|---|
| 15243 | if (!rdev->wiphy.coalesce || !rdev->ops->set_coalesce) | 
|---|
| 15244 | return -EOPNOTSUPP; | 
|---|
| 15245 |  | 
|---|
| 15246 | if (!info->attrs[NL80211_ATTR_COALESCE_RULE]) { | 
|---|
| 15247 | cfg80211_free_coalesce(coalesce: rdev->coalesce); | 
|---|
| 15248 | rdev->coalesce = NULL; | 
|---|
| 15249 | rdev_set_coalesce(rdev, NULL); | 
|---|
| 15250 | return 0; | 
|---|
| 15251 | } | 
|---|
| 15252 |  | 
|---|
| 15253 | nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE], | 
|---|
| 15254 | rem_rule) | 
|---|
| 15255 | n_rules++; | 
|---|
| 15256 | if (n_rules > coalesce->n_rules) | 
|---|
| 15257 | return -EINVAL; | 
|---|
| 15258 |  | 
|---|
| 15259 | new_coalesce = kzalloc(struct_size(new_coalesce, rules, n_rules), | 
|---|
| 15260 | GFP_KERNEL); | 
|---|
| 15261 | if (!new_coalesce) | 
|---|
| 15262 | return -ENOMEM; | 
|---|
| 15263 |  | 
|---|
| 15264 | new_coalesce->n_rules = n_rules; | 
|---|
| 15265 | i = 0; | 
|---|
| 15266 |  | 
|---|
| 15267 | nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE], | 
|---|
| 15268 | rem_rule) { | 
|---|
| 15269 | err = nl80211_parse_coalesce_rule(rdev, rule, | 
|---|
| 15270 | new_rule: &new_coalesce->rules[i]); | 
|---|
| 15271 | if (err) | 
|---|
| 15272 | goto error; | 
|---|
| 15273 |  | 
|---|
| 15274 | i++; | 
|---|
| 15275 | } | 
|---|
| 15276 |  | 
|---|
| 15277 | err = rdev_set_coalesce(rdev, coalesce: new_coalesce); | 
|---|
| 15278 | if (err) | 
|---|
| 15279 | goto error; | 
|---|
| 15280 |  | 
|---|
| 15281 | cfg80211_free_coalesce(coalesce: rdev->coalesce); | 
|---|
| 15282 | rdev->coalesce = new_coalesce; | 
|---|
| 15283 |  | 
|---|
| 15284 | return 0; | 
|---|
| 15285 | error: | 
|---|
| 15286 | cfg80211_free_coalesce(coalesce: new_coalesce); | 
|---|
| 15287 |  | 
|---|
| 15288 | return err; | 
|---|
| 15289 | } | 
|---|
| 15290 |  | 
|---|
| 15291 | static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 15292 | { | 
|---|
| 15293 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 15294 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 15295 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 15296 | struct nlattr *tb[NUM_NL80211_REKEY_DATA]; | 
|---|
| 15297 | struct cfg80211_gtk_rekey_data rekey_data = {}; | 
|---|
| 15298 | int err; | 
|---|
| 15299 |  | 
|---|
| 15300 | if (!info->attrs[NL80211_ATTR_REKEY_DATA]) | 
|---|
| 15301 | return -EINVAL; | 
|---|
| 15302 |  | 
|---|
| 15303 | err = nla_parse_nested_deprecated(tb, maxtype: MAX_NL80211_REKEY_DATA, | 
|---|
| 15304 | nla: info->attrs[NL80211_ATTR_REKEY_DATA], | 
|---|
| 15305 | policy: nl80211_rekey_policy, extack: info->extack); | 
|---|
| 15306 | if (err) | 
|---|
| 15307 | return err; | 
|---|
| 15308 |  | 
|---|
| 15309 | if (!tb[NL80211_REKEY_DATA_REPLAY_CTR] || !tb[NL80211_REKEY_DATA_KEK] || | 
|---|
| 15310 | !tb[NL80211_REKEY_DATA_KCK]) | 
|---|
| 15311 | return -EINVAL; | 
|---|
| 15312 | if (nla_len(nla: tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN && | 
|---|
| 15313 | !(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK && | 
|---|
| 15314 | nla_len(nla: tb[NL80211_REKEY_DATA_KEK]) == NL80211_KEK_EXT_LEN)) | 
|---|
| 15315 | return -ERANGE; | 
|---|
| 15316 | if (nla_len(nla: tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN && | 
|---|
| 15317 | !(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK && | 
|---|
| 15318 | nla_len(nla: tb[NL80211_REKEY_DATA_KCK]) == NL80211_KCK_EXT_LEN) && | 
|---|
| 15319 | !(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_EXT_KCK_32 && | 
|---|
| 15320 | nla_len(nla: tb[NL80211_REKEY_DATA_KCK]) == NL80211_KCK_EXT_LEN_32)) | 
|---|
| 15321 | return -ERANGE; | 
|---|
| 15322 |  | 
|---|
| 15323 | rekey_data.kek = nla_data(nla: tb[NL80211_REKEY_DATA_KEK]); | 
|---|
| 15324 | rekey_data.kck = nla_data(nla: tb[NL80211_REKEY_DATA_KCK]); | 
|---|
| 15325 | rekey_data.replay_ctr = nla_data(nla: tb[NL80211_REKEY_DATA_REPLAY_CTR]); | 
|---|
| 15326 | rekey_data.kek_len = nla_len(nla: tb[NL80211_REKEY_DATA_KEK]); | 
|---|
| 15327 | rekey_data.kck_len = nla_len(nla: tb[NL80211_REKEY_DATA_KCK]); | 
|---|
| 15328 | if (tb[NL80211_REKEY_DATA_AKM]) | 
|---|
| 15329 | rekey_data.akm = nla_get_u32(nla: tb[NL80211_REKEY_DATA_AKM]); | 
|---|
| 15330 |  | 
|---|
| 15331 | if (!wdev->connected) | 
|---|
| 15332 | return -ENOTCONN; | 
|---|
| 15333 |  | 
|---|
| 15334 | if (!rdev->ops->set_rekey_data) | 
|---|
| 15335 | return -EOPNOTSUPP; | 
|---|
| 15336 |  | 
|---|
| 15337 | return rdev_set_rekey_data(rdev, dev, data: &rekey_data); | 
|---|
| 15338 | } | 
|---|
| 15339 |  | 
|---|
| 15340 | static int nl80211_register_unexpected_frame(struct sk_buff *skb, | 
|---|
| 15341 | struct genl_info *info) | 
|---|
| 15342 | { | 
|---|
| 15343 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 15344 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 15345 |  | 
|---|
| 15346 | if (wdev->iftype != NL80211_IFTYPE_AP && | 
|---|
| 15347 | wdev->iftype != NL80211_IFTYPE_P2P_GO) | 
|---|
| 15348 | return -EINVAL; | 
|---|
| 15349 |  | 
|---|
| 15350 | if (wdev->ap_unexpected_nlportid) | 
|---|
| 15351 | return -EBUSY; | 
|---|
| 15352 |  | 
|---|
| 15353 | wdev->ap_unexpected_nlportid = info->snd_portid; | 
|---|
| 15354 | return 0; | 
|---|
| 15355 | } | 
|---|
| 15356 |  | 
|---|
| 15357 | static int nl80211_probe_client(struct sk_buff *skb, | 
|---|
| 15358 | struct genl_info *info) | 
|---|
| 15359 | { | 
|---|
| 15360 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 15361 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 15362 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 15363 | struct sk_buff *msg; | 
|---|
| 15364 | void *hdr; | 
|---|
| 15365 | const u8 *addr; | 
|---|
| 15366 | u64 cookie; | 
|---|
| 15367 | int err; | 
|---|
| 15368 |  | 
|---|
| 15369 | if (wdev->iftype != NL80211_IFTYPE_AP && | 
|---|
| 15370 | wdev->iftype != NL80211_IFTYPE_P2P_GO) | 
|---|
| 15371 | return -EOPNOTSUPP; | 
|---|
| 15372 |  | 
|---|
| 15373 | if (!info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 15374 | return -EINVAL; | 
|---|
| 15375 |  | 
|---|
| 15376 | if (!rdev->ops->probe_client) | 
|---|
| 15377 | return -EOPNOTSUPP; | 
|---|
| 15378 |  | 
|---|
| 15379 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 15380 | if (!msg) | 
|---|
| 15381 | return -ENOMEM; | 
|---|
| 15382 |  | 
|---|
| 15383 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, | 
|---|
| 15384 | cmd: NL80211_CMD_PROBE_CLIENT); | 
|---|
| 15385 | if (!hdr) { | 
|---|
| 15386 | err = -ENOBUFS; | 
|---|
| 15387 | goto free_msg; | 
|---|
| 15388 | } | 
|---|
| 15389 |  | 
|---|
| 15390 | addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 15391 |  | 
|---|
| 15392 | err = rdev_probe_client(rdev, dev, peer: addr, cookie: &cookie); | 
|---|
| 15393 | if (err) | 
|---|
| 15394 | goto free_msg; | 
|---|
| 15395 |  | 
|---|
| 15396 | if (nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: cookie, | 
|---|
| 15397 | padattr: NL80211_ATTR_PAD)) | 
|---|
| 15398 | goto nla_put_failure; | 
|---|
| 15399 |  | 
|---|
| 15400 | genlmsg_end(skb: msg, hdr); | 
|---|
| 15401 |  | 
|---|
| 15402 | return genlmsg_reply(skb: msg, info); | 
|---|
| 15403 |  | 
|---|
| 15404 | nla_put_failure: | 
|---|
| 15405 | err = -ENOBUFS; | 
|---|
| 15406 | free_msg: | 
|---|
| 15407 | nlmsg_free(skb: msg); | 
|---|
| 15408 | return err; | 
|---|
| 15409 | } | 
|---|
| 15410 |  | 
|---|
| 15411 | static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 15412 | { | 
|---|
| 15413 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 15414 | struct cfg80211_beacon_registration *reg, *nreg; | 
|---|
| 15415 | int rv; | 
|---|
| 15416 |  | 
|---|
| 15417 | if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS)) | 
|---|
| 15418 | return -EOPNOTSUPP; | 
|---|
| 15419 |  | 
|---|
| 15420 | nreg = kzalloc(sizeof(*nreg), GFP_KERNEL); | 
|---|
| 15421 | if (!nreg) | 
|---|
| 15422 | return -ENOMEM; | 
|---|
| 15423 |  | 
|---|
| 15424 | /* First, check if already registered. */ | 
|---|
| 15425 | spin_lock_bh(lock: &rdev->beacon_registrations_lock); | 
|---|
| 15426 | list_for_each_entry(reg, &rdev->beacon_registrations, list) { | 
|---|
| 15427 | if (reg->nlportid == info->snd_portid) { | 
|---|
| 15428 | rv = -EALREADY; | 
|---|
| 15429 | goto out_err; | 
|---|
| 15430 | } | 
|---|
| 15431 | } | 
|---|
| 15432 | /* Add it to the list */ | 
|---|
| 15433 | nreg->nlportid = info->snd_portid; | 
|---|
| 15434 | list_add(new: &nreg->list, head: &rdev->beacon_registrations); | 
|---|
| 15435 |  | 
|---|
| 15436 | spin_unlock_bh(lock: &rdev->beacon_registrations_lock); | 
|---|
| 15437 |  | 
|---|
| 15438 | return 0; | 
|---|
| 15439 | out_err: | 
|---|
| 15440 | spin_unlock_bh(lock: &rdev->beacon_registrations_lock); | 
|---|
| 15441 | kfree(objp: nreg); | 
|---|
| 15442 | return rv; | 
|---|
| 15443 | } | 
|---|
| 15444 |  | 
|---|
| 15445 | static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 15446 | { | 
|---|
| 15447 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 15448 | struct wireless_dev *wdev = info->user_ptr[1]; | 
|---|
| 15449 | int err; | 
|---|
| 15450 |  | 
|---|
| 15451 | if (!rdev->ops->start_p2p_device) | 
|---|
| 15452 | return -EOPNOTSUPP; | 
|---|
| 15453 |  | 
|---|
| 15454 | if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE) | 
|---|
| 15455 | return -EOPNOTSUPP; | 
|---|
| 15456 |  | 
|---|
| 15457 | if (wdev_running(wdev)) | 
|---|
| 15458 | return 0; | 
|---|
| 15459 |  | 
|---|
| 15460 | if (rfkill_blocked(rfkill: rdev->wiphy.rfkill)) | 
|---|
| 15461 | return -ERFKILL; | 
|---|
| 15462 |  | 
|---|
| 15463 | err = rdev_start_p2p_device(rdev, wdev); | 
|---|
| 15464 | if (err) | 
|---|
| 15465 | return err; | 
|---|
| 15466 |  | 
|---|
| 15467 | wdev->is_running = true; | 
|---|
| 15468 | rdev->opencount++; | 
|---|
| 15469 |  | 
|---|
| 15470 | return 0; | 
|---|
| 15471 | } | 
|---|
| 15472 |  | 
|---|
| 15473 | static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 15474 | { | 
|---|
| 15475 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 15476 | struct wireless_dev *wdev = info->user_ptr[1]; | 
|---|
| 15477 |  | 
|---|
| 15478 | if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE) | 
|---|
| 15479 | return -EOPNOTSUPP; | 
|---|
| 15480 |  | 
|---|
| 15481 | if (!rdev->ops->stop_p2p_device) | 
|---|
| 15482 | return -EOPNOTSUPP; | 
|---|
| 15483 |  | 
|---|
| 15484 | cfg80211_stop_p2p_device(rdev, wdev); | 
|---|
| 15485 |  | 
|---|
| 15486 | return 0; | 
|---|
| 15487 | } | 
|---|
| 15488 |  | 
|---|
| 15489 | static struct ieee80211_channel *nl80211_get_nan_channel(struct wiphy *wiphy, | 
|---|
| 15490 | int freq) | 
|---|
| 15491 | { | 
|---|
| 15492 | struct ieee80211_channel *chan; | 
|---|
| 15493 | struct cfg80211_chan_def def; | 
|---|
| 15494 |  | 
|---|
| 15495 | /* Check if the frequency is valid for NAN */ | 
|---|
| 15496 | if (freq != 5220 && freq != 5745 && freq != 2437) | 
|---|
| 15497 | return NULL; | 
|---|
| 15498 |  | 
|---|
| 15499 | chan = ieee80211_get_channel(wiphy, freq); | 
|---|
| 15500 | if (!chan) | 
|---|
| 15501 | return NULL; | 
|---|
| 15502 |  | 
|---|
| 15503 | cfg80211_chandef_create(chandef: &def, channel: chan, chantype: NL80211_CHAN_NO_HT); | 
|---|
| 15504 |  | 
|---|
| 15505 | /* Check if the channel is allowed */ | 
|---|
| 15506 | if (cfg80211_reg_can_beacon(wiphy, chandef: &def, iftype: NL80211_IFTYPE_NAN)) | 
|---|
| 15507 | return chan; | 
|---|
| 15508 |  | 
|---|
| 15509 | return NULL; | 
|---|
| 15510 | } | 
|---|
| 15511 |  | 
|---|
| 15512 | static int nl80211_parse_nan_band_config(struct wiphy *wiphy, | 
|---|
| 15513 | struct nlattr **tb, | 
|---|
| 15514 | struct cfg80211_nan_band_config *cfg, | 
|---|
| 15515 | enum nl80211_band band) | 
|---|
| 15516 | { | 
|---|
| 15517 | if (BIT(band) & ~(u32)wiphy->nan_supported_bands) | 
|---|
| 15518 | return -EINVAL; | 
|---|
| 15519 |  | 
|---|
| 15520 | if (tb[NL80211_NAN_BAND_CONF_FREQ]) { | 
|---|
| 15521 | u16 freq = nla_get_u16(nla: tb[NL80211_NAN_BAND_CONF_FREQ]); | 
|---|
| 15522 |  | 
|---|
| 15523 | if (band != NL80211_BAND_5GHZ) | 
|---|
| 15524 | return -EINVAL; | 
|---|
| 15525 |  | 
|---|
| 15526 | cfg->chan = nl80211_get_nan_channel(wiphy, freq); | 
|---|
| 15527 | if (!cfg->chan) | 
|---|
| 15528 | return -EINVAL; | 
|---|
| 15529 | } | 
|---|
| 15530 |  | 
|---|
| 15531 | if (tb[NL80211_NAN_BAND_CONF_RSSI_CLOSE]) { | 
|---|
| 15532 | cfg->rssi_close = | 
|---|
| 15533 | nla_get_s8(nla: tb[NL80211_NAN_BAND_CONF_RSSI_CLOSE]); | 
|---|
| 15534 | if (!tb[NL80211_NAN_BAND_CONF_RSSI_MIDDLE]) | 
|---|
| 15535 | return -EINVAL; | 
|---|
| 15536 | } | 
|---|
| 15537 |  | 
|---|
| 15538 | if (tb[NL80211_NAN_BAND_CONF_RSSI_MIDDLE]) { | 
|---|
| 15539 | cfg->rssi_middle = | 
|---|
| 15540 | nla_get_s8(nla: tb[NL80211_NAN_BAND_CONF_RSSI_MIDDLE]); | 
|---|
| 15541 | if (!cfg->rssi_close || cfg->rssi_middle >= cfg->rssi_close) | 
|---|
| 15542 | return -EINVAL; | 
|---|
| 15543 | } | 
|---|
| 15544 |  | 
|---|
| 15545 | if (tb[NL80211_NAN_BAND_CONF_WAKE_DW]) { | 
|---|
| 15546 | cfg->awake_dw_interval = | 
|---|
| 15547 | nla_get_u8(nla: tb[NL80211_NAN_BAND_CONF_WAKE_DW]); | 
|---|
| 15548 |  | 
|---|
| 15549 | if (band == NL80211_BAND_2GHZ && cfg->awake_dw_interval == 0) | 
|---|
| 15550 | return -EINVAL; | 
|---|
| 15551 | } | 
|---|
| 15552 |  | 
|---|
| 15553 | cfg->disable_scan = | 
|---|
| 15554 | nla_get_flag(nla: tb[NL80211_NAN_BAND_CONF_DISABLE_SCAN]); | 
|---|
| 15555 | return 0; | 
|---|
| 15556 | } | 
|---|
| 15557 |  | 
|---|
| 15558 | static int nl80211_parse_nan_conf(struct wiphy *wiphy, | 
|---|
| 15559 | struct genl_info *info, | 
|---|
| 15560 | struct cfg80211_nan_conf *conf, | 
|---|
| 15561 | u32 *changed_flags) | 
|---|
| 15562 | { | 
|---|
| 15563 | struct nlattr *attrs[NL80211_NAN_CONF_ATTR_MAX + 1]; | 
|---|
| 15564 | int err, rem; | 
|---|
| 15565 | u32 changed = 0; | 
|---|
| 15566 | struct nlattr *band_config; | 
|---|
| 15567 |  | 
|---|
| 15568 | if (info->attrs[NL80211_ATTR_NAN_MASTER_PREF]) { | 
|---|
| 15569 | conf->master_pref = | 
|---|
| 15570 | nla_get_u8(nla: info->attrs[NL80211_ATTR_NAN_MASTER_PREF]); | 
|---|
| 15571 |  | 
|---|
| 15572 | changed |= CFG80211_NAN_CONF_CHANGED_PREF; | 
|---|
| 15573 | } | 
|---|
| 15574 |  | 
|---|
| 15575 | if (info->attrs[NL80211_ATTR_BANDS]) { | 
|---|
| 15576 | u32 bands = nla_get_u32(nla: info->attrs[NL80211_ATTR_BANDS]); | 
|---|
| 15577 |  | 
|---|
| 15578 | if (bands & ~(u32)wiphy->nan_supported_bands) | 
|---|
| 15579 | return -EOPNOTSUPP; | 
|---|
| 15580 |  | 
|---|
| 15581 | if (bands && !(bands & BIT(NL80211_BAND_2GHZ))) | 
|---|
| 15582 | return -EINVAL; | 
|---|
| 15583 |  | 
|---|
| 15584 | conf->bands = bands; | 
|---|
| 15585 | changed |= CFG80211_NAN_CONF_CHANGED_BANDS; | 
|---|
| 15586 | } | 
|---|
| 15587 |  | 
|---|
| 15588 | conf->band_cfgs[NL80211_BAND_2GHZ].awake_dw_interval = 1; | 
|---|
| 15589 | if (conf->bands & BIT(NL80211_BAND_5GHZ) || !conf->bands) | 
|---|
| 15590 | conf->band_cfgs[NL80211_BAND_5GHZ].awake_dw_interval = 1; | 
|---|
| 15591 |  | 
|---|
| 15592 | /* On 2.4 GHz band use channel 6 */ | 
|---|
| 15593 | conf->band_cfgs[NL80211_BAND_2GHZ].chan = | 
|---|
| 15594 | nl80211_get_nan_channel(wiphy, freq: 2437); | 
|---|
| 15595 | if (!conf->band_cfgs[NL80211_BAND_2GHZ].chan) | 
|---|
| 15596 | return -EINVAL; | 
|---|
| 15597 |  | 
|---|
| 15598 | if (!info->attrs[NL80211_ATTR_NAN_CONFIG]) | 
|---|
| 15599 | goto out; | 
|---|
| 15600 |  | 
|---|
| 15601 | err = nla_parse_nested(tb: attrs, maxtype: NL80211_NAN_CONF_ATTR_MAX, | 
|---|
| 15602 | nla: info->attrs[NL80211_ATTR_NAN_CONFIG], NULL, | 
|---|
| 15603 | extack: info->extack); | 
|---|
| 15604 | if (err) | 
|---|
| 15605 | return err; | 
|---|
| 15606 |  | 
|---|
| 15607 | changed |= CFG80211_NAN_CONF_CHANGED_CONFIG; | 
|---|
| 15608 | if (attrs[NL80211_NAN_CONF_CLUSTER_ID]) | 
|---|
| 15609 | conf->cluster_id = | 
|---|
| 15610 | nla_data(nla: attrs[NL80211_NAN_CONF_CLUSTER_ID]); | 
|---|
| 15611 |  | 
|---|
| 15612 | if (attrs[NL80211_NAN_CONF_EXTRA_ATTRS]) { | 
|---|
| 15613 | conf->extra_nan_attrs = | 
|---|
| 15614 | nla_data(nla: attrs[NL80211_NAN_CONF_EXTRA_ATTRS]); | 
|---|
| 15615 | conf->extra_nan_attrs_len = | 
|---|
| 15616 | nla_len(nla: attrs[NL80211_NAN_CONF_EXTRA_ATTRS]); | 
|---|
| 15617 | } | 
|---|
| 15618 |  | 
|---|
| 15619 | if (attrs[NL80211_NAN_CONF_VENDOR_ELEMS]) { | 
|---|
| 15620 | conf->vendor_elems = | 
|---|
| 15621 | nla_data(nla: attrs[NL80211_NAN_CONF_VENDOR_ELEMS]); | 
|---|
| 15622 | conf->vendor_elems_len = | 
|---|
| 15623 | nla_len(nla: attrs[NL80211_NAN_CONF_VENDOR_ELEMS]); | 
|---|
| 15624 | } | 
|---|
| 15625 |  | 
|---|
| 15626 | if (attrs[NL80211_NAN_CONF_BAND_CONFIGS]) { | 
|---|
| 15627 | nla_for_each_nested(band_config, | 
|---|
| 15628 | attrs[NL80211_NAN_CONF_BAND_CONFIGS], | 
|---|
| 15629 | rem) { | 
|---|
| 15630 | enum nl80211_band band; | 
|---|
| 15631 | struct cfg80211_nan_band_config *cfg; | 
|---|
| 15632 | struct nlattr *tb[NL80211_NAN_BAND_CONF_ATTR_MAX + 1]; | 
|---|
| 15633 |  | 
|---|
| 15634 | err = nla_parse_nested(tb, | 
|---|
| 15635 | maxtype: NL80211_NAN_BAND_CONF_ATTR_MAX, | 
|---|
| 15636 | nla: band_config, NULL, | 
|---|
| 15637 | extack: info->extack); | 
|---|
| 15638 | if (err) | 
|---|
| 15639 | return err; | 
|---|
| 15640 |  | 
|---|
| 15641 | if (!tb[NL80211_NAN_BAND_CONF_BAND]) | 
|---|
| 15642 | return -EINVAL; | 
|---|
| 15643 |  | 
|---|
| 15644 | band = nla_get_u8(nla: tb[NL80211_NAN_BAND_CONF_BAND]); | 
|---|
| 15645 | if (conf->bands && !(conf->bands & BIT(band))) | 
|---|
| 15646 | return -EINVAL; | 
|---|
| 15647 |  | 
|---|
| 15648 | cfg = &conf->band_cfgs[band]; | 
|---|
| 15649 |  | 
|---|
| 15650 | err = nl80211_parse_nan_band_config(wiphy, tb, cfg, | 
|---|
| 15651 | band); | 
|---|
| 15652 | if (err) | 
|---|
| 15653 | return err; | 
|---|
| 15654 | } | 
|---|
| 15655 | } | 
|---|
| 15656 |  | 
|---|
| 15657 | if (attrs[NL80211_NAN_CONF_SCAN_PERIOD]) | 
|---|
| 15658 | conf->scan_period = | 
|---|
| 15659 | nla_get_u16(nla: attrs[NL80211_NAN_CONF_SCAN_PERIOD]); | 
|---|
| 15660 |  | 
|---|
| 15661 | if (attrs[NL80211_NAN_CONF_SCAN_DWELL_TIME]) | 
|---|
| 15662 | conf->scan_dwell_time = | 
|---|
| 15663 | nla_get_u16(nla: attrs[NL80211_NAN_CONF_SCAN_DWELL_TIME]); | 
|---|
| 15664 |  | 
|---|
| 15665 | if (attrs[NL80211_NAN_CONF_DISCOVERY_BEACON_INTERVAL]) | 
|---|
| 15666 | conf->discovery_beacon_interval = | 
|---|
| 15667 | nla_get_u8(nla: attrs[NL80211_NAN_CONF_DISCOVERY_BEACON_INTERVAL]); | 
|---|
| 15668 |  | 
|---|
| 15669 | if (attrs[NL80211_NAN_CONF_NOTIFY_DW]) | 
|---|
| 15670 | conf->enable_dw_notification = | 
|---|
| 15671 | nla_get_flag(nla: attrs[NL80211_NAN_CONF_NOTIFY_DW]); | 
|---|
| 15672 |  | 
|---|
| 15673 | out: | 
|---|
| 15674 | if (!conf->band_cfgs[NL80211_BAND_5GHZ].chan && | 
|---|
| 15675 | (!conf->bands || conf->bands & BIT(NL80211_BAND_5GHZ))) { | 
|---|
| 15676 | /* If no 5GHz channel is specified use default, if possible */ | 
|---|
| 15677 | conf->band_cfgs[NL80211_BAND_5GHZ].chan = | 
|---|
| 15678 | nl80211_get_nan_channel(wiphy, freq: 5745); | 
|---|
| 15679 | if (!conf->band_cfgs[NL80211_BAND_5GHZ].chan) | 
|---|
| 15680 | conf->band_cfgs[NL80211_BAND_5GHZ].chan = | 
|---|
| 15681 | nl80211_get_nan_channel(wiphy, freq: 5220); | 
|---|
| 15682 |  | 
|---|
| 15683 | /* Return error if user space asked explicitly for 5 GHz */ | 
|---|
| 15684 | if (!conf->band_cfgs[NL80211_BAND_5GHZ].chan && | 
|---|
| 15685 | conf->bands & BIT(NL80211_BAND_5GHZ)) { | 
|---|
| 15686 | NL_SET_ERR_MSG_ATTR(info->extack, | 
|---|
| 15687 | info->attrs[NL80211_ATTR_BANDS], | 
|---|
| 15688 | "5 GHz band operation is not allowed"); | 
|---|
| 15689 | return -EINVAL; | 
|---|
| 15690 | } | 
|---|
| 15691 | } | 
|---|
| 15692 |  | 
|---|
| 15693 | if (changed_flags) | 
|---|
| 15694 | *changed_flags = changed; | 
|---|
| 15695 |  | 
|---|
| 15696 | return 0; | 
|---|
| 15697 | } | 
|---|
| 15698 |  | 
|---|
| 15699 | static int nl80211_start_nan(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 15700 | { | 
|---|
| 15701 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 15702 | struct wireless_dev *wdev = info->user_ptr[1]; | 
|---|
| 15703 | struct cfg80211_nan_conf conf = {}; | 
|---|
| 15704 | int err; | 
|---|
| 15705 |  | 
|---|
| 15706 | if (wdev->iftype != NL80211_IFTYPE_NAN) | 
|---|
| 15707 | return -EOPNOTSUPP; | 
|---|
| 15708 |  | 
|---|
| 15709 | if (wdev_running(wdev)) | 
|---|
| 15710 | return -EEXIST; | 
|---|
| 15711 |  | 
|---|
| 15712 | if (rfkill_blocked(rfkill: rdev->wiphy.rfkill)) | 
|---|
| 15713 | return -ERFKILL; | 
|---|
| 15714 |  | 
|---|
| 15715 | /* Master preference is mandatory for START_NAN */ | 
|---|
| 15716 | if (!info->attrs[NL80211_ATTR_NAN_MASTER_PREF]) | 
|---|
| 15717 | return -EINVAL; | 
|---|
| 15718 |  | 
|---|
| 15719 | err = nl80211_parse_nan_conf(wiphy: &rdev->wiphy, info, conf: &conf, NULL); | 
|---|
| 15720 | if (err) | 
|---|
| 15721 | return err; | 
|---|
| 15722 |  | 
|---|
| 15723 | err = rdev_start_nan(rdev, wdev, conf: &conf); | 
|---|
| 15724 | if (err) | 
|---|
| 15725 | return err; | 
|---|
| 15726 |  | 
|---|
| 15727 | wdev->is_running = true; | 
|---|
| 15728 | rdev->opencount++; | 
|---|
| 15729 |  | 
|---|
| 15730 | return 0; | 
|---|
| 15731 | } | 
|---|
| 15732 |  | 
|---|
| 15733 | static int nl80211_stop_nan(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 15734 | { | 
|---|
| 15735 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 15736 | struct wireless_dev *wdev = info->user_ptr[1]; | 
|---|
| 15737 |  | 
|---|
| 15738 | if (wdev->iftype != NL80211_IFTYPE_NAN) | 
|---|
| 15739 | return -EOPNOTSUPP; | 
|---|
| 15740 |  | 
|---|
| 15741 | cfg80211_stop_nan(rdev, wdev); | 
|---|
| 15742 |  | 
|---|
| 15743 | return 0; | 
|---|
| 15744 | } | 
|---|
| 15745 |  | 
|---|
| 15746 | static int validate_nan_filter(struct nlattr *filter_attr) | 
|---|
| 15747 | { | 
|---|
| 15748 | struct nlattr *attr; | 
|---|
| 15749 | int len = 0, n_entries = 0, rem; | 
|---|
| 15750 |  | 
|---|
| 15751 | nla_for_each_nested(attr, filter_attr, rem) { | 
|---|
| 15752 | len += nla_len(nla: attr); | 
|---|
| 15753 | n_entries++; | 
|---|
| 15754 | } | 
|---|
| 15755 |  | 
|---|
| 15756 | if (len >= U8_MAX) | 
|---|
| 15757 | return -EINVAL; | 
|---|
| 15758 |  | 
|---|
| 15759 | return n_entries; | 
|---|
| 15760 | } | 
|---|
| 15761 |  | 
|---|
| 15762 | static int handle_nan_filter(struct nlattr *attr_filter, | 
|---|
| 15763 | struct cfg80211_nan_func *func, | 
|---|
| 15764 | bool tx) | 
|---|
| 15765 | { | 
|---|
| 15766 | struct nlattr *attr; | 
|---|
| 15767 | int n_entries, rem, i; | 
|---|
| 15768 | struct cfg80211_nan_func_filter *filter; | 
|---|
| 15769 |  | 
|---|
| 15770 | n_entries = validate_nan_filter(filter_attr: attr_filter); | 
|---|
| 15771 | if (n_entries < 0) | 
|---|
| 15772 | return n_entries; | 
|---|
| 15773 |  | 
|---|
| 15774 | BUILD_BUG_ON(sizeof(*func->rx_filters) != sizeof(*func->tx_filters)); | 
|---|
| 15775 |  | 
|---|
| 15776 | filter = kcalloc(n_entries, sizeof(*func->rx_filters), GFP_KERNEL); | 
|---|
| 15777 | if (!filter) | 
|---|
| 15778 | return -ENOMEM; | 
|---|
| 15779 |  | 
|---|
| 15780 | i = 0; | 
|---|
| 15781 | nla_for_each_nested(attr, attr_filter, rem) { | 
|---|
| 15782 | filter[i].filter = nla_memdup(attr, GFP_KERNEL); | 
|---|
| 15783 | if (!filter[i].filter) | 
|---|
| 15784 | goto err; | 
|---|
| 15785 |  | 
|---|
| 15786 | filter[i].len = nla_len(nla: attr); | 
|---|
| 15787 | i++; | 
|---|
| 15788 | } | 
|---|
| 15789 | if (tx) { | 
|---|
| 15790 | func->num_tx_filters = n_entries; | 
|---|
| 15791 | func->tx_filters = filter; | 
|---|
| 15792 | } else { | 
|---|
| 15793 | func->num_rx_filters = n_entries; | 
|---|
| 15794 | func->rx_filters = filter; | 
|---|
| 15795 | } | 
|---|
| 15796 |  | 
|---|
| 15797 | return 0; | 
|---|
| 15798 |  | 
|---|
| 15799 | err: | 
|---|
| 15800 | i = 0; | 
|---|
| 15801 | nla_for_each_nested(attr, attr_filter, rem) { | 
|---|
| 15802 | kfree(objp: filter[i].filter); | 
|---|
| 15803 | i++; | 
|---|
| 15804 | } | 
|---|
| 15805 | kfree(objp: filter); | 
|---|
| 15806 | return -ENOMEM; | 
|---|
| 15807 | } | 
|---|
| 15808 |  | 
|---|
| 15809 | static int nl80211_nan_add_func(struct sk_buff *skb, | 
|---|
| 15810 | struct genl_info *info) | 
|---|
| 15811 | { | 
|---|
| 15812 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 15813 | struct wireless_dev *wdev = info->user_ptr[1]; | 
|---|
| 15814 | struct nlattr *tb[NUM_NL80211_NAN_FUNC_ATTR], *func_attr; | 
|---|
| 15815 | struct cfg80211_nan_func *func; | 
|---|
| 15816 | struct sk_buff *msg = NULL; | 
|---|
| 15817 | void *hdr = NULL; | 
|---|
| 15818 | int err = 0; | 
|---|
| 15819 |  | 
|---|
| 15820 | if (wdev->iftype != NL80211_IFTYPE_NAN) | 
|---|
| 15821 | return -EOPNOTSUPP; | 
|---|
| 15822 |  | 
|---|
| 15823 | if (!wdev_running(wdev)) | 
|---|
| 15824 | return -ENOTCONN; | 
|---|
| 15825 |  | 
|---|
| 15826 | if (!info->attrs[NL80211_ATTR_NAN_FUNC]) | 
|---|
| 15827 | return -EINVAL; | 
|---|
| 15828 |  | 
|---|
| 15829 | err = nla_parse_nested_deprecated(tb, maxtype: NL80211_NAN_FUNC_ATTR_MAX, | 
|---|
| 15830 | nla: info->attrs[NL80211_ATTR_NAN_FUNC], | 
|---|
| 15831 | policy: nl80211_nan_func_policy, | 
|---|
| 15832 | extack: info->extack); | 
|---|
| 15833 | if (err) | 
|---|
| 15834 | return err; | 
|---|
| 15835 |  | 
|---|
| 15836 | func = kzalloc(sizeof(*func), GFP_KERNEL); | 
|---|
| 15837 | if (!func) | 
|---|
| 15838 | return -ENOMEM; | 
|---|
| 15839 |  | 
|---|
| 15840 | func->cookie = cfg80211_assign_cookie(rdev); | 
|---|
| 15841 |  | 
|---|
| 15842 | if (!tb[NL80211_NAN_FUNC_TYPE]) { | 
|---|
| 15843 | err = -EINVAL; | 
|---|
| 15844 | goto out; | 
|---|
| 15845 | } | 
|---|
| 15846 |  | 
|---|
| 15847 |  | 
|---|
| 15848 | func->type = nla_get_u8(nla: tb[NL80211_NAN_FUNC_TYPE]); | 
|---|
| 15849 |  | 
|---|
| 15850 | if (!tb[NL80211_NAN_FUNC_SERVICE_ID]) { | 
|---|
| 15851 | err = -EINVAL; | 
|---|
| 15852 | goto out; | 
|---|
| 15853 | } | 
|---|
| 15854 |  | 
|---|
| 15855 | memcpy(to: func->service_id, from: nla_data(nla: tb[NL80211_NAN_FUNC_SERVICE_ID]), | 
|---|
| 15856 | len: sizeof(func->service_id)); | 
|---|
| 15857 |  | 
|---|
| 15858 | func->close_range = | 
|---|
| 15859 | nla_get_flag(nla: tb[NL80211_NAN_FUNC_CLOSE_RANGE]); | 
|---|
| 15860 |  | 
|---|
| 15861 | if (tb[NL80211_NAN_FUNC_SERVICE_INFO]) { | 
|---|
| 15862 | func->serv_spec_info_len = | 
|---|
| 15863 | nla_len(nla: tb[NL80211_NAN_FUNC_SERVICE_INFO]); | 
|---|
| 15864 | func->serv_spec_info = | 
|---|
| 15865 | kmemdup(nla_data(tb[NL80211_NAN_FUNC_SERVICE_INFO]), | 
|---|
| 15866 | func->serv_spec_info_len, | 
|---|
| 15867 | GFP_KERNEL); | 
|---|
| 15868 | if (!func->serv_spec_info) { | 
|---|
| 15869 | err = -ENOMEM; | 
|---|
| 15870 | goto out; | 
|---|
| 15871 | } | 
|---|
| 15872 | } | 
|---|
| 15873 |  | 
|---|
| 15874 | if (tb[NL80211_NAN_FUNC_TTL]) | 
|---|
| 15875 | func->ttl = nla_get_u32(nla: tb[NL80211_NAN_FUNC_TTL]); | 
|---|
| 15876 |  | 
|---|
| 15877 | switch (func->type) { | 
|---|
| 15878 | case NL80211_NAN_FUNC_PUBLISH: | 
|---|
| 15879 | if (!tb[NL80211_NAN_FUNC_PUBLISH_TYPE]) { | 
|---|
| 15880 | err = -EINVAL; | 
|---|
| 15881 | goto out; | 
|---|
| 15882 | } | 
|---|
| 15883 |  | 
|---|
| 15884 | func->publish_type = | 
|---|
| 15885 | nla_get_u8(nla: tb[NL80211_NAN_FUNC_PUBLISH_TYPE]); | 
|---|
| 15886 | func->publish_bcast = | 
|---|
| 15887 | nla_get_flag(nla: tb[NL80211_NAN_FUNC_PUBLISH_BCAST]); | 
|---|
| 15888 |  | 
|---|
| 15889 | if ((!(func->publish_type & NL80211_NAN_SOLICITED_PUBLISH)) && | 
|---|
| 15890 | func->publish_bcast) { | 
|---|
| 15891 | err = -EINVAL; | 
|---|
| 15892 | goto out; | 
|---|
| 15893 | } | 
|---|
| 15894 | break; | 
|---|
| 15895 | case NL80211_NAN_FUNC_SUBSCRIBE: | 
|---|
| 15896 | func->subscribe_active = | 
|---|
| 15897 | nla_get_flag(nla: tb[NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE]); | 
|---|
| 15898 | break; | 
|---|
| 15899 | case NL80211_NAN_FUNC_FOLLOW_UP: | 
|---|
| 15900 | if (!tb[NL80211_NAN_FUNC_FOLLOW_UP_ID] || | 
|---|
| 15901 | !tb[NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID] || | 
|---|
| 15902 | !tb[NL80211_NAN_FUNC_FOLLOW_UP_DEST]) { | 
|---|
| 15903 | err = -EINVAL; | 
|---|
| 15904 | goto out; | 
|---|
| 15905 | } | 
|---|
| 15906 |  | 
|---|
| 15907 | func->followup_id = | 
|---|
| 15908 | nla_get_u8(nla: tb[NL80211_NAN_FUNC_FOLLOW_UP_ID]); | 
|---|
| 15909 | func->followup_reqid = | 
|---|
| 15910 | nla_get_u8(nla: tb[NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID]); | 
|---|
| 15911 | memcpy(to: func->followup_dest.addr, | 
|---|
| 15912 | from: nla_data(nla: tb[NL80211_NAN_FUNC_FOLLOW_UP_DEST]), | 
|---|
| 15913 | len: sizeof(func->followup_dest.addr)); | 
|---|
| 15914 | if (func->ttl) { | 
|---|
| 15915 | err = -EINVAL; | 
|---|
| 15916 | goto out; | 
|---|
| 15917 | } | 
|---|
| 15918 | break; | 
|---|
| 15919 | default: | 
|---|
| 15920 | err = -EINVAL; | 
|---|
| 15921 | goto out; | 
|---|
| 15922 | } | 
|---|
| 15923 |  | 
|---|
| 15924 | if (tb[NL80211_NAN_FUNC_SRF]) { | 
|---|
| 15925 | struct nlattr *srf_tb[NUM_NL80211_NAN_SRF_ATTR]; | 
|---|
| 15926 |  | 
|---|
| 15927 | err = nla_parse_nested_deprecated(tb: srf_tb, | 
|---|
| 15928 | maxtype: NL80211_NAN_SRF_ATTR_MAX, | 
|---|
| 15929 | nla: tb[NL80211_NAN_FUNC_SRF], | 
|---|
| 15930 | policy: nl80211_nan_srf_policy, | 
|---|
| 15931 | extack: info->extack); | 
|---|
| 15932 | if (err) | 
|---|
| 15933 | goto out; | 
|---|
| 15934 |  | 
|---|
| 15935 | func->srf_include = | 
|---|
| 15936 | nla_get_flag(nla: srf_tb[NL80211_NAN_SRF_INCLUDE]); | 
|---|
| 15937 |  | 
|---|
| 15938 | if (srf_tb[NL80211_NAN_SRF_BF]) { | 
|---|
| 15939 | if (srf_tb[NL80211_NAN_SRF_MAC_ADDRS] || | 
|---|
| 15940 | !srf_tb[NL80211_NAN_SRF_BF_IDX]) { | 
|---|
| 15941 | err = -EINVAL; | 
|---|
| 15942 | goto out; | 
|---|
| 15943 | } | 
|---|
| 15944 |  | 
|---|
| 15945 | func->srf_bf_len = | 
|---|
| 15946 | nla_len(nla: srf_tb[NL80211_NAN_SRF_BF]); | 
|---|
| 15947 | func->srf_bf = | 
|---|
| 15948 | kmemdup(nla_data(srf_tb[NL80211_NAN_SRF_BF]), | 
|---|
| 15949 | func->srf_bf_len, GFP_KERNEL); | 
|---|
| 15950 | if (!func->srf_bf) { | 
|---|
| 15951 | err = -ENOMEM; | 
|---|
| 15952 | goto out; | 
|---|
| 15953 | } | 
|---|
| 15954 |  | 
|---|
| 15955 | func->srf_bf_idx = | 
|---|
| 15956 | nla_get_u8(nla: srf_tb[NL80211_NAN_SRF_BF_IDX]); | 
|---|
| 15957 | } else { | 
|---|
| 15958 | struct nlattr *attr, *mac_attr = | 
|---|
| 15959 | srf_tb[NL80211_NAN_SRF_MAC_ADDRS]; | 
|---|
| 15960 | int n_entries, rem, i = 0; | 
|---|
| 15961 |  | 
|---|
| 15962 | if (!mac_attr) { | 
|---|
| 15963 | err = -EINVAL; | 
|---|
| 15964 | goto out; | 
|---|
| 15965 | } | 
|---|
| 15966 |  | 
|---|
| 15967 | n_entries = validate_acl_mac_addrs(nl_attr: mac_attr); | 
|---|
| 15968 | if (n_entries <= 0) { | 
|---|
| 15969 | err = -EINVAL; | 
|---|
| 15970 | goto out; | 
|---|
| 15971 | } | 
|---|
| 15972 |  | 
|---|
| 15973 | func->srf_num_macs = n_entries; | 
|---|
| 15974 | func->srf_macs = | 
|---|
| 15975 | kcalloc(n_entries, sizeof(*func->srf_macs), | 
|---|
| 15976 | GFP_KERNEL); | 
|---|
| 15977 | if (!func->srf_macs) { | 
|---|
| 15978 | err = -ENOMEM; | 
|---|
| 15979 | goto out; | 
|---|
| 15980 | } | 
|---|
| 15981 |  | 
|---|
| 15982 | nla_for_each_nested(attr, mac_attr, rem) | 
|---|
| 15983 | memcpy(to: func->srf_macs[i++].addr, from: nla_data(nla: attr), | 
|---|
| 15984 | len: sizeof(*func->srf_macs)); | 
|---|
| 15985 | } | 
|---|
| 15986 | } | 
|---|
| 15987 |  | 
|---|
| 15988 | if (tb[NL80211_NAN_FUNC_TX_MATCH_FILTER]) { | 
|---|
| 15989 | err = handle_nan_filter(attr_filter: tb[NL80211_NAN_FUNC_TX_MATCH_FILTER], | 
|---|
| 15990 | func, tx: true); | 
|---|
| 15991 | if (err) | 
|---|
| 15992 | goto out; | 
|---|
| 15993 | } | 
|---|
| 15994 |  | 
|---|
| 15995 | if (tb[NL80211_NAN_FUNC_RX_MATCH_FILTER]) { | 
|---|
| 15996 | err = handle_nan_filter(attr_filter: tb[NL80211_NAN_FUNC_RX_MATCH_FILTER], | 
|---|
| 15997 | func, tx: false); | 
|---|
| 15998 | if (err) | 
|---|
| 15999 | goto out; | 
|---|
| 16000 | } | 
|---|
| 16001 |  | 
|---|
| 16002 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 16003 | if (!msg) { | 
|---|
| 16004 | err = -ENOMEM; | 
|---|
| 16005 | goto out; | 
|---|
| 16006 | } | 
|---|
| 16007 |  | 
|---|
| 16008 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, | 
|---|
| 16009 | cmd: NL80211_CMD_ADD_NAN_FUNCTION); | 
|---|
| 16010 | /* This can't really happen - we just allocated 4KB */ | 
|---|
| 16011 | if (WARN_ON(!hdr)) { | 
|---|
| 16012 | err = -ENOMEM; | 
|---|
| 16013 | goto out; | 
|---|
| 16014 | } | 
|---|
| 16015 |  | 
|---|
| 16016 | err = rdev_add_nan_func(rdev, wdev, nan_func: func); | 
|---|
| 16017 | out: | 
|---|
| 16018 | if (err < 0) { | 
|---|
| 16019 | cfg80211_free_nan_func(f: func); | 
|---|
| 16020 | nlmsg_free(skb: msg); | 
|---|
| 16021 | return err; | 
|---|
| 16022 | } | 
|---|
| 16023 |  | 
|---|
| 16024 | /* propagate the instance id and cookie to userspace  */ | 
|---|
| 16025 | if (nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: func->cookie, | 
|---|
| 16026 | padattr: NL80211_ATTR_PAD)) | 
|---|
| 16027 | goto nla_put_failure; | 
|---|
| 16028 |  | 
|---|
| 16029 | func_attr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_NAN_FUNC); | 
|---|
| 16030 | if (!func_attr) | 
|---|
| 16031 | goto nla_put_failure; | 
|---|
| 16032 |  | 
|---|
| 16033 | if (nla_put_u8(skb: msg, attrtype: NL80211_NAN_FUNC_INSTANCE_ID, | 
|---|
| 16034 | value: func->instance_id)) | 
|---|
| 16035 | goto nla_put_failure; | 
|---|
| 16036 |  | 
|---|
| 16037 | nla_nest_end(skb: msg, start: func_attr); | 
|---|
| 16038 |  | 
|---|
| 16039 | genlmsg_end(skb: msg, hdr); | 
|---|
| 16040 | return genlmsg_reply(skb: msg, info); | 
|---|
| 16041 |  | 
|---|
| 16042 | nla_put_failure: | 
|---|
| 16043 | nlmsg_free(skb: msg); | 
|---|
| 16044 | return -ENOBUFS; | 
|---|
| 16045 | } | 
|---|
| 16046 |  | 
|---|
| 16047 | static int nl80211_nan_del_func(struct sk_buff *skb, | 
|---|
| 16048 | struct genl_info *info) | 
|---|
| 16049 | { | 
|---|
| 16050 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 16051 | struct wireless_dev *wdev = info->user_ptr[1]; | 
|---|
| 16052 | u64 cookie; | 
|---|
| 16053 |  | 
|---|
| 16054 | if (wdev->iftype != NL80211_IFTYPE_NAN) | 
|---|
| 16055 | return -EOPNOTSUPP; | 
|---|
| 16056 |  | 
|---|
| 16057 | if (!wdev_running(wdev)) | 
|---|
| 16058 | return -ENOTCONN; | 
|---|
| 16059 |  | 
|---|
| 16060 | if (!info->attrs[NL80211_ATTR_COOKIE]) | 
|---|
| 16061 | return -EINVAL; | 
|---|
| 16062 |  | 
|---|
| 16063 | cookie = nla_get_u64(nla: info->attrs[NL80211_ATTR_COOKIE]); | 
|---|
| 16064 |  | 
|---|
| 16065 | rdev_del_nan_func(rdev, wdev, cookie); | 
|---|
| 16066 |  | 
|---|
| 16067 | return 0; | 
|---|
| 16068 | } | 
|---|
| 16069 |  | 
|---|
| 16070 | static int nl80211_nan_change_config(struct sk_buff *skb, | 
|---|
| 16071 | struct genl_info *info) | 
|---|
| 16072 | { | 
|---|
| 16073 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 16074 | struct wireless_dev *wdev = info->user_ptr[1]; | 
|---|
| 16075 | struct cfg80211_nan_conf conf = {}; | 
|---|
| 16076 | u32 changed = 0; | 
|---|
| 16077 | int err; | 
|---|
| 16078 |  | 
|---|
| 16079 | if (wdev->iftype != NL80211_IFTYPE_NAN) | 
|---|
| 16080 | return -EOPNOTSUPP; | 
|---|
| 16081 |  | 
|---|
| 16082 | if (!wdev_running(wdev)) | 
|---|
| 16083 | return -ENOTCONN; | 
|---|
| 16084 |  | 
|---|
| 16085 | err = nl80211_parse_nan_conf(wiphy: &rdev->wiphy, info, conf: &conf, changed_flags: &changed); | 
|---|
| 16086 | if (err) | 
|---|
| 16087 | return err; | 
|---|
| 16088 |  | 
|---|
| 16089 | if (!changed) | 
|---|
| 16090 | return -EINVAL; | 
|---|
| 16091 |  | 
|---|
| 16092 | return rdev_nan_change_conf(rdev, wdev, conf: &conf, changes: changed); | 
|---|
| 16093 | } | 
|---|
| 16094 |  | 
|---|
| 16095 | void cfg80211_nan_match(struct wireless_dev *wdev, | 
|---|
| 16096 | struct cfg80211_nan_match_params *match, gfp_t gfp) | 
|---|
| 16097 | { | 
|---|
| 16098 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 16099 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 16100 | struct nlattr *match_attr, *local_func_attr, *peer_func_attr; | 
|---|
| 16101 | struct sk_buff *msg; | 
|---|
| 16102 | void *hdr; | 
|---|
| 16103 |  | 
|---|
| 16104 | if (WARN_ON(!match->inst_id || !match->peer_inst_id || !match->addr)) | 
|---|
| 16105 | return; | 
|---|
| 16106 |  | 
|---|
| 16107 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 16108 | if (!msg) | 
|---|
| 16109 | return; | 
|---|
| 16110 |  | 
|---|
| 16111 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_NAN_MATCH); | 
|---|
| 16112 | if (!hdr) { | 
|---|
| 16113 | nlmsg_free(skb: msg); | 
|---|
| 16114 | return; | 
|---|
| 16115 | } | 
|---|
| 16116 |  | 
|---|
| 16117 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 16118 | (wdev->netdev && nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, | 
|---|
| 16119 | value: wdev->netdev->ifindex)) || | 
|---|
| 16120 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), | 
|---|
| 16121 | padattr: NL80211_ATTR_PAD)) | 
|---|
| 16122 | goto nla_put_failure; | 
|---|
| 16123 |  | 
|---|
| 16124 | if (nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: match->cookie, | 
|---|
| 16125 | padattr: NL80211_ATTR_PAD) || | 
|---|
| 16126 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: match->addr)) | 
|---|
| 16127 | goto nla_put_failure; | 
|---|
| 16128 |  | 
|---|
| 16129 | match_attr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_NAN_MATCH); | 
|---|
| 16130 | if (!match_attr) | 
|---|
| 16131 | goto nla_put_failure; | 
|---|
| 16132 |  | 
|---|
| 16133 | local_func_attr = nla_nest_start_noflag(skb: msg, | 
|---|
| 16134 | attrtype: NL80211_NAN_MATCH_FUNC_LOCAL); | 
|---|
| 16135 | if (!local_func_attr) | 
|---|
| 16136 | goto nla_put_failure; | 
|---|
| 16137 |  | 
|---|
| 16138 | if (nla_put_u8(skb: msg, attrtype: NL80211_NAN_FUNC_INSTANCE_ID, value: match->inst_id)) | 
|---|
| 16139 | goto nla_put_failure; | 
|---|
| 16140 |  | 
|---|
| 16141 | nla_nest_end(skb: msg, start: local_func_attr); | 
|---|
| 16142 |  | 
|---|
| 16143 | peer_func_attr = nla_nest_start_noflag(skb: msg, | 
|---|
| 16144 | attrtype: NL80211_NAN_MATCH_FUNC_PEER); | 
|---|
| 16145 | if (!peer_func_attr) | 
|---|
| 16146 | goto nla_put_failure; | 
|---|
| 16147 |  | 
|---|
| 16148 | if (nla_put_u8(skb: msg, attrtype: NL80211_NAN_FUNC_TYPE, value: match->type) || | 
|---|
| 16149 | nla_put_u8(skb: msg, attrtype: NL80211_NAN_FUNC_INSTANCE_ID, value: match->peer_inst_id)) | 
|---|
| 16150 | goto nla_put_failure; | 
|---|
| 16151 |  | 
|---|
| 16152 | if (match->info && match->info_len && | 
|---|
| 16153 | nla_put(skb: msg, attrtype: NL80211_NAN_FUNC_SERVICE_INFO, attrlen: match->info_len, | 
|---|
| 16154 | data: match->info)) | 
|---|
| 16155 | goto nla_put_failure; | 
|---|
| 16156 |  | 
|---|
| 16157 | nla_nest_end(skb: msg, start: peer_func_attr); | 
|---|
| 16158 | nla_nest_end(skb: msg, start: match_attr); | 
|---|
| 16159 | genlmsg_end(skb: msg, hdr); | 
|---|
| 16160 |  | 
|---|
| 16161 | if (!wdev->owner_nlportid) | 
|---|
| 16162 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), | 
|---|
| 16163 | skb: msg, portid: 0, group: NL80211_MCGRP_NAN, flags: gfp); | 
|---|
| 16164 | else | 
|---|
| 16165 | genlmsg_unicast(net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, | 
|---|
| 16166 | portid: wdev->owner_nlportid); | 
|---|
| 16167 |  | 
|---|
| 16168 | return; | 
|---|
| 16169 |  | 
|---|
| 16170 | nla_put_failure: | 
|---|
| 16171 | nlmsg_free(skb: msg); | 
|---|
| 16172 | } | 
|---|
| 16173 | EXPORT_SYMBOL(cfg80211_nan_match); | 
|---|
| 16174 |  | 
|---|
| 16175 | void cfg80211_nan_func_terminated(struct wireless_dev *wdev, | 
|---|
| 16176 | u8 inst_id, | 
|---|
| 16177 | enum nl80211_nan_func_term_reason reason, | 
|---|
| 16178 | u64 cookie, gfp_t gfp) | 
|---|
| 16179 | { | 
|---|
| 16180 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 16181 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 16182 | struct sk_buff *msg; | 
|---|
| 16183 | struct nlattr *func_attr; | 
|---|
| 16184 | void *hdr; | 
|---|
| 16185 |  | 
|---|
| 16186 | if (WARN_ON(!inst_id)) | 
|---|
| 16187 | return; | 
|---|
| 16188 |  | 
|---|
| 16189 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 16190 | if (!msg) | 
|---|
| 16191 | return; | 
|---|
| 16192 |  | 
|---|
| 16193 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_DEL_NAN_FUNCTION); | 
|---|
| 16194 | if (!hdr) { | 
|---|
| 16195 | nlmsg_free(skb: msg); | 
|---|
| 16196 | return; | 
|---|
| 16197 | } | 
|---|
| 16198 |  | 
|---|
| 16199 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 16200 | (wdev->netdev && nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, | 
|---|
| 16201 | value: wdev->netdev->ifindex)) || | 
|---|
| 16202 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), | 
|---|
| 16203 | padattr: NL80211_ATTR_PAD)) | 
|---|
| 16204 | goto nla_put_failure; | 
|---|
| 16205 |  | 
|---|
| 16206 | if (nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: cookie, | 
|---|
| 16207 | padattr: NL80211_ATTR_PAD)) | 
|---|
| 16208 | goto nla_put_failure; | 
|---|
| 16209 |  | 
|---|
| 16210 | func_attr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_NAN_FUNC); | 
|---|
| 16211 | if (!func_attr) | 
|---|
| 16212 | goto nla_put_failure; | 
|---|
| 16213 |  | 
|---|
| 16214 | if (nla_put_u8(skb: msg, attrtype: NL80211_NAN_FUNC_INSTANCE_ID, value: inst_id) || | 
|---|
| 16215 | nla_put_u8(skb: msg, attrtype: NL80211_NAN_FUNC_TERM_REASON, value: reason)) | 
|---|
| 16216 | goto nla_put_failure; | 
|---|
| 16217 |  | 
|---|
| 16218 | nla_nest_end(skb: msg, start: func_attr); | 
|---|
| 16219 | genlmsg_end(skb: msg, hdr); | 
|---|
| 16220 |  | 
|---|
| 16221 | if (!wdev->owner_nlportid) | 
|---|
| 16222 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), | 
|---|
| 16223 | skb: msg, portid: 0, group: NL80211_MCGRP_NAN, flags: gfp); | 
|---|
| 16224 | else | 
|---|
| 16225 | genlmsg_unicast(net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, | 
|---|
| 16226 | portid: wdev->owner_nlportid); | 
|---|
| 16227 |  | 
|---|
| 16228 | return; | 
|---|
| 16229 |  | 
|---|
| 16230 | nla_put_failure: | 
|---|
| 16231 | nlmsg_free(skb: msg); | 
|---|
| 16232 | } | 
|---|
| 16233 | EXPORT_SYMBOL(cfg80211_nan_func_terminated); | 
|---|
| 16234 |  | 
|---|
| 16235 | static int nl80211_get_protocol_features(struct sk_buff *skb, | 
|---|
| 16236 | struct genl_info *info) | 
|---|
| 16237 | { | 
|---|
| 16238 | void *hdr; | 
|---|
| 16239 | struct sk_buff *msg; | 
|---|
| 16240 |  | 
|---|
| 16241 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 16242 | if (!msg) | 
|---|
| 16243 | return -ENOMEM; | 
|---|
| 16244 |  | 
|---|
| 16245 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, | 
|---|
| 16246 | cmd: NL80211_CMD_GET_PROTOCOL_FEATURES); | 
|---|
| 16247 | if (!hdr) | 
|---|
| 16248 | goto nla_put_failure; | 
|---|
| 16249 |  | 
|---|
| 16250 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_PROTOCOL_FEATURES, | 
|---|
| 16251 | value: NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)) | 
|---|
| 16252 | goto nla_put_failure; | 
|---|
| 16253 |  | 
|---|
| 16254 | genlmsg_end(skb: msg, hdr); | 
|---|
| 16255 | return genlmsg_reply(skb: msg, info); | 
|---|
| 16256 |  | 
|---|
| 16257 | nla_put_failure: | 
|---|
| 16258 | kfree_skb(skb: msg); | 
|---|
| 16259 | return -ENOBUFS; | 
|---|
| 16260 | } | 
|---|
| 16261 |  | 
|---|
| 16262 | static int nl80211_update_ft_ies(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 16263 | { | 
|---|
| 16264 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 16265 | struct cfg80211_update_ft_ies_params ft_params; | 
|---|
| 16266 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 16267 |  | 
|---|
| 16268 | if (!rdev->ops->update_ft_ies) | 
|---|
| 16269 | return -EOPNOTSUPP; | 
|---|
| 16270 |  | 
|---|
| 16271 | if (!info->attrs[NL80211_ATTR_MDID] || | 
|---|
| 16272 | !info->attrs[NL80211_ATTR_IE]) | 
|---|
| 16273 | return -EINVAL; | 
|---|
| 16274 |  | 
|---|
| 16275 | memset(s: &ft_params, c: 0, n: sizeof(ft_params)); | 
|---|
| 16276 | ft_params.md = nla_get_u16(nla: info->attrs[NL80211_ATTR_MDID]); | 
|---|
| 16277 | ft_params.ie = nla_data(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 16278 | ft_params.ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 16279 |  | 
|---|
| 16280 | return rdev_update_ft_ies(rdev, dev, ftie: &ft_params); | 
|---|
| 16281 | } | 
|---|
| 16282 |  | 
|---|
| 16283 | static int nl80211_crit_protocol_start(struct sk_buff *skb, | 
|---|
| 16284 | struct genl_info *info) | 
|---|
| 16285 | { | 
|---|
| 16286 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 16287 | struct wireless_dev *wdev = info->user_ptr[1]; | 
|---|
| 16288 | enum nl80211_crit_proto_id proto = NL80211_CRIT_PROTO_UNSPEC; | 
|---|
| 16289 | u16 duration; | 
|---|
| 16290 | int ret; | 
|---|
| 16291 |  | 
|---|
| 16292 | if (!rdev->ops->crit_proto_start) | 
|---|
| 16293 | return -EOPNOTSUPP; | 
|---|
| 16294 |  | 
|---|
| 16295 | if (WARN_ON(!rdev->ops->crit_proto_stop)) | 
|---|
| 16296 | return -EINVAL; | 
|---|
| 16297 |  | 
|---|
| 16298 | if (rdev->crit_proto_nlportid) | 
|---|
| 16299 | return -EBUSY; | 
|---|
| 16300 |  | 
|---|
| 16301 | /* determine protocol if provided */ | 
|---|
| 16302 | if (info->attrs[NL80211_ATTR_CRIT_PROT_ID]) | 
|---|
| 16303 | proto = nla_get_u16(nla: info->attrs[NL80211_ATTR_CRIT_PROT_ID]); | 
|---|
| 16304 |  | 
|---|
| 16305 | if (proto >= NUM_NL80211_CRIT_PROTO) | 
|---|
| 16306 | return -EINVAL; | 
|---|
| 16307 |  | 
|---|
| 16308 | /* timeout must be provided */ | 
|---|
| 16309 | if (!info->attrs[NL80211_ATTR_MAX_CRIT_PROT_DURATION]) | 
|---|
| 16310 | return -EINVAL; | 
|---|
| 16311 |  | 
|---|
| 16312 | duration = | 
|---|
| 16313 | nla_get_u16(nla: info->attrs[NL80211_ATTR_MAX_CRIT_PROT_DURATION]); | 
|---|
| 16314 |  | 
|---|
| 16315 | ret = rdev_crit_proto_start(rdev, wdev, protocol: proto, duration); | 
|---|
| 16316 | if (!ret) | 
|---|
| 16317 | rdev->crit_proto_nlportid = info->snd_portid; | 
|---|
| 16318 |  | 
|---|
| 16319 | return ret; | 
|---|
| 16320 | } | 
|---|
| 16321 |  | 
|---|
| 16322 | static int nl80211_crit_protocol_stop(struct sk_buff *skb, | 
|---|
| 16323 | struct genl_info *info) | 
|---|
| 16324 | { | 
|---|
| 16325 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 16326 | struct wireless_dev *wdev = info->user_ptr[1]; | 
|---|
| 16327 |  | 
|---|
| 16328 | if (!rdev->ops->crit_proto_stop) | 
|---|
| 16329 | return -EOPNOTSUPP; | 
|---|
| 16330 |  | 
|---|
| 16331 | if (rdev->crit_proto_nlportid) { | 
|---|
| 16332 | rdev->crit_proto_nlportid = 0; | 
|---|
| 16333 | rdev_crit_proto_stop(rdev, wdev); | 
|---|
| 16334 | } | 
|---|
| 16335 | return 0; | 
|---|
| 16336 | } | 
|---|
| 16337 |  | 
|---|
| 16338 | static int nl80211_vendor_check_policy(const struct wiphy_vendor_command *vcmd, | 
|---|
| 16339 | struct nlattr *attr, | 
|---|
| 16340 | struct netlink_ext_ack *extack) | 
|---|
| 16341 | { | 
|---|
| 16342 | if (vcmd->policy == VENDOR_CMD_RAW_DATA) { | 
|---|
| 16343 | if (attr->nla_type & NLA_F_NESTED) { | 
|---|
| 16344 | NL_SET_ERR_MSG_ATTR(extack, attr, | 
|---|
| 16345 | "unexpected nested data"); | 
|---|
| 16346 | return -EINVAL; | 
|---|
| 16347 | } | 
|---|
| 16348 |  | 
|---|
| 16349 | return 0; | 
|---|
| 16350 | } | 
|---|
| 16351 |  | 
|---|
| 16352 | if (!(attr->nla_type & NLA_F_NESTED)) { | 
|---|
| 16353 | NL_SET_ERR_MSG_ATTR(extack, attr, "expected nested data"); | 
|---|
| 16354 | return -EINVAL; | 
|---|
| 16355 | } | 
|---|
| 16356 |  | 
|---|
| 16357 | return nla_validate_nested(start: attr, maxtype: vcmd->maxattr, policy: vcmd->policy, extack); | 
|---|
| 16358 | } | 
|---|
| 16359 |  | 
|---|
| 16360 | static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 16361 | { | 
|---|
| 16362 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 16363 | struct wireless_dev *wdev = | 
|---|
| 16364 | __cfg80211_wdev_from_attrs(rdev, netns: genl_info_net(info), | 
|---|
| 16365 | attrs: info->attrs); | 
|---|
| 16366 | int i, err; | 
|---|
| 16367 | u32 vid, subcmd; | 
|---|
| 16368 |  | 
|---|
| 16369 | if (!rdev->wiphy.vendor_commands) | 
|---|
| 16370 | return -EOPNOTSUPP; | 
|---|
| 16371 |  | 
|---|
| 16372 | if (IS_ERR(ptr: wdev)) { | 
|---|
| 16373 | err = PTR_ERR(ptr: wdev); | 
|---|
| 16374 | if (err != -EINVAL) | 
|---|
| 16375 | return err; | 
|---|
| 16376 | wdev = NULL; | 
|---|
| 16377 | } else if (wdev->wiphy != &rdev->wiphy) { | 
|---|
| 16378 | return -EINVAL; | 
|---|
| 16379 | } | 
|---|
| 16380 |  | 
|---|
| 16381 | if (!info->attrs[NL80211_ATTR_VENDOR_ID] || | 
|---|
| 16382 | !info->attrs[NL80211_ATTR_VENDOR_SUBCMD]) | 
|---|
| 16383 | return -EINVAL; | 
|---|
| 16384 |  | 
|---|
| 16385 | vid = nla_get_u32(nla: info->attrs[NL80211_ATTR_VENDOR_ID]); | 
|---|
| 16386 | subcmd = nla_get_u32(nla: info->attrs[NL80211_ATTR_VENDOR_SUBCMD]); | 
|---|
| 16387 | for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) { | 
|---|
| 16388 | const struct wiphy_vendor_command *vcmd; | 
|---|
| 16389 | void *data = NULL; | 
|---|
| 16390 | int len = 0; | 
|---|
| 16391 |  | 
|---|
| 16392 | vcmd = &rdev->wiphy.vendor_commands[i]; | 
|---|
| 16393 |  | 
|---|
| 16394 | if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd) | 
|---|
| 16395 | continue; | 
|---|
| 16396 |  | 
|---|
| 16397 | if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV | | 
|---|
| 16398 | WIPHY_VENDOR_CMD_NEED_NETDEV)) { | 
|---|
| 16399 | if (!wdev) | 
|---|
| 16400 | return -EINVAL; | 
|---|
| 16401 | if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV && | 
|---|
| 16402 | !wdev->netdev) | 
|---|
| 16403 | return -EINVAL; | 
|---|
| 16404 |  | 
|---|
| 16405 | if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) { | 
|---|
| 16406 | if (!wdev_running(wdev)) | 
|---|
| 16407 | return -ENETDOWN; | 
|---|
| 16408 | } | 
|---|
| 16409 | } else { | 
|---|
| 16410 | wdev = NULL; | 
|---|
| 16411 | } | 
|---|
| 16412 |  | 
|---|
| 16413 | if (!vcmd->doit) | 
|---|
| 16414 | return -EOPNOTSUPP; | 
|---|
| 16415 |  | 
|---|
| 16416 | if (info->attrs[NL80211_ATTR_VENDOR_DATA]) { | 
|---|
| 16417 | data = nla_data(nla: info->attrs[NL80211_ATTR_VENDOR_DATA]); | 
|---|
| 16418 | len = nla_len(nla: info->attrs[NL80211_ATTR_VENDOR_DATA]); | 
|---|
| 16419 |  | 
|---|
| 16420 | err = nl80211_vendor_check_policy(vcmd, | 
|---|
| 16421 | attr: info->attrs[NL80211_ATTR_VENDOR_DATA], | 
|---|
| 16422 | extack: info->extack); | 
|---|
| 16423 | if (err) | 
|---|
| 16424 | return err; | 
|---|
| 16425 | } | 
|---|
| 16426 |  | 
|---|
| 16427 | rdev->cur_cmd_info = info; | 
|---|
| 16428 | err = vcmd->doit(&rdev->wiphy, wdev, data, len); | 
|---|
| 16429 | rdev->cur_cmd_info = NULL; | 
|---|
| 16430 | return err; | 
|---|
| 16431 | } | 
|---|
| 16432 |  | 
|---|
| 16433 | return -EOPNOTSUPP; | 
|---|
| 16434 | } | 
|---|
| 16435 |  | 
|---|
| 16436 | static int nl80211_prepare_vendor_dump(struct sk_buff *skb, | 
|---|
| 16437 | struct netlink_callback *cb, | 
|---|
| 16438 | struct cfg80211_registered_device **rdev, | 
|---|
| 16439 | struct wireless_dev **wdev) | 
|---|
| 16440 | { | 
|---|
| 16441 | struct nlattr **attrbuf; | 
|---|
| 16442 | u32 vid, subcmd; | 
|---|
| 16443 | unsigned int i; | 
|---|
| 16444 | int vcmd_idx = -1; | 
|---|
| 16445 | int err; | 
|---|
| 16446 | void *data = NULL; | 
|---|
| 16447 | unsigned int data_len = 0; | 
|---|
| 16448 |  | 
|---|
| 16449 | if (cb->args[0]) { | 
|---|
| 16450 | /* subtract the 1 again here */ | 
|---|
| 16451 | struct wiphy *wiphy = wiphy_idx_to_wiphy(wiphy_idx: cb->args[0] - 1); | 
|---|
| 16452 | struct wireless_dev *tmp; | 
|---|
| 16453 |  | 
|---|
| 16454 | if (!wiphy) | 
|---|
| 16455 | return -ENODEV; | 
|---|
| 16456 | *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 16457 | *wdev = NULL; | 
|---|
| 16458 |  | 
|---|
| 16459 | if (cb->args[1]) { | 
|---|
| 16460 | list_for_each_entry(tmp, &wiphy->wdev_list, list) { | 
|---|
| 16461 | if (tmp->identifier == cb->args[1] - 1) { | 
|---|
| 16462 | *wdev = tmp; | 
|---|
| 16463 | break; | 
|---|
| 16464 | } | 
|---|
| 16465 | } | 
|---|
| 16466 | } | 
|---|
| 16467 |  | 
|---|
| 16468 | /* keep rtnl locked in successful case */ | 
|---|
| 16469 | return 0; | 
|---|
| 16470 | } | 
|---|
| 16471 |  | 
|---|
| 16472 | attrbuf = kcalloc(NUM_NL80211_ATTR, sizeof(*attrbuf), GFP_KERNEL); | 
|---|
| 16473 | if (!attrbuf) | 
|---|
| 16474 | return -ENOMEM; | 
|---|
| 16475 |  | 
|---|
| 16476 | err = nlmsg_parse_deprecated(nlh: cb->nlh, | 
|---|
| 16477 | GENL_HDRLEN + nl80211_fam.hdrsize, | 
|---|
| 16478 | tb: attrbuf, maxtype: nl80211_fam.maxattr, | 
|---|
| 16479 | policy: nl80211_policy, NULL); | 
|---|
| 16480 | if (err) | 
|---|
| 16481 | goto out; | 
|---|
| 16482 |  | 
|---|
| 16483 | if (!attrbuf[NL80211_ATTR_VENDOR_ID] || | 
|---|
| 16484 | !attrbuf[NL80211_ATTR_VENDOR_SUBCMD]) { | 
|---|
| 16485 | err = -EINVAL; | 
|---|
| 16486 | goto out; | 
|---|
| 16487 | } | 
|---|
| 16488 |  | 
|---|
| 16489 | *wdev = __cfg80211_wdev_from_attrs(NULL, netns: sock_net(sk: skb->sk), attrs: attrbuf); | 
|---|
| 16490 | if (IS_ERR(ptr: *wdev)) | 
|---|
| 16491 | *wdev = NULL; | 
|---|
| 16492 |  | 
|---|
| 16493 | *rdev = __cfg80211_rdev_from_attrs(netns: sock_net(sk: skb->sk), attrs: attrbuf); | 
|---|
| 16494 | if (IS_ERR(ptr: *rdev)) { | 
|---|
| 16495 | err = PTR_ERR(ptr: *rdev); | 
|---|
| 16496 | goto out; | 
|---|
| 16497 | } | 
|---|
| 16498 |  | 
|---|
| 16499 | vid = nla_get_u32(nla: attrbuf[NL80211_ATTR_VENDOR_ID]); | 
|---|
| 16500 | subcmd = nla_get_u32(nla: attrbuf[NL80211_ATTR_VENDOR_SUBCMD]); | 
|---|
| 16501 |  | 
|---|
| 16502 | for (i = 0; i < (*rdev)->wiphy.n_vendor_commands; i++) { | 
|---|
| 16503 | const struct wiphy_vendor_command *vcmd; | 
|---|
| 16504 |  | 
|---|
| 16505 | vcmd = &(*rdev)->wiphy.vendor_commands[i]; | 
|---|
| 16506 |  | 
|---|
| 16507 | if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd) | 
|---|
| 16508 | continue; | 
|---|
| 16509 |  | 
|---|
| 16510 | if (!vcmd->dumpit) { | 
|---|
| 16511 | err = -EOPNOTSUPP; | 
|---|
| 16512 | goto out; | 
|---|
| 16513 | } | 
|---|
| 16514 |  | 
|---|
| 16515 | vcmd_idx = i; | 
|---|
| 16516 | break; | 
|---|
| 16517 | } | 
|---|
| 16518 |  | 
|---|
| 16519 | if (vcmd_idx < 0) { | 
|---|
| 16520 | err = -EOPNOTSUPP; | 
|---|
| 16521 | goto out; | 
|---|
| 16522 | } | 
|---|
| 16523 |  | 
|---|
| 16524 | if (attrbuf[NL80211_ATTR_VENDOR_DATA]) { | 
|---|
| 16525 | data = nla_data(nla: attrbuf[NL80211_ATTR_VENDOR_DATA]); | 
|---|
| 16526 | data_len = nla_len(nla: attrbuf[NL80211_ATTR_VENDOR_DATA]); | 
|---|
| 16527 |  | 
|---|
| 16528 | err = nl80211_vendor_check_policy( | 
|---|
| 16529 | vcmd: &(*rdev)->wiphy.vendor_commands[vcmd_idx], | 
|---|
| 16530 | attr: attrbuf[NL80211_ATTR_VENDOR_DATA], | 
|---|
| 16531 | extack: cb->extack); | 
|---|
| 16532 | if (err) | 
|---|
| 16533 | goto out; | 
|---|
| 16534 | } | 
|---|
| 16535 |  | 
|---|
| 16536 | /* 0 is the first index - add 1 to parse only once */ | 
|---|
| 16537 | cb->args[0] = (*rdev)->wiphy_idx + 1; | 
|---|
| 16538 | /* add 1 to know if it was NULL */ | 
|---|
| 16539 | cb->args[1] = *wdev ? (*wdev)->identifier + 1 : 0; | 
|---|
| 16540 | cb->args[2] = vcmd_idx; | 
|---|
| 16541 | cb->args[3] = (unsigned long)data; | 
|---|
| 16542 | cb->args[4] = data_len; | 
|---|
| 16543 |  | 
|---|
| 16544 | /* keep rtnl locked in successful case */ | 
|---|
| 16545 | err = 0; | 
|---|
| 16546 | out: | 
|---|
| 16547 | kfree(objp: attrbuf); | 
|---|
| 16548 | return err; | 
|---|
| 16549 | } | 
|---|
| 16550 |  | 
|---|
| 16551 | static int nl80211_vendor_cmd_dump(struct sk_buff *skb, | 
|---|
| 16552 | struct netlink_callback *cb) | 
|---|
| 16553 | { | 
|---|
| 16554 | struct cfg80211_registered_device *rdev; | 
|---|
| 16555 | struct wireless_dev *wdev; | 
|---|
| 16556 | unsigned int vcmd_idx; | 
|---|
| 16557 | const struct wiphy_vendor_command *vcmd; | 
|---|
| 16558 | void *data; | 
|---|
| 16559 | int data_len; | 
|---|
| 16560 | int err; | 
|---|
| 16561 | struct nlattr *vendor_data; | 
|---|
| 16562 |  | 
|---|
| 16563 | rtnl_lock(); | 
|---|
| 16564 | err = nl80211_prepare_vendor_dump(skb, cb, rdev: &rdev, wdev: &wdev); | 
|---|
| 16565 | if (err) | 
|---|
| 16566 | goto out; | 
|---|
| 16567 |  | 
|---|
| 16568 | vcmd_idx = cb->args[2]; | 
|---|
| 16569 | data = (void *)cb->args[3]; | 
|---|
| 16570 | data_len = cb->args[4]; | 
|---|
| 16571 | vcmd = &rdev->wiphy.vendor_commands[vcmd_idx]; | 
|---|
| 16572 |  | 
|---|
| 16573 | if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV | | 
|---|
| 16574 | WIPHY_VENDOR_CMD_NEED_NETDEV)) { | 
|---|
| 16575 | if (!wdev) { | 
|---|
| 16576 | err = -EINVAL; | 
|---|
| 16577 | goto out; | 
|---|
| 16578 | } | 
|---|
| 16579 | if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV && | 
|---|
| 16580 | !wdev->netdev) { | 
|---|
| 16581 | err = -EINVAL; | 
|---|
| 16582 | goto out; | 
|---|
| 16583 | } | 
|---|
| 16584 |  | 
|---|
| 16585 | if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) { | 
|---|
| 16586 | if (!wdev_running(wdev)) { | 
|---|
| 16587 | err = -ENETDOWN; | 
|---|
| 16588 | goto out; | 
|---|
| 16589 | } | 
|---|
| 16590 | } | 
|---|
| 16591 | } | 
|---|
| 16592 |  | 
|---|
| 16593 | while (1) { | 
|---|
| 16594 | void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).portid, | 
|---|
| 16595 | seq: cb->nlh->nlmsg_seq, NLM_F_MULTI, | 
|---|
| 16596 | cmd: NL80211_CMD_VENDOR); | 
|---|
| 16597 | if (!hdr) | 
|---|
| 16598 | break; | 
|---|
| 16599 |  | 
|---|
| 16600 | if (nla_put_u32(skb, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 16601 | (wdev && nla_put_u64_64bit(skb, attrtype: NL80211_ATTR_WDEV, | 
|---|
| 16602 | value: wdev_id(wdev), | 
|---|
| 16603 | padattr: NL80211_ATTR_PAD))) { | 
|---|
| 16604 | genlmsg_cancel(skb, hdr); | 
|---|
| 16605 | break; | 
|---|
| 16606 | } | 
|---|
| 16607 |  | 
|---|
| 16608 | vendor_data = nla_nest_start_noflag(skb, | 
|---|
| 16609 | attrtype: NL80211_ATTR_VENDOR_DATA); | 
|---|
| 16610 | if (!vendor_data) { | 
|---|
| 16611 | genlmsg_cancel(skb, hdr); | 
|---|
| 16612 | break; | 
|---|
| 16613 | } | 
|---|
| 16614 |  | 
|---|
| 16615 | err = vcmd->dumpit(&rdev->wiphy, wdev, skb, data, data_len, | 
|---|
| 16616 | (unsigned long *)&cb->args[5]); | 
|---|
| 16617 | nla_nest_end(skb, start: vendor_data); | 
|---|
| 16618 |  | 
|---|
| 16619 | if (err == -ENOBUFS || err == -ENOENT) { | 
|---|
| 16620 | genlmsg_cancel(skb, hdr); | 
|---|
| 16621 | break; | 
|---|
| 16622 | } else if (err <= 0) { | 
|---|
| 16623 | genlmsg_cancel(skb, hdr); | 
|---|
| 16624 | goto out; | 
|---|
| 16625 | } | 
|---|
| 16626 |  | 
|---|
| 16627 | genlmsg_end(skb, hdr); | 
|---|
| 16628 | } | 
|---|
| 16629 |  | 
|---|
| 16630 | err = skb->len; | 
|---|
| 16631 | out: | 
|---|
| 16632 | rtnl_unlock(); | 
|---|
| 16633 | return err; | 
|---|
| 16634 | } | 
|---|
| 16635 |  | 
|---|
| 16636 | struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy, | 
|---|
| 16637 | enum nl80211_commands cmd, | 
|---|
| 16638 | enum nl80211_attrs attr, | 
|---|
| 16639 | int approxlen) | 
|---|
| 16640 | { | 
|---|
| 16641 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 16642 |  | 
|---|
| 16643 | if (WARN_ON(!rdev->cur_cmd_info)) | 
|---|
| 16644 | return NULL; | 
|---|
| 16645 |  | 
|---|
| 16646 | return __cfg80211_alloc_vendor_skb(rdev, NULL, approxlen, | 
|---|
| 16647 | portid: rdev->cur_cmd_info->snd_portid, | 
|---|
| 16648 | seq: rdev->cur_cmd_info->snd_seq, | 
|---|
| 16649 | cmd, attr, NULL, GFP_KERNEL); | 
|---|
| 16650 | } | 
|---|
| 16651 | EXPORT_SYMBOL(__cfg80211_alloc_reply_skb); | 
|---|
| 16652 |  | 
|---|
| 16653 | int cfg80211_vendor_cmd_reply(struct sk_buff *skb) | 
|---|
| 16654 | { | 
|---|
| 16655 | struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; | 
|---|
| 16656 | void *hdr = ((void **)skb->cb)[1]; | 
|---|
| 16657 | struct nlattr *data = ((void **)skb->cb)[2]; | 
|---|
| 16658 |  | 
|---|
| 16659 | /* clear CB data for netlink core to own from now on */ | 
|---|
| 16660 | memset(s: skb->cb, c: 0, n: sizeof(skb->cb)); | 
|---|
| 16661 |  | 
|---|
| 16662 | if (WARN_ON(!rdev->cur_cmd_info)) { | 
|---|
| 16663 | kfree_skb(skb); | 
|---|
| 16664 | return -EINVAL; | 
|---|
| 16665 | } | 
|---|
| 16666 |  | 
|---|
| 16667 | nla_nest_end(skb, start: data); | 
|---|
| 16668 | genlmsg_end(skb, hdr); | 
|---|
| 16669 | return genlmsg_reply(skb, info: rdev->cur_cmd_info); | 
|---|
| 16670 | } | 
|---|
| 16671 | EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_reply); | 
|---|
| 16672 |  | 
|---|
| 16673 | unsigned int cfg80211_vendor_cmd_get_sender(struct wiphy *wiphy) | 
|---|
| 16674 | { | 
|---|
| 16675 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 16676 |  | 
|---|
| 16677 | if (WARN_ON(!rdev->cur_cmd_info)) | 
|---|
| 16678 | return 0; | 
|---|
| 16679 |  | 
|---|
| 16680 | return rdev->cur_cmd_info->snd_portid; | 
|---|
| 16681 | } | 
|---|
| 16682 | EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_get_sender); | 
|---|
| 16683 |  | 
|---|
| 16684 | static int nl80211_set_qos_map(struct sk_buff *skb, | 
|---|
| 16685 | struct genl_info *info) | 
|---|
| 16686 | { | 
|---|
| 16687 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 16688 | struct cfg80211_qos_map *qos_map = NULL; | 
|---|
| 16689 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 16690 | u8 *pos, len, num_des, des_len, des; | 
|---|
| 16691 | int ret; | 
|---|
| 16692 |  | 
|---|
| 16693 | if (!rdev->ops->set_qos_map) | 
|---|
| 16694 | return -EOPNOTSUPP; | 
|---|
| 16695 |  | 
|---|
| 16696 | if (info->attrs[NL80211_ATTR_QOS_MAP]) { | 
|---|
| 16697 | pos = nla_data(nla: info->attrs[NL80211_ATTR_QOS_MAP]); | 
|---|
| 16698 | len = nla_len(nla: info->attrs[NL80211_ATTR_QOS_MAP]); | 
|---|
| 16699 |  | 
|---|
| 16700 | if (len % 2) | 
|---|
| 16701 | return -EINVAL; | 
|---|
| 16702 |  | 
|---|
| 16703 | qos_map = kzalloc(sizeof(struct cfg80211_qos_map), GFP_KERNEL); | 
|---|
| 16704 | if (!qos_map) | 
|---|
| 16705 | return -ENOMEM; | 
|---|
| 16706 |  | 
|---|
| 16707 | num_des = (len - IEEE80211_QOS_MAP_LEN_MIN) >> 1; | 
|---|
| 16708 | if (num_des) { | 
|---|
| 16709 | des_len = num_des * | 
|---|
| 16710 | sizeof(struct cfg80211_dscp_exception); | 
|---|
| 16711 | memcpy(to: qos_map->dscp_exception, from: pos, len: des_len); | 
|---|
| 16712 | qos_map->num_des = num_des; | 
|---|
| 16713 | for (des = 0; des < num_des; des++) { | 
|---|
| 16714 | if (qos_map->dscp_exception[des].up > 7) { | 
|---|
| 16715 | kfree(objp: qos_map); | 
|---|
| 16716 | return -EINVAL; | 
|---|
| 16717 | } | 
|---|
| 16718 | } | 
|---|
| 16719 | pos += des_len; | 
|---|
| 16720 | } | 
|---|
| 16721 | memcpy(to: qos_map->up, from: pos, IEEE80211_QOS_MAP_LEN_MIN); | 
|---|
| 16722 | } | 
|---|
| 16723 |  | 
|---|
| 16724 | ret = nl80211_key_allowed(wdev: dev->ieee80211_ptr); | 
|---|
| 16725 | if (!ret) | 
|---|
| 16726 | ret = rdev_set_qos_map(rdev, dev, qos_map); | 
|---|
| 16727 |  | 
|---|
| 16728 | kfree(objp: qos_map); | 
|---|
| 16729 | return ret; | 
|---|
| 16730 | } | 
|---|
| 16731 |  | 
|---|
| 16732 | static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 16733 | { | 
|---|
| 16734 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 16735 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 16736 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 16737 | const u8 *peer; | 
|---|
| 16738 | u8 tsid, up; | 
|---|
| 16739 | u16 admitted_time = 0; | 
|---|
| 16740 |  | 
|---|
| 16741 | if (!(rdev->wiphy.features & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)) | 
|---|
| 16742 | return -EOPNOTSUPP; | 
|---|
| 16743 |  | 
|---|
| 16744 | if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC] || | 
|---|
| 16745 | !info->attrs[NL80211_ATTR_USER_PRIO]) | 
|---|
| 16746 | return -EINVAL; | 
|---|
| 16747 |  | 
|---|
| 16748 | tsid = nla_get_u8(nla: info->attrs[NL80211_ATTR_TSID]); | 
|---|
| 16749 | up = nla_get_u8(nla: info->attrs[NL80211_ATTR_USER_PRIO]); | 
|---|
| 16750 |  | 
|---|
| 16751 | /* WMM uses TIDs 0-7 even for TSPEC */ | 
|---|
| 16752 | if (tsid >= IEEE80211_FIRST_TSPEC_TSID) { | 
|---|
| 16753 | /* TODO: handle 802.11 TSPEC/admission control | 
|---|
| 16754 | * need more attributes for that (e.g. BA session requirement); | 
|---|
| 16755 | * change the WMM admission test above to allow both then | 
|---|
| 16756 | */ | 
|---|
| 16757 | return -EINVAL; | 
|---|
| 16758 | } | 
|---|
| 16759 |  | 
|---|
| 16760 | peer = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 16761 |  | 
|---|
| 16762 | if (info->attrs[NL80211_ATTR_ADMITTED_TIME]) { | 
|---|
| 16763 | admitted_time = | 
|---|
| 16764 | nla_get_u16(nla: info->attrs[NL80211_ATTR_ADMITTED_TIME]); | 
|---|
| 16765 | if (!admitted_time) | 
|---|
| 16766 | return -EINVAL; | 
|---|
| 16767 | } | 
|---|
| 16768 |  | 
|---|
| 16769 | switch (wdev->iftype) { | 
|---|
| 16770 | case NL80211_IFTYPE_STATION: | 
|---|
| 16771 | case NL80211_IFTYPE_P2P_CLIENT: | 
|---|
| 16772 | if (wdev->connected) | 
|---|
| 16773 | break; | 
|---|
| 16774 | return -ENOTCONN; | 
|---|
| 16775 | default: | 
|---|
| 16776 | return -EOPNOTSUPP; | 
|---|
| 16777 | } | 
|---|
| 16778 |  | 
|---|
| 16779 | return rdev_add_tx_ts(rdev, dev, tsid, peer, user_prio: up, admitted_time); | 
|---|
| 16780 | } | 
|---|
| 16781 |  | 
|---|
| 16782 | static int nl80211_del_tx_ts(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 16783 | { | 
|---|
| 16784 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 16785 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 16786 | const u8 *peer; | 
|---|
| 16787 | u8 tsid; | 
|---|
| 16788 |  | 
|---|
| 16789 | if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 16790 | return -EINVAL; | 
|---|
| 16791 |  | 
|---|
| 16792 | tsid = nla_get_u8(nla: info->attrs[NL80211_ATTR_TSID]); | 
|---|
| 16793 | peer = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 16794 |  | 
|---|
| 16795 | return rdev_del_tx_ts(rdev, dev, tsid, peer); | 
|---|
| 16796 | } | 
|---|
| 16797 |  | 
|---|
| 16798 | static int nl80211_tdls_channel_switch(struct sk_buff *skb, | 
|---|
| 16799 | struct genl_info *info) | 
|---|
| 16800 | { | 
|---|
| 16801 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 16802 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 16803 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 16804 | struct cfg80211_chan_def chandef = {}; | 
|---|
| 16805 | const u8 *addr; | 
|---|
| 16806 | u8 oper_class; | 
|---|
| 16807 | int err; | 
|---|
| 16808 |  | 
|---|
| 16809 | if (!rdev->ops->tdls_channel_switch || | 
|---|
| 16810 | !(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH)) | 
|---|
| 16811 | return -EOPNOTSUPP; | 
|---|
| 16812 |  | 
|---|
| 16813 | switch (dev->ieee80211_ptr->iftype) { | 
|---|
| 16814 | case NL80211_IFTYPE_STATION: | 
|---|
| 16815 | case NL80211_IFTYPE_P2P_CLIENT: | 
|---|
| 16816 | break; | 
|---|
| 16817 | default: | 
|---|
| 16818 | return -EOPNOTSUPP; | 
|---|
| 16819 | } | 
|---|
| 16820 |  | 
|---|
| 16821 | if (!info->attrs[NL80211_ATTR_MAC] || | 
|---|
| 16822 | !info->attrs[NL80211_ATTR_OPER_CLASS]) | 
|---|
| 16823 | return -EINVAL; | 
|---|
| 16824 |  | 
|---|
| 16825 | err = nl80211_parse_chandef(rdev, info, chandef: &chandef); | 
|---|
| 16826 | if (err) | 
|---|
| 16827 | return err; | 
|---|
| 16828 |  | 
|---|
| 16829 | /* | 
|---|
| 16830 | * Don't allow wide channels on the 2.4Ghz band, as per IEEE802.11-2012 | 
|---|
| 16831 | * section 10.22.6.2.1. Disallow 5/10Mhz channels as well for now, the | 
|---|
| 16832 | * specification is not defined for them. | 
|---|
| 16833 | */ | 
|---|
| 16834 | if (chandef.chan->band == NL80211_BAND_2GHZ && | 
|---|
| 16835 | chandef.width != NL80211_CHAN_WIDTH_20_NOHT && | 
|---|
| 16836 | chandef.width != NL80211_CHAN_WIDTH_20) | 
|---|
| 16837 | return -EINVAL; | 
|---|
| 16838 |  | 
|---|
| 16839 | /* we will be active on the TDLS link */ | 
|---|
| 16840 | if (!cfg80211_reg_can_beacon_relax(wiphy: &rdev->wiphy, chandef: &chandef, | 
|---|
| 16841 | iftype: wdev->iftype)) | 
|---|
| 16842 | return -EINVAL; | 
|---|
| 16843 |  | 
|---|
| 16844 | /* don't allow switching to DFS channels */ | 
|---|
| 16845 | if (cfg80211_chandef_dfs_required(wiphy: wdev->wiphy, chandef: &chandef, iftype: wdev->iftype)) | 
|---|
| 16846 | return -EINVAL; | 
|---|
| 16847 |  | 
|---|
| 16848 | addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 16849 | oper_class = nla_get_u8(nla: info->attrs[NL80211_ATTR_OPER_CLASS]); | 
|---|
| 16850 |  | 
|---|
| 16851 | return rdev_tdls_channel_switch(rdev, dev, addr, oper_class, chandef: &chandef); | 
|---|
| 16852 | } | 
|---|
| 16853 |  | 
|---|
| 16854 | static int nl80211_tdls_cancel_channel_switch(struct sk_buff *skb, | 
|---|
| 16855 | struct genl_info *info) | 
|---|
| 16856 | { | 
|---|
| 16857 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 16858 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 16859 | const u8 *addr; | 
|---|
| 16860 |  | 
|---|
| 16861 | if (!rdev->ops->tdls_channel_switch || | 
|---|
| 16862 | !rdev->ops->tdls_cancel_channel_switch || | 
|---|
| 16863 | !(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH)) | 
|---|
| 16864 | return -EOPNOTSUPP; | 
|---|
| 16865 |  | 
|---|
| 16866 | switch (dev->ieee80211_ptr->iftype) { | 
|---|
| 16867 | case NL80211_IFTYPE_STATION: | 
|---|
| 16868 | case NL80211_IFTYPE_P2P_CLIENT: | 
|---|
| 16869 | break; | 
|---|
| 16870 | default: | 
|---|
| 16871 | return -EOPNOTSUPP; | 
|---|
| 16872 | } | 
|---|
| 16873 |  | 
|---|
| 16874 | if (!info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 16875 | return -EINVAL; | 
|---|
| 16876 |  | 
|---|
| 16877 | addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 16878 |  | 
|---|
| 16879 | rdev_tdls_cancel_channel_switch(rdev, dev, addr); | 
|---|
| 16880 |  | 
|---|
| 16881 | return 0; | 
|---|
| 16882 | } | 
|---|
| 16883 |  | 
|---|
| 16884 | static int nl80211_set_multicast_to_unicast(struct sk_buff *skb, | 
|---|
| 16885 | struct genl_info *info) | 
|---|
| 16886 | { | 
|---|
| 16887 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 16888 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 16889 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 16890 | const struct nlattr *nla; | 
|---|
| 16891 | bool enabled; | 
|---|
| 16892 |  | 
|---|
| 16893 | if (!rdev->ops->set_multicast_to_unicast) | 
|---|
| 16894 | return -EOPNOTSUPP; | 
|---|
| 16895 |  | 
|---|
| 16896 | if (wdev->iftype != NL80211_IFTYPE_AP && | 
|---|
| 16897 | wdev->iftype != NL80211_IFTYPE_P2P_GO) | 
|---|
| 16898 | return -EOPNOTSUPP; | 
|---|
| 16899 |  | 
|---|
| 16900 | nla = info->attrs[NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED]; | 
|---|
| 16901 | enabled = nla_get_flag(nla); | 
|---|
| 16902 |  | 
|---|
| 16903 | return rdev_set_multicast_to_unicast(rdev, dev, enabled); | 
|---|
| 16904 | } | 
|---|
| 16905 |  | 
|---|
| 16906 | static int nl80211_set_pmk(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 16907 | { | 
|---|
| 16908 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 16909 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 16910 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 16911 | struct cfg80211_pmk_conf pmk_conf = {}; | 
|---|
| 16912 |  | 
|---|
| 16913 | if (wdev->iftype != NL80211_IFTYPE_STATION && | 
|---|
| 16914 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) | 
|---|
| 16915 | return -EOPNOTSUPP; | 
|---|
| 16916 |  | 
|---|
| 16917 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 16918 | ftidx: NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X)) | 
|---|
| 16919 | return -EOPNOTSUPP; | 
|---|
| 16920 |  | 
|---|
| 16921 | if (!info->attrs[NL80211_ATTR_MAC] || !info->attrs[NL80211_ATTR_PMK]) | 
|---|
| 16922 | return -EINVAL; | 
|---|
| 16923 |  | 
|---|
| 16924 | if (!wdev->connected) | 
|---|
| 16925 | return -ENOTCONN; | 
|---|
| 16926 |  | 
|---|
| 16927 | pmk_conf.aa = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 16928 | if (memcmp(pmk_conf.aa, wdev->u.client.connected_addr, ETH_ALEN)) | 
|---|
| 16929 | return -EINVAL; | 
|---|
| 16930 |  | 
|---|
| 16931 | pmk_conf.pmk = nla_data(nla: info->attrs[NL80211_ATTR_PMK]); | 
|---|
| 16932 | pmk_conf.pmk_len = nla_len(nla: info->attrs[NL80211_ATTR_PMK]); | 
|---|
| 16933 | if (pmk_conf.pmk_len != WLAN_PMK_LEN && | 
|---|
| 16934 | pmk_conf.pmk_len != WLAN_PMK_LEN_SUITE_B_192) | 
|---|
| 16935 | return -EINVAL; | 
|---|
| 16936 |  | 
|---|
| 16937 | if (info->attrs[NL80211_ATTR_PMKR0_NAME]) | 
|---|
| 16938 | pmk_conf.pmk_r0_name = | 
|---|
| 16939 | nla_data(nla: info->attrs[NL80211_ATTR_PMKR0_NAME]); | 
|---|
| 16940 |  | 
|---|
| 16941 | return rdev_set_pmk(rdev, dev, pmk_conf: &pmk_conf); | 
|---|
| 16942 | } | 
|---|
| 16943 |  | 
|---|
| 16944 | static int nl80211_del_pmk(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 16945 | { | 
|---|
| 16946 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 16947 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 16948 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 16949 | const u8 *aa; | 
|---|
| 16950 |  | 
|---|
| 16951 | if (wdev->iftype != NL80211_IFTYPE_STATION && | 
|---|
| 16952 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) | 
|---|
| 16953 | return -EOPNOTSUPP; | 
|---|
| 16954 |  | 
|---|
| 16955 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 16956 | ftidx: NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X)) | 
|---|
| 16957 | return -EOPNOTSUPP; | 
|---|
| 16958 |  | 
|---|
| 16959 | if (!info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 16960 | return -EINVAL; | 
|---|
| 16961 |  | 
|---|
| 16962 | aa = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 16963 | return rdev_del_pmk(rdev, dev, aa); | 
|---|
| 16964 | } | 
|---|
| 16965 |  | 
|---|
| 16966 | static int nl80211_external_auth(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 16967 | { | 
|---|
| 16968 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 16969 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 16970 | struct cfg80211_external_auth_params params; | 
|---|
| 16971 |  | 
|---|
| 16972 | if (!rdev->ops->external_auth) | 
|---|
| 16973 | return -EOPNOTSUPP; | 
|---|
| 16974 |  | 
|---|
| 16975 | if (!info->attrs[NL80211_ATTR_SSID] && | 
|---|
| 16976 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | 
|---|
| 16977 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) | 
|---|
| 16978 | return -EINVAL; | 
|---|
| 16979 |  | 
|---|
| 16980 | if (!info->attrs[NL80211_ATTR_BSSID]) | 
|---|
| 16981 | return -EINVAL; | 
|---|
| 16982 |  | 
|---|
| 16983 | if (!info->attrs[NL80211_ATTR_STATUS_CODE]) | 
|---|
| 16984 | return -EINVAL; | 
|---|
| 16985 |  | 
|---|
| 16986 | memset(s: ¶ms, c: 0, n: sizeof(params)); | 
|---|
| 16987 |  | 
|---|
| 16988 | if (info->attrs[NL80211_ATTR_SSID]) { | 
|---|
| 16989 | params.ssid.ssid_len = nla_len(nla: info->attrs[NL80211_ATTR_SSID]); | 
|---|
| 16990 | if (params.ssid.ssid_len == 0) | 
|---|
| 16991 | return -EINVAL; | 
|---|
| 16992 | memcpy(to: params.ssid.ssid, | 
|---|
| 16993 | from: nla_data(nla: info->attrs[NL80211_ATTR_SSID]), | 
|---|
| 16994 | len: params.ssid.ssid_len); | 
|---|
| 16995 | } | 
|---|
| 16996 |  | 
|---|
| 16997 | memcpy(to: params.bssid, from: nla_data(nla: info->attrs[NL80211_ATTR_BSSID]), | 
|---|
| 16998 | ETH_ALEN); | 
|---|
| 16999 |  | 
|---|
| 17000 | params.status = nla_get_u16(nla: info->attrs[NL80211_ATTR_STATUS_CODE]); | 
|---|
| 17001 |  | 
|---|
| 17002 | if (info->attrs[NL80211_ATTR_PMKID]) | 
|---|
| 17003 | params.pmkid = nla_data(nla: info->attrs[NL80211_ATTR_PMKID]); | 
|---|
| 17004 |  | 
|---|
| 17005 | return rdev_external_auth(rdev, dev, params: ¶ms); | 
|---|
| 17006 | } | 
|---|
| 17007 |  | 
|---|
| 17008 | static int nl80211_tx_control_port(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 17009 | { | 
|---|
| 17010 | bool dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK]; | 
|---|
| 17011 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 17012 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 17013 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 17014 | const u8 *buf; | 
|---|
| 17015 | size_t len; | 
|---|
| 17016 | u8 *dest; | 
|---|
| 17017 | u16 proto; | 
|---|
| 17018 | bool noencrypt; | 
|---|
| 17019 | u64 cookie = 0; | 
|---|
| 17020 | int link_id; | 
|---|
| 17021 | int err; | 
|---|
| 17022 |  | 
|---|
| 17023 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 17024 | ftidx: NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211)) | 
|---|
| 17025 | return -EOPNOTSUPP; | 
|---|
| 17026 |  | 
|---|
| 17027 | if (!rdev->ops->tx_control_port) | 
|---|
| 17028 | return -EOPNOTSUPP; | 
|---|
| 17029 |  | 
|---|
| 17030 | if (!info->attrs[NL80211_ATTR_FRAME] || | 
|---|
| 17031 | !info->attrs[NL80211_ATTR_MAC] || | 
|---|
| 17032 | !info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) { | 
|---|
| 17033 | GENL_SET_ERR_MSG(info, "Frame, MAC or ethertype missing"); | 
|---|
| 17034 | return -EINVAL; | 
|---|
| 17035 | } | 
|---|
| 17036 |  | 
|---|
| 17037 | switch (wdev->iftype) { | 
|---|
| 17038 | case NL80211_IFTYPE_AP: | 
|---|
| 17039 | case NL80211_IFTYPE_P2P_GO: | 
|---|
| 17040 | case NL80211_IFTYPE_MESH_POINT: | 
|---|
| 17041 | break; | 
|---|
| 17042 | case NL80211_IFTYPE_ADHOC: | 
|---|
| 17043 | if (wdev->u.ibss.current_bss) | 
|---|
| 17044 | break; | 
|---|
| 17045 | return -ENOTCONN; | 
|---|
| 17046 | case NL80211_IFTYPE_STATION: | 
|---|
| 17047 | case NL80211_IFTYPE_P2P_CLIENT: | 
|---|
| 17048 | if (wdev->connected) | 
|---|
| 17049 | break; | 
|---|
| 17050 | return -ENOTCONN; | 
|---|
| 17051 | default: | 
|---|
| 17052 | return -EOPNOTSUPP; | 
|---|
| 17053 | } | 
|---|
| 17054 |  | 
|---|
| 17055 | buf = nla_data(nla: info->attrs[NL80211_ATTR_FRAME]); | 
|---|
| 17056 | len = nla_len(nla: info->attrs[NL80211_ATTR_FRAME]); | 
|---|
| 17057 | dest = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 17058 | proto = nla_get_u16(nla: info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]); | 
|---|
| 17059 | noencrypt = | 
|---|
| 17060 | nla_get_flag(nla: info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT]); | 
|---|
| 17061 |  | 
|---|
| 17062 | link_id = nl80211_link_id_or_invalid(attrs: info->attrs); | 
|---|
| 17063 |  | 
|---|
| 17064 | err = rdev_tx_control_port(rdev, dev, buf, len, | 
|---|
| 17065 | dest, cpu_to_be16(proto), noencrypt, link: link_id, | 
|---|
| 17066 | cookie: dont_wait_for_ack ? NULL : &cookie); | 
|---|
| 17067 | if (!err && !dont_wait_for_ack) | 
|---|
| 17068 | nl_set_extack_cookie_u64(extack: info->extack, cookie); | 
|---|
| 17069 | return err; | 
|---|
| 17070 | } | 
|---|
| 17071 |  | 
|---|
| 17072 | static int nl80211_get_ftm_responder_stats(struct sk_buff *skb, | 
|---|
| 17073 | struct genl_info *info) | 
|---|
| 17074 | { | 
|---|
| 17075 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 17076 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 17077 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 17078 | struct cfg80211_ftm_responder_stats ftm_stats = {}; | 
|---|
| 17079 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); | 
|---|
| 17080 | struct sk_buff *msg; | 
|---|
| 17081 | void *hdr; | 
|---|
| 17082 | struct nlattr *ftm_stats_attr; | 
|---|
| 17083 | int err; | 
|---|
| 17084 |  | 
|---|
| 17085 | if (wdev->iftype != NL80211_IFTYPE_AP || | 
|---|
| 17086 | !wdev->links[link_id].ap.beacon_interval) | 
|---|
| 17087 | return -EOPNOTSUPP; | 
|---|
| 17088 |  | 
|---|
| 17089 | err = rdev_get_ftm_responder_stats(rdev, dev, ftm_stats: &ftm_stats); | 
|---|
| 17090 | if (err) | 
|---|
| 17091 | return err; | 
|---|
| 17092 |  | 
|---|
| 17093 | if (!ftm_stats.filled) | 
|---|
| 17094 | return -ENODATA; | 
|---|
| 17095 |  | 
|---|
| 17096 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 17097 | if (!msg) | 
|---|
| 17098 | return -ENOMEM; | 
|---|
| 17099 |  | 
|---|
| 17100 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, | 
|---|
| 17101 | cmd: NL80211_CMD_GET_FTM_RESPONDER_STATS); | 
|---|
| 17102 | if (!hdr) | 
|---|
| 17103 | goto nla_put_failure; | 
|---|
| 17104 |  | 
|---|
| 17105 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex)) | 
|---|
| 17106 | goto nla_put_failure; | 
|---|
| 17107 |  | 
|---|
| 17108 | ftm_stats_attr = nla_nest_start_noflag(skb: msg, | 
|---|
| 17109 | attrtype: NL80211_ATTR_FTM_RESPONDER_STATS); | 
|---|
| 17110 | if (!ftm_stats_attr) | 
|---|
| 17111 | goto nla_put_failure; | 
|---|
| 17112 |  | 
|---|
| 17113 | #define SET_FTM(field, name, type)					 \ | 
|---|
| 17114 | do { if ((ftm_stats.filled & BIT(NL80211_FTM_STATS_ ## name)) && \ | 
|---|
| 17115 | nla_put_ ## type(msg, NL80211_FTM_STATS_ ## name,		 \ | 
|---|
| 17116 | ftm_stats.field))				 \ | 
|---|
| 17117 | goto nla_put_failure; } while (0) | 
|---|
| 17118 | #define SET_FTM_U64(field, name)					 \ | 
|---|
| 17119 | do { if ((ftm_stats.filled & BIT(NL80211_FTM_STATS_ ## name)) && \ | 
|---|
| 17120 | nla_put_u64_64bit(msg, NL80211_FTM_STATS_ ## name,		 \ | 
|---|
| 17121 | ftm_stats.field, NL80211_FTM_STATS_PAD))	 \ | 
|---|
| 17122 | goto nla_put_failure; } while (0) | 
|---|
| 17123 |  | 
|---|
| 17124 | SET_FTM(success_num, SUCCESS_NUM, u32); | 
|---|
| 17125 | SET_FTM(partial_num, PARTIAL_NUM, u32); | 
|---|
| 17126 | SET_FTM(failed_num, FAILED_NUM, u32); | 
|---|
| 17127 | SET_FTM(asap_num, ASAP_NUM, u32); | 
|---|
| 17128 | SET_FTM(non_asap_num, NON_ASAP_NUM, u32); | 
|---|
| 17129 | SET_FTM_U64(total_duration_ms, TOTAL_DURATION_MSEC); | 
|---|
| 17130 | SET_FTM(unknown_triggers_num, UNKNOWN_TRIGGERS_NUM, u32); | 
|---|
| 17131 | SET_FTM(reschedule_requests_num, RESCHEDULE_REQUESTS_NUM, u32); | 
|---|
| 17132 | SET_FTM(out_of_window_triggers_num, OUT_OF_WINDOW_TRIGGERS_NUM, u32); | 
|---|
| 17133 | #undef SET_FTM | 
|---|
| 17134 |  | 
|---|
| 17135 | nla_nest_end(skb: msg, start: ftm_stats_attr); | 
|---|
| 17136 |  | 
|---|
| 17137 | genlmsg_end(skb: msg, hdr); | 
|---|
| 17138 | return genlmsg_reply(skb: msg, info); | 
|---|
| 17139 |  | 
|---|
| 17140 | nla_put_failure: | 
|---|
| 17141 | nlmsg_free(skb: msg); | 
|---|
| 17142 | return -ENOBUFS; | 
|---|
| 17143 | } | 
|---|
| 17144 |  | 
|---|
| 17145 | static int nl80211_update_owe_info(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 17146 | { | 
|---|
| 17147 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 17148 | struct cfg80211_update_owe_info owe_info; | 
|---|
| 17149 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 17150 |  | 
|---|
| 17151 | if (!rdev->ops->update_owe_info) | 
|---|
| 17152 | return -EOPNOTSUPP; | 
|---|
| 17153 |  | 
|---|
| 17154 | if (!info->attrs[NL80211_ATTR_STATUS_CODE] || | 
|---|
| 17155 | !info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 17156 | return -EINVAL; | 
|---|
| 17157 |  | 
|---|
| 17158 | memset(s: &owe_info, c: 0, n: sizeof(owe_info)); | 
|---|
| 17159 | owe_info.status = nla_get_u16(nla: info->attrs[NL80211_ATTR_STATUS_CODE]); | 
|---|
| 17160 | nla_memcpy(dest: owe_info.peer, src: info->attrs[NL80211_ATTR_MAC], ETH_ALEN); | 
|---|
| 17161 |  | 
|---|
| 17162 | if (info->attrs[NL80211_ATTR_IE]) { | 
|---|
| 17163 | owe_info.ie = nla_data(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 17164 | owe_info.ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); | 
|---|
| 17165 | } | 
|---|
| 17166 |  | 
|---|
| 17167 | return rdev_update_owe_info(rdev, dev, oweinfo: &owe_info); | 
|---|
| 17168 | } | 
|---|
| 17169 |  | 
|---|
| 17170 | static int nl80211_probe_mesh_link(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 17171 | { | 
|---|
| 17172 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 17173 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 17174 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 17175 | struct station_info sinfo = {}; | 
|---|
| 17176 | const u8 *buf; | 
|---|
| 17177 | size_t len; | 
|---|
| 17178 | u8 *dest; | 
|---|
| 17179 | int err; | 
|---|
| 17180 |  | 
|---|
| 17181 | if (!rdev->ops->probe_mesh_link || !rdev->ops->get_station) | 
|---|
| 17182 | return -EOPNOTSUPP; | 
|---|
| 17183 |  | 
|---|
| 17184 | if (!info->attrs[NL80211_ATTR_MAC] || | 
|---|
| 17185 | !info->attrs[NL80211_ATTR_FRAME]) { | 
|---|
| 17186 | GENL_SET_ERR_MSG(info, "Frame or MAC missing"); | 
|---|
| 17187 | return -EINVAL; | 
|---|
| 17188 | } | 
|---|
| 17189 |  | 
|---|
| 17190 | if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) | 
|---|
| 17191 | return -EOPNOTSUPP; | 
|---|
| 17192 |  | 
|---|
| 17193 | dest = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 17194 | buf = nla_data(nla: info->attrs[NL80211_ATTR_FRAME]); | 
|---|
| 17195 | len = nla_len(nla: info->attrs[NL80211_ATTR_FRAME]); | 
|---|
| 17196 |  | 
|---|
| 17197 | if (len < sizeof(struct ethhdr)) | 
|---|
| 17198 | return -EINVAL; | 
|---|
| 17199 |  | 
|---|
| 17200 | if (!ether_addr_equal(addr1: buf, addr2: dest) || is_multicast_ether_addr(addr: buf) || | 
|---|
| 17201 | !ether_addr_equal(addr1: buf + ETH_ALEN, addr2: dev->dev_addr)) | 
|---|
| 17202 | return -EINVAL; | 
|---|
| 17203 |  | 
|---|
| 17204 | err = rdev_get_station(rdev, dev, mac: dest, sinfo: &sinfo); | 
|---|
| 17205 | if (err) | 
|---|
| 17206 | return err; | 
|---|
| 17207 |  | 
|---|
| 17208 | cfg80211_sinfo_release_content(sinfo: &sinfo); | 
|---|
| 17209 |  | 
|---|
| 17210 | return rdev_probe_mesh_link(rdev, dev, dest, buf, len); | 
|---|
| 17211 | } | 
|---|
| 17212 |  | 
|---|
| 17213 | static int parse_tid_conf(struct cfg80211_registered_device *rdev, | 
|---|
| 17214 | struct nlattr *attrs[], struct net_device *dev, | 
|---|
| 17215 | struct cfg80211_tid_cfg *tid_conf, | 
|---|
| 17216 | struct genl_info *info, const u8 *peer, | 
|---|
| 17217 | unsigned int link_id) | 
|---|
| 17218 | { | 
|---|
| 17219 | struct netlink_ext_ack *extack = info->extack; | 
|---|
| 17220 | u64 mask; | 
|---|
| 17221 | int err; | 
|---|
| 17222 |  | 
|---|
| 17223 | if (!attrs[NL80211_TID_CONFIG_ATTR_TIDS]) | 
|---|
| 17224 | return -EINVAL; | 
|---|
| 17225 |  | 
|---|
| 17226 | tid_conf->config_override = | 
|---|
| 17227 | nla_get_flag(nla: attrs[NL80211_TID_CONFIG_ATTR_OVERRIDE]); | 
|---|
| 17228 | tid_conf->tids = nla_get_u16(nla: attrs[NL80211_TID_CONFIG_ATTR_TIDS]); | 
|---|
| 17229 |  | 
|---|
| 17230 | if (tid_conf->config_override) { | 
|---|
| 17231 | if (rdev->ops->reset_tid_config) { | 
|---|
| 17232 | err = rdev_reset_tid_config(rdev, dev, peer, | 
|---|
| 17233 | tids: tid_conf->tids); | 
|---|
| 17234 | if (err) | 
|---|
| 17235 | return err; | 
|---|
| 17236 | } else { | 
|---|
| 17237 | return -EINVAL; | 
|---|
| 17238 | } | 
|---|
| 17239 | } | 
|---|
| 17240 |  | 
|---|
| 17241 | if (attrs[NL80211_TID_CONFIG_ATTR_NOACK]) { | 
|---|
| 17242 | tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_NOACK); | 
|---|
| 17243 | tid_conf->noack = | 
|---|
| 17244 | nla_get_u8(nla: attrs[NL80211_TID_CONFIG_ATTR_NOACK]); | 
|---|
| 17245 | } | 
|---|
| 17246 |  | 
|---|
| 17247 | if (attrs[NL80211_TID_CONFIG_ATTR_RETRY_SHORT]) { | 
|---|
| 17248 | tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_RETRY_SHORT); | 
|---|
| 17249 | tid_conf->retry_short = | 
|---|
| 17250 | nla_get_u8(nla: attrs[NL80211_TID_CONFIG_ATTR_RETRY_SHORT]); | 
|---|
| 17251 |  | 
|---|
| 17252 | if (tid_conf->retry_short > rdev->wiphy.max_data_retry_count) | 
|---|
| 17253 | return -EINVAL; | 
|---|
| 17254 | } | 
|---|
| 17255 |  | 
|---|
| 17256 | if (attrs[NL80211_TID_CONFIG_ATTR_RETRY_LONG]) { | 
|---|
| 17257 | tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_RETRY_LONG); | 
|---|
| 17258 | tid_conf->retry_long = | 
|---|
| 17259 | nla_get_u8(nla: attrs[NL80211_TID_CONFIG_ATTR_RETRY_LONG]); | 
|---|
| 17260 |  | 
|---|
| 17261 | if (tid_conf->retry_long > rdev->wiphy.max_data_retry_count) | 
|---|
| 17262 | return -EINVAL; | 
|---|
| 17263 | } | 
|---|
| 17264 |  | 
|---|
| 17265 | if (attrs[NL80211_TID_CONFIG_ATTR_AMPDU_CTRL]) { | 
|---|
| 17266 | tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL); | 
|---|
| 17267 | tid_conf->ampdu = | 
|---|
| 17268 | nla_get_u8(nla: attrs[NL80211_TID_CONFIG_ATTR_AMPDU_CTRL]); | 
|---|
| 17269 | } | 
|---|
| 17270 |  | 
|---|
| 17271 | if (attrs[NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL]) { | 
|---|
| 17272 | tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL); | 
|---|
| 17273 | tid_conf->rtscts = | 
|---|
| 17274 | nla_get_u8(nla: attrs[NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL]); | 
|---|
| 17275 | } | 
|---|
| 17276 |  | 
|---|
| 17277 | if (attrs[NL80211_TID_CONFIG_ATTR_AMSDU_CTRL]) { | 
|---|
| 17278 | tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_AMSDU_CTRL); | 
|---|
| 17279 | tid_conf->amsdu = | 
|---|
| 17280 | nla_get_u8(nla: attrs[NL80211_TID_CONFIG_ATTR_AMSDU_CTRL]); | 
|---|
| 17281 | } | 
|---|
| 17282 |  | 
|---|
| 17283 | if (attrs[NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE]) { | 
|---|
| 17284 | u32 idx = NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE, attr; | 
|---|
| 17285 |  | 
|---|
| 17286 | tid_conf->txrate_type = nla_get_u8(nla: attrs[idx]); | 
|---|
| 17287 |  | 
|---|
| 17288 | if (tid_conf->txrate_type != NL80211_TX_RATE_AUTOMATIC) { | 
|---|
| 17289 | attr = NL80211_TID_CONFIG_ATTR_TX_RATE; | 
|---|
| 17290 | err = nl80211_parse_tx_bitrate_mask(info, attrs, attr, | 
|---|
| 17291 | mask: &tid_conf->txrate_mask, dev, | 
|---|
| 17292 | default_all_enabled: true, link_id); | 
|---|
| 17293 | if (err) | 
|---|
| 17294 | return err; | 
|---|
| 17295 |  | 
|---|
| 17296 | tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_TX_RATE); | 
|---|
| 17297 | } | 
|---|
| 17298 | tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE); | 
|---|
| 17299 | } | 
|---|
| 17300 |  | 
|---|
| 17301 | if (peer) | 
|---|
| 17302 | mask = rdev->wiphy.tid_config_support.peer; | 
|---|
| 17303 | else | 
|---|
| 17304 | mask = rdev->wiphy.tid_config_support.vif; | 
|---|
| 17305 |  | 
|---|
| 17306 | if (tid_conf->mask & ~mask) { | 
|---|
| 17307 | NL_SET_ERR_MSG(extack, "unsupported TID configuration"); | 
|---|
| 17308 | return -EOPNOTSUPP; | 
|---|
| 17309 | } | 
|---|
| 17310 |  | 
|---|
| 17311 | return 0; | 
|---|
| 17312 | } | 
|---|
| 17313 |  | 
|---|
| 17314 | static int nl80211_set_tid_config(struct sk_buff *skb, | 
|---|
| 17315 | struct genl_info *info) | 
|---|
| 17316 | { | 
|---|
| 17317 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 17318 | struct nlattr *attrs[NL80211_TID_CONFIG_ATTR_MAX + 1]; | 
|---|
| 17319 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); | 
|---|
| 17320 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 17321 | struct cfg80211_tid_config *tid_config; | 
|---|
| 17322 | struct nlattr *tid; | 
|---|
| 17323 | int conf_idx = 0, rem_conf; | 
|---|
| 17324 | int ret = -EINVAL; | 
|---|
| 17325 | u32 num_conf = 0; | 
|---|
| 17326 |  | 
|---|
| 17327 | if (!info->attrs[NL80211_ATTR_TID_CONFIG]) | 
|---|
| 17328 | return -EINVAL; | 
|---|
| 17329 |  | 
|---|
| 17330 | if (!rdev->ops->set_tid_config) | 
|---|
| 17331 | return -EOPNOTSUPP; | 
|---|
| 17332 |  | 
|---|
| 17333 | nla_for_each_nested(tid, info->attrs[NL80211_ATTR_TID_CONFIG], | 
|---|
| 17334 | rem_conf) | 
|---|
| 17335 | num_conf++; | 
|---|
| 17336 |  | 
|---|
| 17337 | tid_config = kzalloc(struct_size(tid_config, tid_conf, num_conf), | 
|---|
| 17338 | GFP_KERNEL); | 
|---|
| 17339 | if (!tid_config) | 
|---|
| 17340 | return -ENOMEM; | 
|---|
| 17341 |  | 
|---|
| 17342 | tid_config->n_tid_conf = num_conf; | 
|---|
| 17343 |  | 
|---|
| 17344 | if (info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 17345 | tid_config->peer = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 17346 |  | 
|---|
| 17347 | nla_for_each_nested(tid, info->attrs[NL80211_ATTR_TID_CONFIG], | 
|---|
| 17348 | rem_conf) { | 
|---|
| 17349 | ret = nla_parse_nested(tb: attrs, maxtype: NL80211_TID_CONFIG_ATTR_MAX, | 
|---|
| 17350 | nla: tid, NULL, NULL); | 
|---|
| 17351 |  | 
|---|
| 17352 | if (ret) | 
|---|
| 17353 | goto bad_tid_conf; | 
|---|
| 17354 |  | 
|---|
| 17355 | ret = parse_tid_conf(rdev, attrs, dev, | 
|---|
| 17356 | tid_conf: &tid_config->tid_conf[conf_idx], | 
|---|
| 17357 | info, peer: tid_config->peer, link_id); | 
|---|
| 17358 | if (ret) | 
|---|
| 17359 | goto bad_tid_conf; | 
|---|
| 17360 |  | 
|---|
| 17361 | conf_idx++; | 
|---|
| 17362 | } | 
|---|
| 17363 |  | 
|---|
| 17364 | ret = rdev_set_tid_config(rdev, dev, tid_conf: tid_config); | 
|---|
| 17365 |  | 
|---|
| 17366 | bad_tid_conf: | 
|---|
| 17367 | kfree(objp: tid_config); | 
|---|
| 17368 | return ret; | 
|---|
| 17369 | } | 
|---|
| 17370 |  | 
|---|
| 17371 | static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 17372 | { | 
|---|
| 17373 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 17374 | struct cfg80211_color_change_settings params = {}; | 
|---|
| 17375 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 17376 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 17377 | struct nlattr **tb; | 
|---|
| 17378 | u16 offset; | 
|---|
| 17379 | int err; | 
|---|
| 17380 |  | 
|---|
| 17381 | if (!rdev->ops->color_change) | 
|---|
| 17382 | return -EOPNOTSUPP; | 
|---|
| 17383 |  | 
|---|
| 17384 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, | 
|---|
| 17385 | ftidx: NL80211_EXT_FEATURE_BSS_COLOR)) | 
|---|
| 17386 | return -EOPNOTSUPP; | 
|---|
| 17387 |  | 
|---|
| 17388 | if (wdev->iftype != NL80211_IFTYPE_AP) | 
|---|
| 17389 | return -EOPNOTSUPP; | 
|---|
| 17390 |  | 
|---|
| 17391 | if (!info->attrs[NL80211_ATTR_COLOR_CHANGE_COUNT] || | 
|---|
| 17392 | !info->attrs[NL80211_ATTR_COLOR_CHANGE_COLOR] || | 
|---|
| 17393 | !info->attrs[NL80211_ATTR_COLOR_CHANGE_ELEMS]) | 
|---|
| 17394 | return -EINVAL; | 
|---|
| 17395 |  | 
|---|
| 17396 | params.count = nla_get_u8(nla: info->attrs[NL80211_ATTR_COLOR_CHANGE_COUNT]); | 
|---|
| 17397 | params.color = nla_get_u8(nla: info->attrs[NL80211_ATTR_COLOR_CHANGE_COLOR]); | 
|---|
| 17398 |  | 
|---|
| 17399 | err = nl80211_parse_beacon(rdev, attrs: info->attrs, bcn: ¶ms.beacon_next, | 
|---|
| 17400 | extack: info->extack); | 
|---|
| 17401 | if (err) | 
|---|
| 17402 | return err; | 
|---|
| 17403 |  | 
|---|
| 17404 | tb = kcalloc(NL80211_ATTR_MAX + 1, sizeof(*tb), GFP_KERNEL); | 
|---|
| 17405 | if (!tb) | 
|---|
| 17406 | return -ENOMEM; | 
|---|
| 17407 |  | 
|---|
| 17408 | err = nla_parse_nested(tb, maxtype: NL80211_ATTR_MAX, | 
|---|
| 17409 | nla: info->attrs[NL80211_ATTR_COLOR_CHANGE_ELEMS], | 
|---|
| 17410 | policy: nl80211_policy, extack: info->extack); | 
|---|
| 17411 | if (err) | 
|---|
| 17412 | goto out; | 
|---|
| 17413 |  | 
|---|
| 17414 | err = nl80211_parse_beacon(rdev, attrs: tb, bcn: ¶ms.beacon_color_change, | 
|---|
| 17415 | extack: info->extack); | 
|---|
| 17416 | if (err) | 
|---|
| 17417 | goto out; | 
|---|
| 17418 |  | 
|---|
| 17419 | if (!tb[NL80211_ATTR_CNTDWN_OFFS_BEACON]) { | 
|---|
| 17420 | err = -EINVAL; | 
|---|
| 17421 | goto out; | 
|---|
| 17422 | } | 
|---|
| 17423 |  | 
|---|
| 17424 | if (nla_len(nla: tb[NL80211_ATTR_CNTDWN_OFFS_BEACON]) != sizeof(u16)) { | 
|---|
| 17425 | err = -EINVAL; | 
|---|
| 17426 | goto out; | 
|---|
| 17427 | } | 
|---|
| 17428 |  | 
|---|
| 17429 | offset = nla_get_u16(nla: tb[NL80211_ATTR_CNTDWN_OFFS_BEACON]); | 
|---|
| 17430 | if (offset >= params.beacon_color_change.tail_len) { | 
|---|
| 17431 | err = -EINVAL; | 
|---|
| 17432 | goto out; | 
|---|
| 17433 | } | 
|---|
| 17434 |  | 
|---|
| 17435 | if (params.beacon_color_change.tail[offset] != params.count) { | 
|---|
| 17436 | err = -EINVAL; | 
|---|
| 17437 | goto out; | 
|---|
| 17438 | } | 
|---|
| 17439 |  | 
|---|
| 17440 | params.counter_offset_beacon = offset; | 
|---|
| 17441 |  | 
|---|
| 17442 | if (tb[NL80211_ATTR_CNTDWN_OFFS_PRESP]) { | 
|---|
| 17443 | if (nla_len(nla: tb[NL80211_ATTR_CNTDWN_OFFS_PRESP]) != | 
|---|
| 17444 | sizeof(u16)) { | 
|---|
| 17445 | err = -EINVAL; | 
|---|
| 17446 | goto out; | 
|---|
| 17447 | } | 
|---|
| 17448 |  | 
|---|
| 17449 | offset = nla_get_u16(nla: tb[NL80211_ATTR_CNTDWN_OFFS_PRESP]); | 
|---|
| 17450 | if (offset >= params.beacon_color_change.probe_resp_len) { | 
|---|
| 17451 | err = -EINVAL; | 
|---|
| 17452 | goto out; | 
|---|
| 17453 | } | 
|---|
| 17454 |  | 
|---|
| 17455 | if (params.beacon_color_change.probe_resp[offset] != | 
|---|
| 17456 | params.count) { | 
|---|
| 17457 | err = -EINVAL; | 
|---|
| 17458 | goto out; | 
|---|
| 17459 | } | 
|---|
| 17460 |  | 
|---|
| 17461 | params.counter_offset_presp = offset; | 
|---|
| 17462 | } | 
|---|
| 17463 |  | 
|---|
| 17464 | if (info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP]) { | 
|---|
| 17465 | err = nl80211_parse_unsol_bcast_probe_resp( | 
|---|
| 17466 | rdev, attrs: info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP], | 
|---|
| 17467 | presp: ¶ms.unsol_bcast_probe_resp); | 
|---|
| 17468 | if (err) | 
|---|
| 17469 | goto out; | 
|---|
| 17470 | } | 
|---|
| 17471 |  | 
|---|
| 17472 | params.link_id = nl80211_link_id(attrs: info->attrs); | 
|---|
| 17473 | err = rdev_color_change(rdev, dev, params: ¶ms); | 
|---|
| 17474 |  | 
|---|
| 17475 | out: | 
|---|
| 17476 | kfree(objp: params.beacon_next.mbssid_ies); | 
|---|
| 17477 | kfree(objp: params.beacon_color_change.mbssid_ies); | 
|---|
| 17478 | kfree(objp: params.beacon_next.rnr_ies); | 
|---|
| 17479 | kfree(objp: params.beacon_color_change.rnr_ies); | 
|---|
| 17480 | kfree(objp: tb); | 
|---|
| 17481 | return err; | 
|---|
| 17482 | } | 
|---|
| 17483 |  | 
|---|
| 17484 | static int nl80211_set_fils_aad(struct sk_buff *skb, | 
|---|
| 17485 | struct genl_info *info) | 
|---|
| 17486 | { | 
|---|
| 17487 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 17488 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 17489 | struct cfg80211_fils_aad fils_aad = {}; | 
|---|
| 17490 | u8 *nonces; | 
|---|
| 17491 |  | 
|---|
| 17492 | if (!info->attrs[NL80211_ATTR_MAC] || | 
|---|
| 17493 | !info->attrs[NL80211_ATTR_FILS_KEK] || | 
|---|
| 17494 | !info->attrs[NL80211_ATTR_FILS_NONCES]) | 
|---|
| 17495 | return -EINVAL; | 
|---|
| 17496 |  | 
|---|
| 17497 | fils_aad.macaddr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 17498 | fils_aad.kek_len = nla_len(nla: info->attrs[NL80211_ATTR_FILS_KEK]); | 
|---|
| 17499 | fils_aad.kek = nla_data(nla: info->attrs[NL80211_ATTR_FILS_KEK]); | 
|---|
| 17500 | nonces = nla_data(nla: info->attrs[NL80211_ATTR_FILS_NONCES]); | 
|---|
| 17501 | fils_aad.snonce = nonces; | 
|---|
| 17502 | fils_aad.anonce = nonces + FILS_NONCE_LEN; | 
|---|
| 17503 |  | 
|---|
| 17504 | return rdev_set_fils_aad(rdev, dev, fils_aad: &fils_aad); | 
|---|
| 17505 | } | 
|---|
| 17506 |  | 
|---|
| 17507 | static int nl80211_add_link(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 17508 | { | 
|---|
| 17509 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 17510 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); | 
|---|
| 17511 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 17512 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 17513 | int ret; | 
|---|
| 17514 |  | 
|---|
| 17515 | if (!(wdev->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO)) | 
|---|
| 17516 | return -EINVAL; | 
|---|
| 17517 |  | 
|---|
| 17518 | switch (wdev->iftype) { | 
|---|
| 17519 | case NL80211_IFTYPE_AP: | 
|---|
| 17520 | break; | 
|---|
| 17521 | default: | 
|---|
| 17522 | return -EINVAL; | 
|---|
| 17523 | } | 
|---|
| 17524 |  | 
|---|
| 17525 | if (!info->attrs[NL80211_ATTR_MAC] || | 
|---|
| 17526 | !is_valid_ether_addr(addr: nla_data(nla: info->attrs[NL80211_ATTR_MAC]))) | 
|---|
| 17527 | return -EINVAL; | 
|---|
| 17528 |  | 
|---|
| 17529 | wdev->valid_links |= BIT(link_id); | 
|---|
| 17530 | ether_addr_copy(dst: wdev->links[link_id].addr, | 
|---|
| 17531 | src: nla_data(nla: info->attrs[NL80211_ATTR_MAC])); | 
|---|
| 17532 |  | 
|---|
| 17533 | ret = rdev_add_intf_link(rdev, wdev, link_id); | 
|---|
| 17534 | if (ret) { | 
|---|
| 17535 | wdev->valid_links &= ~BIT(link_id); | 
|---|
| 17536 | eth_zero_addr(addr: wdev->links[link_id].addr); | 
|---|
| 17537 | } | 
|---|
| 17538 |  | 
|---|
| 17539 | return ret; | 
|---|
| 17540 | } | 
|---|
| 17541 |  | 
|---|
| 17542 | static int nl80211_remove_link(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 17543 | { | 
|---|
| 17544 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); | 
|---|
| 17545 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 17546 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 17547 |  | 
|---|
| 17548 | /* cannot remove if there's no link */ | 
|---|
| 17549 | if (!info->attrs[NL80211_ATTR_MLO_LINK_ID]) | 
|---|
| 17550 | return -EINVAL; | 
|---|
| 17551 |  | 
|---|
| 17552 | switch (wdev->iftype) { | 
|---|
| 17553 | case NL80211_IFTYPE_AP: | 
|---|
| 17554 | break; | 
|---|
| 17555 | default: | 
|---|
| 17556 | return -EINVAL; | 
|---|
| 17557 | } | 
|---|
| 17558 |  | 
|---|
| 17559 | cfg80211_remove_link(wdev, link_id); | 
|---|
| 17560 |  | 
|---|
| 17561 | return 0; | 
|---|
| 17562 | } | 
|---|
| 17563 |  | 
|---|
| 17564 | static int | 
|---|
| 17565 | nl80211_add_mod_link_station(struct sk_buff *skb, struct genl_info *info, | 
|---|
| 17566 | bool add) | 
|---|
| 17567 | { | 
|---|
| 17568 | struct link_station_parameters params = {}; | 
|---|
| 17569 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 17570 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 17571 | int err; | 
|---|
| 17572 |  | 
|---|
| 17573 | if ((add && !rdev->ops->add_link_station) || | 
|---|
| 17574 | (!add && !rdev->ops->mod_link_station)) | 
|---|
| 17575 | return -EOPNOTSUPP; | 
|---|
| 17576 |  | 
|---|
| 17577 | if (add && !info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 17578 | return -EINVAL; | 
|---|
| 17579 |  | 
|---|
| 17580 | if (!info->attrs[NL80211_ATTR_MLD_ADDR]) | 
|---|
| 17581 | return -EINVAL; | 
|---|
| 17582 |  | 
|---|
| 17583 | if (add && !info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) | 
|---|
| 17584 | return -EINVAL; | 
|---|
| 17585 |  | 
|---|
| 17586 | params.mld_mac = nla_data(nla: info->attrs[NL80211_ATTR_MLD_ADDR]); | 
|---|
| 17587 |  | 
|---|
| 17588 | if (info->attrs[NL80211_ATTR_MAC]) { | 
|---|
| 17589 | params.link_mac = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 17590 | if (!is_valid_ether_addr(addr: params.link_mac)) | 
|---|
| 17591 | return -EINVAL; | 
|---|
| 17592 | } | 
|---|
| 17593 |  | 
|---|
| 17594 | if (!info->attrs[NL80211_ATTR_MLO_LINK_ID]) | 
|---|
| 17595 | return -EINVAL; | 
|---|
| 17596 |  | 
|---|
| 17597 | params.link_id = nla_get_u8(nla: info->attrs[NL80211_ATTR_MLO_LINK_ID]); | 
|---|
| 17598 |  | 
|---|
| 17599 | if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) { | 
|---|
| 17600 | params.supported_rates = | 
|---|
| 17601 | nla_data(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | 
|---|
| 17602 | params.supported_rates_len = | 
|---|
| 17603 | nla_len(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | 
|---|
| 17604 | } | 
|---|
| 17605 |  | 
|---|
| 17606 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) | 
|---|
| 17607 | params.ht_capa = | 
|---|
| 17608 | nla_data(nla: info->attrs[NL80211_ATTR_HT_CAPABILITY]); | 
|---|
| 17609 |  | 
|---|
| 17610 | if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) | 
|---|
| 17611 | params.vht_capa = | 
|---|
| 17612 | nla_data(nla: info->attrs[NL80211_ATTR_VHT_CAPABILITY]); | 
|---|
| 17613 |  | 
|---|
| 17614 | if (info->attrs[NL80211_ATTR_HE_CAPABILITY]) { | 
|---|
| 17615 | params.he_capa = | 
|---|
| 17616 | nla_data(nla: info->attrs[NL80211_ATTR_HE_CAPABILITY]); | 
|---|
| 17617 | params.he_capa_len = | 
|---|
| 17618 | nla_len(nla: info->attrs[NL80211_ATTR_HE_CAPABILITY]); | 
|---|
| 17619 |  | 
|---|
| 17620 | if (info->attrs[NL80211_ATTR_EHT_CAPABILITY]) { | 
|---|
| 17621 | params.eht_capa = | 
|---|
| 17622 | nla_data(nla: info->attrs[NL80211_ATTR_EHT_CAPABILITY]); | 
|---|
| 17623 | params.eht_capa_len = | 
|---|
| 17624 | nla_len(nla: info->attrs[NL80211_ATTR_EHT_CAPABILITY]); | 
|---|
| 17625 |  | 
|---|
| 17626 | if (!ieee80211_eht_capa_size_ok(he_capa: (const u8 *)params.he_capa, | 
|---|
| 17627 | data: (const u8 *)params.eht_capa, | 
|---|
| 17628 | len: params.eht_capa_len, | 
|---|
| 17629 | from_ap: false)) | 
|---|
| 17630 | return -EINVAL; | 
|---|
| 17631 | } | 
|---|
| 17632 | } | 
|---|
| 17633 |  | 
|---|
| 17634 | if (info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]) | 
|---|
| 17635 | params.he_6ghz_capa = | 
|---|
| 17636 | nla_data(nla: info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]); | 
|---|
| 17637 |  | 
|---|
| 17638 | if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) { | 
|---|
| 17639 | params.opmode_notif_used = true; | 
|---|
| 17640 | params.opmode_notif = | 
|---|
| 17641 | nla_get_u8(nla: info->attrs[NL80211_ATTR_OPMODE_NOTIF]); | 
|---|
| 17642 | } | 
|---|
| 17643 |  | 
|---|
| 17644 | err = nl80211_parse_sta_txpower_setting(info, txpwr: ¶ms.txpwr, | 
|---|
| 17645 | txpwr_set: ¶ms.txpwr_set); | 
|---|
| 17646 | if (err) | 
|---|
| 17647 | return err; | 
|---|
| 17648 |  | 
|---|
| 17649 | if (add) | 
|---|
| 17650 | return rdev_add_link_station(rdev, dev, params: ¶ms); | 
|---|
| 17651 |  | 
|---|
| 17652 | return rdev_mod_link_station(rdev, dev, params: ¶ms); | 
|---|
| 17653 | } | 
|---|
| 17654 |  | 
|---|
| 17655 | static int | 
|---|
| 17656 | nl80211_add_link_station(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 17657 | { | 
|---|
| 17658 | return nl80211_add_mod_link_station(skb, info, add: true); | 
|---|
| 17659 | } | 
|---|
| 17660 |  | 
|---|
| 17661 | static int | 
|---|
| 17662 | nl80211_modify_link_station(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 17663 | { | 
|---|
| 17664 | return nl80211_add_mod_link_station(skb, info, add: false); | 
|---|
| 17665 | } | 
|---|
| 17666 |  | 
|---|
| 17667 | static int | 
|---|
| 17668 | nl80211_remove_link_station(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 17669 | { | 
|---|
| 17670 | struct link_station_del_parameters params = {}; | 
|---|
| 17671 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 17672 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 17673 |  | 
|---|
| 17674 | if (!rdev->ops->del_link_station) | 
|---|
| 17675 | return -EOPNOTSUPP; | 
|---|
| 17676 |  | 
|---|
| 17677 | if (!info->attrs[NL80211_ATTR_MLD_ADDR] || | 
|---|
| 17678 | !info->attrs[NL80211_ATTR_MLO_LINK_ID]) | 
|---|
| 17679 | return -EINVAL; | 
|---|
| 17680 |  | 
|---|
| 17681 | params.mld_mac = nla_data(nla: info->attrs[NL80211_ATTR_MLD_ADDR]); | 
|---|
| 17682 | params.link_id = nla_get_u8(nla: info->attrs[NL80211_ATTR_MLO_LINK_ID]); | 
|---|
| 17683 |  | 
|---|
| 17684 | return rdev_del_link_station(rdev, dev, params: ¶ms); | 
|---|
| 17685 | } | 
|---|
| 17686 |  | 
|---|
| 17687 | static int nl80211_set_hw_timestamp(struct sk_buff *skb, | 
|---|
| 17688 | struct genl_info *info) | 
|---|
| 17689 | { | 
|---|
| 17690 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 17691 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 17692 | struct cfg80211_set_hw_timestamp hwts = {}; | 
|---|
| 17693 |  | 
|---|
| 17694 | if (!rdev->wiphy.hw_timestamp_max_peers) | 
|---|
| 17695 | return -EOPNOTSUPP; | 
|---|
| 17696 |  | 
|---|
| 17697 | if (!info->attrs[NL80211_ATTR_MAC] && | 
|---|
| 17698 | rdev->wiphy.hw_timestamp_max_peers != CFG80211_HW_TIMESTAMP_ALL_PEERS) | 
|---|
| 17699 | return -EOPNOTSUPP; | 
|---|
| 17700 |  | 
|---|
| 17701 | if (info->attrs[NL80211_ATTR_MAC]) | 
|---|
| 17702 | hwts.macaddr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); | 
|---|
| 17703 |  | 
|---|
| 17704 | hwts.enable = | 
|---|
| 17705 | nla_get_flag(nla: info->attrs[NL80211_ATTR_HW_TIMESTAMP_ENABLED]); | 
|---|
| 17706 |  | 
|---|
| 17707 | return rdev_set_hw_timestamp(rdev, dev, hwts: &hwts); | 
|---|
| 17708 | } | 
|---|
| 17709 |  | 
|---|
| 17710 | static int | 
|---|
| 17711 | nl80211_set_ttlm(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 17712 | { | 
|---|
| 17713 | struct cfg80211_ttlm_params params = {}; | 
|---|
| 17714 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 17715 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 17716 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 17717 |  | 
|---|
| 17718 | if (wdev->iftype != NL80211_IFTYPE_STATION && | 
|---|
| 17719 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) | 
|---|
| 17720 | return -EOPNOTSUPP; | 
|---|
| 17721 |  | 
|---|
| 17722 | if (!wdev->connected) | 
|---|
| 17723 | return -ENOLINK; | 
|---|
| 17724 |  | 
|---|
| 17725 | if (!info->attrs[NL80211_ATTR_MLO_TTLM_DLINK] || | 
|---|
| 17726 | !info->attrs[NL80211_ATTR_MLO_TTLM_ULINK]) | 
|---|
| 17727 | return -EINVAL; | 
|---|
| 17728 |  | 
|---|
| 17729 | nla_memcpy(dest: params.dlink, | 
|---|
| 17730 | src: info->attrs[NL80211_ATTR_MLO_TTLM_DLINK], | 
|---|
| 17731 | count: sizeof(params.dlink)); | 
|---|
| 17732 | nla_memcpy(dest: params.ulink, | 
|---|
| 17733 | src: info->attrs[NL80211_ATTR_MLO_TTLM_ULINK], | 
|---|
| 17734 | count: sizeof(params.ulink)); | 
|---|
| 17735 |  | 
|---|
| 17736 | return rdev_set_ttlm(rdev, dev, params: ¶ms); | 
|---|
| 17737 | } | 
|---|
| 17738 |  | 
|---|
| 17739 | static int nl80211_assoc_ml_reconf(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 17740 | { | 
|---|
| 17741 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 17742 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 17743 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 17744 | struct cfg80211_ml_reconf_req req = {}; | 
|---|
| 17745 | unsigned int link_id; | 
|---|
| 17746 | u16 add_links; | 
|---|
| 17747 | int err; | 
|---|
| 17748 |  | 
|---|
| 17749 | if (!wdev->valid_links) | 
|---|
| 17750 | return -EINVAL; | 
|---|
| 17751 |  | 
|---|
| 17752 | if (dev->ieee80211_ptr->conn_owner_nlportid && | 
|---|
| 17753 | dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid) | 
|---|
| 17754 | return -EPERM; | 
|---|
| 17755 |  | 
|---|
| 17756 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && | 
|---|
| 17757 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) | 
|---|
| 17758 | return -EOPNOTSUPP; | 
|---|
| 17759 |  | 
|---|
| 17760 | add_links = 0; | 
|---|
| 17761 | if (info->attrs[NL80211_ATTR_MLO_LINKS]) { | 
|---|
| 17762 | err = nl80211_process_links(rdev, links: req.add_links, | 
|---|
| 17763 | /* mark as MLO, but not assoc */ | 
|---|
| 17764 | IEEE80211_MLD_MAX_NUM_LINKS, | 
|---|
| 17765 | NULL, ssid_len: 0, info); | 
|---|
| 17766 | if (err) | 
|---|
| 17767 | return err; | 
|---|
| 17768 |  | 
|---|
| 17769 | for (link_id = 0; link_id < IEEE80211_MLD_MAX_NUM_LINKS; | 
|---|
| 17770 | link_id++) { | 
|---|
| 17771 | if (!req.add_links[link_id].bss) | 
|---|
| 17772 | continue; | 
|---|
| 17773 | add_links |= BIT(link_id); | 
|---|
| 17774 | } | 
|---|
| 17775 | } | 
|---|
| 17776 |  | 
|---|
| 17777 | if (info->attrs[NL80211_ATTR_MLO_RECONF_REM_LINKS]) | 
|---|
| 17778 | req.rem_links = | 
|---|
| 17779 | nla_get_u16(nla: info->attrs[NL80211_ATTR_MLO_RECONF_REM_LINKS]); | 
|---|
| 17780 |  | 
|---|
| 17781 | /* Validate that existing links are not added, removed links are valid | 
|---|
| 17782 | * and don't allow adding and removing the same links | 
|---|
| 17783 | */ | 
|---|
| 17784 | if ((add_links & req.rem_links) || !(add_links | req.rem_links) || | 
|---|
| 17785 | (wdev->valid_links & add_links) || | 
|---|
| 17786 | ((wdev->valid_links & req.rem_links) != req.rem_links)) { | 
|---|
| 17787 | err = -EINVAL; | 
|---|
| 17788 | goto out; | 
|---|
| 17789 | } | 
|---|
| 17790 |  | 
|---|
| 17791 | if (info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]) | 
|---|
| 17792 | req.ext_mld_capa_ops = | 
|---|
| 17793 | nla_get_u16(nla: info->attrs[NL80211_ATTR_ASSOC_MLD_EXT_CAPA_OPS]); | 
|---|
| 17794 |  | 
|---|
| 17795 | err = cfg80211_assoc_ml_reconf(rdev, dev, req: &req); | 
|---|
| 17796 |  | 
|---|
| 17797 | out: | 
|---|
| 17798 | for (link_id = 0; link_id < ARRAY_SIZE(req.add_links); link_id++) | 
|---|
| 17799 | cfg80211_put_bss(wiphy: &rdev->wiphy, bss: req.add_links[link_id].bss); | 
|---|
| 17800 |  | 
|---|
| 17801 | return err; | 
|---|
| 17802 | } | 
|---|
| 17803 |  | 
|---|
| 17804 | static int | 
|---|
| 17805 | nl80211_epcs_cfg(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 17806 | { | 
|---|
| 17807 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 17808 | struct net_device *dev = info->user_ptr[1]; | 
|---|
| 17809 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 17810 | bool val; | 
|---|
| 17811 |  | 
|---|
| 17812 | if (wdev->iftype != NL80211_IFTYPE_STATION && | 
|---|
| 17813 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) | 
|---|
| 17814 | return -EOPNOTSUPP; | 
|---|
| 17815 |  | 
|---|
| 17816 | if (!wdev->connected) | 
|---|
| 17817 | return -ENOLINK; | 
|---|
| 17818 |  | 
|---|
| 17819 | val = nla_get_flag(nla: info->attrs[NL80211_ATTR_EPCS]); | 
|---|
| 17820 |  | 
|---|
| 17821 | return rdev_set_epcs(rdev, dev, val); | 
|---|
| 17822 | } | 
|---|
| 17823 |  | 
|---|
| 17824 | #define NL80211_FLAG_NEED_WIPHY		0x01 | 
|---|
| 17825 | #define NL80211_FLAG_NEED_NETDEV	0x02 | 
|---|
| 17826 | #define NL80211_FLAG_NEED_RTNL		0x04 | 
|---|
| 17827 | #define NL80211_FLAG_CHECK_NETDEV_UP	0x08 | 
|---|
| 17828 | #define NL80211_FLAG_NEED_NETDEV_UP	(NL80211_FLAG_NEED_NETDEV |\ | 
|---|
| 17829 | NL80211_FLAG_CHECK_NETDEV_UP) | 
|---|
| 17830 | #define NL80211_FLAG_NEED_WDEV		0x10 | 
|---|
| 17831 | /* If a netdev is associated, it must be UP, P2P must be started */ | 
|---|
| 17832 | #define NL80211_FLAG_NEED_WDEV_UP	(NL80211_FLAG_NEED_WDEV |\ | 
|---|
| 17833 | NL80211_FLAG_CHECK_NETDEV_UP) | 
|---|
| 17834 | #define NL80211_FLAG_CLEAR_SKB		0x20 | 
|---|
| 17835 | #define NL80211_FLAG_NO_WIPHY_MTX	0x40 | 
|---|
| 17836 | #define NL80211_FLAG_MLO_VALID_LINK_ID	0x80 | 
|---|
| 17837 | #define NL80211_FLAG_MLO_UNSUPPORTED	0x100 | 
|---|
| 17838 |  | 
|---|
| 17839 | #define INTERNAL_FLAG_SELECTORS(__sel)			\ | 
|---|
| 17840 | SELECTOR(__sel, NONE, 0) /* must be first */	\ | 
|---|
| 17841 | SELECTOR(__sel, WIPHY,				\ | 
|---|
| 17842 | NL80211_FLAG_NEED_WIPHY)		\ | 
|---|
| 17843 | SELECTOR(__sel, WDEV,				\ | 
|---|
| 17844 | NL80211_FLAG_NEED_WDEV)		\ | 
|---|
| 17845 | SELECTOR(__sel, NETDEV,				\ | 
|---|
| 17846 | NL80211_FLAG_NEED_NETDEV)		\ | 
|---|
| 17847 | SELECTOR(__sel, NETDEV_LINK,			\ | 
|---|
| 17848 | NL80211_FLAG_NEED_NETDEV |		\ | 
|---|
| 17849 | NL80211_FLAG_MLO_VALID_LINK_ID)	\ | 
|---|
| 17850 | SELECTOR(__sel, NETDEV_NO_MLO,			\ | 
|---|
| 17851 | NL80211_FLAG_NEED_NETDEV |		\ | 
|---|
| 17852 | NL80211_FLAG_MLO_UNSUPPORTED)	\ | 
|---|
| 17853 | SELECTOR(__sel, WIPHY_RTNL,			\ | 
|---|
| 17854 | NL80211_FLAG_NEED_WIPHY |		\ | 
|---|
| 17855 | NL80211_FLAG_NEED_RTNL)		\ | 
|---|
| 17856 | SELECTOR(__sel, WIPHY_RTNL_NOMTX,		\ | 
|---|
| 17857 | NL80211_FLAG_NEED_WIPHY |		\ | 
|---|
| 17858 | NL80211_FLAG_NEED_RTNL |		\ | 
|---|
| 17859 | NL80211_FLAG_NO_WIPHY_MTX)		\ | 
|---|
| 17860 | SELECTOR(__sel, WDEV_RTNL,			\ | 
|---|
| 17861 | NL80211_FLAG_NEED_WDEV |		\ | 
|---|
| 17862 | NL80211_FLAG_NEED_RTNL)		\ | 
|---|
| 17863 | SELECTOR(__sel, NETDEV_RTNL,			\ | 
|---|
| 17864 | NL80211_FLAG_NEED_NETDEV |		\ | 
|---|
| 17865 | NL80211_FLAG_NEED_RTNL)		\ | 
|---|
| 17866 | SELECTOR(__sel, NETDEV_UP,			\ | 
|---|
| 17867 | NL80211_FLAG_NEED_NETDEV_UP)		\ | 
|---|
| 17868 | SELECTOR(__sel, NETDEV_UP_LINK,			\ | 
|---|
| 17869 | NL80211_FLAG_NEED_NETDEV_UP |		\ | 
|---|
| 17870 | NL80211_FLAG_MLO_VALID_LINK_ID)	\ | 
|---|
| 17871 | SELECTOR(__sel, NETDEV_UP_NO_MLO,		\ | 
|---|
| 17872 | NL80211_FLAG_NEED_NETDEV_UP |		\ | 
|---|
| 17873 | NL80211_FLAG_MLO_UNSUPPORTED)		\ | 
|---|
| 17874 | SELECTOR(__sel, NETDEV_UP_NO_MLO_CLEAR,		\ | 
|---|
| 17875 | NL80211_FLAG_NEED_NETDEV_UP |		\ | 
|---|
| 17876 | NL80211_FLAG_CLEAR_SKB |		\ | 
|---|
| 17877 | NL80211_FLAG_MLO_UNSUPPORTED)		\ | 
|---|
| 17878 | SELECTOR(__sel, NETDEV_UP_NOTMX,		\ | 
|---|
| 17879 | NL80211_FLAG_NEED_NETDEV_UP |		\ | 
|---|
| 17880 | NL80211_FLAG_NO_WIPHY_MTX)		\ | 
|---|
| 17881 | SELECTOR(__sel, NETDEV_UP_NOTMX_MLO,		\ | 
|---|
| 17882 | NL80211_FLAG_NEED_NETDEV_UP |		\ | 
|---|
| 17883 | NL80211_FLAG_NO_WIPHY_MTX |		\ | 
|---|
| 17884 | NL80211_FLAG_MLO_VALID_LINK_ID)	\ | 
|---|
| 17885 | SELECTOR(__sel, NETDEV_UP_CLEAR,		\ | 
|---|
| 17886 | NL80211_FLAG_NEED_NETDEV_UP |		\ | 
|---|
| 17887 | NL80211_FLAG_CLEAR_SKB)		\ | 
|---|
| 17888 | SELECTOR(__sel, WDEV_UP,			\ | 
|---|
| 17889 | NL80211_FLAG_NEED_WDEV_UP)		\ | 
|---|
| 17890 | SELECTOR(__sel, WDEV_UP_LINK,			\ | 
|---|
| 17891 | NL80211_FLAG_NEED_WDEV_UP |		\ | 
|---|
| 17892 | NL80211_FLAG_MLO_VALID_LINK_ID)	\ | 
|---|
| 17893 | SELECTOR(__sel, WDEV_UP_RTNL,			\ | 
|---|
| 17894 | NL80211_FLAG_NEED_WDEV_UP |		\ | 
|---|
| 17895 | NL80211_FLAG_NEED_RTNL)		\ | 
|---|
| 17896 | SELECTOR(__sel, WIPHY_CLEAR,			\ | 
|---|
| 17897 | NL80211_FLAG_NEED_WIPHY |		\ | 
|---|
| 17898 | NL80211_FLAG_CLEAR_SKB) | 
|---|
| 17899 |  | 
|---|
| 17900 | enum nl80211_internal_flags_selector { | 
|---|
| 17901 | #define SELECTOR(_, name, value)	NL80211_IFL_SEL_##name, | 
|---|
| 17902 | INTERNAL_FLAG_SELECTORS(_) | 
|---|
| 17903 | #undef SELECTOR | 
|---|
| 17904 | }; | 
|---|
| 17905 |  | 
|---|
| 17906 | static u32 nl80211_internal_flags[] = { | 
|---|
| 17907 | #define SELECTOR(_, name, value)	[NL80211_IFL_SEL_##name] = value, | 
|---|
| 17908 | INTERNAL_FLAG_SELECTORS(_) | 
|---|
| 17909 | #undef SELECTOR | 
|---|
| 17910 | }; | 
|---|
| 17911 |  | 
|---|
| 17912 | static int nl80211_pre_doit(const struct genl_split_ops *ops, | 
|---|
| 17913 | struct sk_buff *skb, | 
|---|
| 17914 | struct genl_info *info) | 
|---|
| 17915 | { | 
|---|
| 17916 | struct cfg80211_registered_device *rdev = NULL; | 
|---|
| 17917 | struct wireless_dev *wdev = NULL; | 
|---|
| 17918 | struct net_device *dev = NULL; | 
|---|
| 17919 | u32 internal_flags; | 
|---|
| 17920 | int err; | 
|---|
| 17921 |  | 
|---|
| 17922 | if (WARN_ON(ops->internal_flags >= ARRAY_SIZE(nl80211_internal_flags))) | 
|---|
| 17923 | return -EINVAL; | 
|---|
| 17924 |  | 
|---|
| 17925 | internal_flags = nl80211_internal_flags[ops->internal_flags]; | 
|---|
| 17926 |  | 
|---|
| 17927 | rtnl_lock(); | 
|---|
| 17928 | if (internal_flags & NL80211_FLAG_NEED_WIPHY) { | 
|---|
| 17929 | rdev = cfg80211_get_dev_from_info(netns: genl_info_net(info), info); | 
|---|
| 17930 | if (IS_ERR(ptr: rdev)) { | 
|---|
| 17931 | err = PTR_ERR(ptr: rdev); | 
|---|
| 17932 | goto out_unlock; | 
|---|
| 17933 | } | 
|---|
| 17934 | info->user_ptr[0] = rdev; | 
|---|
| 17935 | } else if (internal_flags & NL80211_FLAG_NEED_NETDEV || | 
|---|
| 17936 | internal_flags & NL80211_FLAG_NEED_WDEV) { | 
|---|
| 17937 | wdev = __cfg80211_wdev_from_attrs(NULL, netns: genl_info_net(info), | 
|---|
| 17938 | attrs: info->attrs); | 
|---|
| 17939 | if (IS_ERR(ptr: wdev)) { | 
|---|
| 17940 | err = PTR_ERR(ptr: wdev); | 
|---|
| 17941 | goto out_unlock; | 
|---|
| 17942 | } | 
|---|
| 17943 |  | 
|---|
| 17944 | dev = wdev->netdev; | 
|---|
| 17945 | dev_hold(dev); | 
|---|
| 17946 | rdev = wiphy_to_rdev(wiphy: wdev->wiphy); | 
|---|
| 17947 |  | 
|---|
| 17948 | if (internal_flags & NL80211_FLAG_NEED_NETDEV) { | 
|---|
| 17949 | if (!dev) { | 
|---|
| 17950 | err = -EINVAL; | 
|---|
| 17951 | goto out_unlock; | 
|---|
| 17952 | } | 
|---|
| 17953 |  | 
|---|
| 17954 | info->user_ptr[1] = dev; | 
|---|
| 17955 | } else { | 
|---|
| 17956 | info->user_ptr[1] = wdev; | 
|---|
| 17957 | } | 
|---|
| 17958 |  | 
|---|
| 17959 | if (internal_flags & NL80211_FLAG_CHECK_NETDEV_UP && | 
|---|
| 17960 | !wdev_running(wdev)) { | 
|---|
| 17961 | err = -ENETDOWN; | 
|---|
| 17962 | goto out_unlock; | 
|---|
| 17963 | } | 
|---|
| 17964 |  | 
|---|
| 17965 | info->user_ptr[0] = rdev; | 
|---|
| 17966 | } | 
|---|
| 17967 |  | 
|---|
| 17968 | if (internal_flags & NL80211_FLAG_MLO_VALID_LINK_ID) { | 
|---|
| 17969 | struct nlattr *link_id = info->attrs[NL80211_ATTR_MLO_LINK_ID]; | 
|---|
| 17970 |  | 
|---|
| 17971 | if (!wdev) { | 
|---|
| 17972 | err = -EINVAL; | 
|---|
| 17973 | goto out_unlock; | 
|---|
| 17974 | } | 
|---|
| 17975 |  | 
|---|
| 17976 | /* MLO -> require valid link ID */ | 
|---|
| 17977 | if (wdev->valid_links && | 
|---|
| 17978 | (!link_id || | 
|---|
| 17979 | !(wdev->valid_links & BIT(nla_get_u8(link_id))))) { | 
|---|
| 17980 | err = -EINVAL; | 
|---|
| 17981 | goto out_unlock; | 
|---|
| 17982 | } | 
|---|
| 17983 |  | 
|---|
| 17984 | /* non-MLO -> no link ID attribute accepted */ | 
|---|
| 17985 | if (!wdev->valid_links && link_id) { | 
|---|
| 17986 | err = -EINVAL; | 
|---|
| 17987 | goto out_unlock; | 
|---|
| 17988 | } | 
|---|
| 17989 | } | 
|---|
| 17990 |  | 
|---|
| 17991 | if (internal_flags & NL80211_FLAG_MLO_UNSUPPORTED) { | 
|---|
| 17992 | if (info->attrs[NL80211_ATTR_MLO_LINK_ID] || | 
|---|
| 17993 | (wdev && wdev->valid_links)) { | 
|---|
| 17994 | err = -EINVAL; | 
|---|
| 17995 | goto out_unlock; | 
|---|
| 17996 | } | 
|---|
| 17997 | } | 
|---|
| 17998 |  | 
|---|
| 17999 | if (rdev && !(internal_flags & NL80211_FLAG_NO_WIPHY_MTX)) { | 
|---|
| 18000 | wiphy_lock(wiphy: &rdev->wiphy); | 
|---|
| 18001 | /* we keep the mutex locked until post_doit */ | 
|---|
| 18002 | __release(&rdev->wiphy.mtx); | 
|---|
| 18003 | } | 
|---|
| 18004 | if (!(internal_flags & NL80211_FLAG_NEED_RTNL)) | 
|---|
| 18005 | rtnl_unlock(); | 
|---|
| 18006 |  | 
|---|
| 18007 | return 0; | 
|---|
| 18008 | out_unlock: | 
|---|
| 18009 | rtnl_unlock(); | 
|---|
| 18010 | dev_put(dev); | 
|---|
| 18011 | return err; | 
|---|
| 18012 | } | 
|---|
| 18013 |  | 
|---|
| 18014 | static void nl80211_post_doit(const struct genl_split_ops *ops, | 
|---|
| 18015 | struct sk_buff *skb, | 
|---|
| 18016 | struct genl_info *info) | 
|---|
| 18017 | { | 
|---|
| 18018 | u32 internal_flags = nl80211_internal_flags[ops->internal_flags]; | 
|---|
| 18019 |  | 
|---|
| 18020 | if (info->user_ptr[1]) { | 
|---|
| 18021 | if (internal_flags & NL80211_FLAG_NEED_WDEV) { | 
|---|
| 18022 | struct wireless_dev *wdev = info->user_ptr[1]; | 
|---|
| 18023 |  | 
|---|
| 18024 | dev_put(dev: wdev->netdev); | 
|---|
| 18025 | } else { | 
|---|
| 18026 | dev_put(dev: info->user_ptr[1]); | 
|---|
| 18027 | } | 
|---|
| 18028 | } | 
|---|
| 18029 |  | 
|---|
| 18030 | if (info->user_ptr[0] && | 
|---|
| 18031 | !(internal_flags & NL80211_FLAG_NO_WIPHY_MTX)) { | 
|---|
| 18032 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 18033 |  | 
|---|
| 18034 | /* we kept the mutex locked since pre_doit */ | 
|---|
| 18035 | __acquire(&rdev->wiphy.mtx); | 
|---|
| 18036 | wiphy_unlock(wiphy: &rdev->wiphy); | 
|---|
| 18037 | } | 
|---|
| 18038 |  | 
|---|
| 18039 | if (internal_flags & NL80211_FLAG_NEED_RTNL) | 
|---|
| 18040 | rtnl_unlock(); | 
|---|
| 18041 |  | 
|---|
| 18042 | /* If needed, clear the netlink message payload from the SKB | 
|---|
| 18043 | * as it might contain key data that shouldn't stick around on | 
|---|
| 18044 | * the heap after the SKB is freed. The netlink message header | 
|---|
| 18045 | * is still needed for further processing, so leave it intact. | 
|---|
| 18046 | */ | 
|---|
| 18047 | if (internal_flags & NL80211_FLAG_CLEAR_SKB) { | 
|---|
| 18048 | struct nlmsghdr *nlh = nlmsg_hdr(skb); | 
|---|
| 18049 |  | 
|---|
| 18050 | memset(s: nlmsg_data(nlh), c: 0, n: nlmsg_len(nlh)); | 
|---|
| 18051 | } | 
|---|
| 18052 | } | 
|---|
| 18053 |  | 
|---|
| 18054 | static int nl80211_set_sar_sub_specs(struct cfg80211_registered_device *rdev, | 
|---|
| 18055 | struct cfg80211_sar_specs *sar_specs, | 
|---|
| 18056 | struct nlattr *spec[], int index) | 
|---|
| 18057 | { | 
|---|
| 18058 | u32 range_index, i; | 
|---|
| 18059 |  | 
|---|
| 18060 | if (!sar_specs || !spec) | 
|---|
| 18061 | return -EINVAL; | 
|---|
| 18062 |  | 
|---|
| 18063 | if (!spec[NL80211_SAR_ATTR_SPECS_POWER] || | 
|---|
| 18064 | !spec[NL80211_SAR_ATTR_SPECS_RANGE_INDEX]) | 
|---|
| 18065 | return -EINVAL; | 
|---|
| 18066 |  | 
|---|
| 18067 | range_index = nla_get_u32(nla: spec[NL80211_SAR_ATTR_SPECS_RANGE_INDEX]); | 
|---|
| 18068 |  | 
|---|
| 18069 | /* check if range_index exceeds num_freq_ranges */ | 
|---|
| 18070 | if (range_index >= rdev->wiphy.sar_capa->num_freq_ranges) | 
|---|
| 18071 | return -EINVAL; | 
|---|
| 18072 |  | 
|---|
| 18073 | /* check if range_index duplicates */ | 
|---|
| 18074 | for (i = 0; i < index; i++) { | 
|---|
| 18075 | if (sar_specs->sub_specs[i].freq_range_index == range_index) | 
|---|
| 18076 | return -EINVAL; | 
|---|
| 18077 | } | 
|---|
| 18078 |  | 
|---|
| 18079 | sar_specs->sub_specs[index].power = | 
|---|
| 18080 | nla_get_s32(nla: spec[NL80211_SAR_ATTR_SPECS_POWER]); | 
|---|
| 18081 |  | 
|---|
| 18082 | sar_specs->sub_specs[index].freq_range_index = range_index; | 
|---|
| 18083 |  | 
|---|
| 18084 | return 0; | 
|---|
| 18085 | } | 
|---|
| 18086 |  | 
|---|
| 18087 | static int nl80211_set_sar_specs(struct sk_buff *skb, struct genl_info *info) | 
|---|
| 18088 | { | 
|---|
| 18089 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 
|---|
| 18090 | struct nlattr *spec[NL80211_SAR_ATTR_SPECS_MAX + 1]; | 
|---|
| 18091 | struct nlattr *tb[NL80211_SAR_ATTR_MAX + 1]; | 
|---|
| 18092 | struct cfg80211_sar_specs *sar_spec; | 
|---|
| 18093 | enum nl80211_sar_type type; | 
|---|
| 18094 | struct nlattr *spec_list; | 
|---|
| 18095 | u32 specs; | 
|---|
| 18096 | int rem, err; | 
|---|
| 18097 |  | 
|---|
| 18098 | if (!rdev->wiphy.sar_capa || !rdev->ops->set_sar_specs) | 
|---|
| 18099 | return -EOPNOTSUPP; | 
|---|
| 18100 |  | 
|---|
| 18101 | if (!info->attrs[NL80211_ATTR_SAR_SPEC]) | 
|---|
| 18102 | return -EINVAL; | 
|---|
| 18103 |  | 
|---|
| 18104 | nla_parse_nested(tb, maxtype: NL80211_SAR_ATTR_MAX, | 
|---|
| 18105 | nla: info->attrs[NL80211_ATTR_SAR_SPEC], | 
|---|
| 18106 | NULL, NULL); | 
|---|
| 18107 |  | 
|---|
| 18108 | if (!tb[NL80211_SAR_ATTR_TYPE] || !tb[NL80211_SAR_ATTR_SPECS]) | 
|---|
| 18109 | return -EINVAL; | 
|---|
| 18110 |  | 
|---|
| 18111 | type = nla_get_u32(nla: tb[NL80211_SAR_ATTR_TYPE]); | 
|---|
| 18112 | if (type != rdev->wiphy.sar_capa->type) | 
|---|
| 18113 | return -EINVAL; | 
|---|
| 18114 |  | 
|---|
| 18115 | specs = 0; | 
|---|
| 18116 | nla_for_each_nested(spec_list, tb[NL80211_SAR_ATTR_SPECS], rem) | 
|---|
| 18117 | specs++; | 
|---|
| 18118 |  | 
|---|
| 18119 | if (specs > rdev->wiphy.sar_capa->num_freq_ranges) | 
|---|
| 18120 | return -EINVAL; | 
|---|
| 18121 |  | 
|---|
| 18122 | sar_spec = kzalloc(struct_size(sar_spec, sub_specs, specs), GFP_KERNEL); | 
|---|
| 18123 | if (!sar_spec) | 
|---|
| 18124 | return -ENOMEM; | 
|---|
| 18125 |  | 
|---|
| 18126 | sar_spec->num_sub_specs = specs; | 
|---|
| 18127 | sar_spec->type = type; | 
|---|
| 18128 | specs = 0; | 
|---|
| 18129 | nla_for_each_nested(spec_list, tb[NL80211_SAR_ATTR_SPECS], rem) { | 
|---|
| 18130 | nla_parse_nested(tb: spec, maxtype: NL80211_SAR_ATTR_SPECS_MAX, | 
|---|
| 18131 | nla: spec_list, NULL, NULL); | 
|---|
| 18132 |  | 
|---|
| 18133 | switch (type) { | 
|---|
| 18134 | case NL80211_SAR_TYPE_POWER: | 
|---|
| 18135 | if (nl80211_set_sar_sub_specs(rdev, sar_specs: sar_spec, | 
|---|
| 18136 | spec, index: specs)) { | 
|---|
| 18137 | err = -EINVAL; | 
|---|
| 18138 | goto error; | 
|---|
| 18139 | } | 
|---|
| 18140 | break; | 
|---|
| 18141 | default: | 
|---|
| 18142 | err = -EINVAL; | 
|---|
| 18143 | goto error; | 
|---|
| 18144 | } | 
|---|
| 18145 | specs++; | 
|---|
| 18146 | } | 
|---|
| 18147 |  | 
|---|
| 18148 | sar_spec->num_sub_specs = specs; | 
|---|
| 18149 |  | 
|---|
| 18150 | rdev->cur_cmd_info = info; | 
|---|
| 18151 | err = rdev_set_sar_specs(rdev, sar: sar_spec); | 
|---|
| 18152 | rdev->cur_cmd_info = NULL; | 
|---|
| 18153 | error: | 
|---|
| 18154 | kfree(objp: sar_spec); | 
|---|
| 18155 | return err; | 
|---|
| 18156 | } | 
|---|
| 18157 |  | 
|---|
| 18158 | #define SELECTOR(__sel, name, value) \ | 
|---|
| 18159 | ((__sel) == (value)) ? NL80211_IFL_SEL_##name : | 
|---|
| 18160 | int __missing_selector(void); | 
|---|
| 18161 | #define IFLAGS(__val) INTERNAL_FLAG_SELECTORS(__val) __missing_selector() | 
|---|
| 18162 |  | 
|---|
| 18163 | static const struct genl_ops nl80211_ops[] = { | 
|---|
| 18164 | { | 
|---|
| 18165 | .cmd = NL80211_CMD_GET_WIPHY, | 
|---|
| 18166 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18167 | .doit = nl80211_get_wiphy, | 
|---|
| 18168 | .dumpit = nl80211_dump_wiphy, | 
|---|
| 18169 | .done = nl80211_dump_wiphy_done, | 
|---|
| 18170 | /* can be retrieved by unprivileged users */ | 
|---|
| 18171 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY), | 
|---|
| 18172 | }, | 
|---|
| 18173 | }; | 
|---|
| 18174 |  | 
|---|
| 18175 | static const struct genl_small_ops nl80211_small_ops[] = { | 
|---|
| 18176 | { | 
|---|
| 18177 | .cmd = NL80211_CMD_SET_WIPHY, | 
|---|
| 18178 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18179 | .doit = nl80211_set_wiphy, | 
|---|
| 18180 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18181 | }, | 
|---|
| 18182 | { | 
|---|
| 18183 | .cmd = NL80211_CMD_GET_INTERFACE, | 
|---|
| 18184 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18185 | .doit = nl80211_get_interface, | 
|---|
| 18186 | .dumpit = nl80211_dump_interface, | 
|---|
| 18187 | /* can be retrieved by unprivileged users */ | 
|---|
| 18188 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV), | 
|---|
| 18189 | }, | 
|---|
| 18190 | { | 
|---|
| 18191 | .cmd = NL80211_CMD_SET_INTERFACE, | 
|---|
| 18192 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18193 | .doit = nl80211_set_interface, | 
|---|
| 18194 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18195 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV | | 
|---|
| 18196 | NL80211_FLAG_NEED_RTNL), | 
|---|
| 18197 | }, | 
|---|
| 18198 | { | 
|---|
| 18199 | .cmd = NL80211_CMD_NEW_INTERFACE, | 
|---|
| 18200 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18201 | .doit = nl80211_new_interface, | 
|---|
| 18202 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18203 | .internal_flags = | 
|---|
| 18204 | IFLAGS(NL80211_FLAG_NEED_WIPHY | | 
|---|
| 18205 | NL80211_FLAG_NEED_RTNL | | 
|---|
| 18206 | /* we take the wiphy mutex later ourselves */ | 
|---|
| 18207 | NL80211_FLAG_NO_WIPHY_MTX), | 
|---|
| 18208 | }, | 
|---|
| 18209 | { | 
|---|
| 18210 | .cmd = NL80211_CMD_DEL_INTERFACE, | 
|---|
| 18211 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18212 | .doit = nl80211_del_interface, | 
|---|
| 18213 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18214 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV | | 
|---|
| 18215 | NL80211_FLAG_NEED_RTNL), | 
|---|
| 18216 | }, | 
|---|
| 18217 | { | 
|---|
| 18218 | .cmd = NL80211_CMD_GET_KEY, | 
|---|
| 18219 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18220 | .doit = nl80211_get_key, | 
|---|
| 18221 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18222 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18223 | }, | 
|---|
| 18224 | { | 
|---|
| 18225 | .cmd = NL80211_CMD_SET_KEY, | 
|---|
| 18226 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18227 | .doit = nl80211_set_key, | 
|---|
| 18228 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18229 | /* cannot use NL80211_FLAG_MLO_VALID_LINK_ID, depends on key */ | 
|---|
| 18230 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18231 | NL80211_FLAG_CLEAR_SKB), | 
|---|
| 18232 | }, | 
|---|
| 18233 | { | 
|---|
| 18234 | .cmd = NL80211_CMD_NEW_KEY, | 
|---|
| 18235 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18236 | .doit = nl80211_new_key, | 
|---|
| 18237 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18238 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18239 | NL80211_FLAG_CLEAR_SKB), | 
|---|
| 18240 | }, | 
|---|
| 18241 | { | 
|---|
| 18242 | .cmd = NL80211_CMD_DEL_KEY, | 
|---|
| 18243 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18244 | .doit = nl80211_del_key, | 
|---|
| 18245 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18246 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18247 | }, | 
|---|
| 18248 | { | 
|---|
| 18249 | .cmd = NL80211_CMD_SET_BEACON, | 
|---|
| 18250 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18251 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18252 | .doit = nl80211_set_beacon, | 
|---|
| 18253 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18254 | NL80211_FLAG_MLO_VALID_LINK_ID), | 
|---|
| 18255 | }, | 
|---|
| 18256 | { | 
|---|
| 18257 | .cmd = NL80211_CMD_START_AP, | 
|---|
| 18258 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18259 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18260 | .doit = nl80211_start_ap, | 
|---|
| 18261 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18262 | NL80211_FLAG_MLO_VALID_LINK_ID), | 
|---|
| 18263 | }, | 
|---|
| 18264 | { | 
|---|
| 18265 | .cmd = NL80211_CMD_STOP_AP, | 
|---|
| 18266 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18267 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18268 | .doit = nl80211_stop_ap, | 
|---|
| 18269 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18270 | NL80211_FLAG_MLO_VALID_LINK_ID), | 
|---|
| 18271 | }, | 
|---|
| 18272 | { | 
|---|
| 18273 | .cmd = NL80211_CMD_GET_STATION, | 
|---|
| 18274 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18275 | .doit = nl80211_get_station, | 
|---|
| 18276 | .dumpit = nl80211_dump_station, | 
|---|
| 18277 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV), | 
|---|
| 18278 | }, | 
|---|
| 18279 | { | 
|---|
| 18280 | .cmd = NL80211_CMD_SET_STATION, | 
|---|
| 18281 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18282 | .doit = nl80211_set_station, | 
|---|
| 18283 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18284 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18285 | }, | 
|---|
| 18286 | { | 
|---|
| 18287 | .cmd = NL80211_CMD_NEW_STATION, | 
|---|
| 18288 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18289 | .doit = nl80211_new_station, | 
|---|
| 18290 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18291 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18292 | }, | 
|---|
| 18293 | { | 
|---|
| 18294 | .cmd = NL80211_CMD_DEL_STATION, | 
|---|
| 18295 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18296 | .doit = nl80211_del_station, | 
|---|
| 18297 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18298 | /* cannot use NL80211_FLAG_MLO_VALID_LINK_ID, depends on | 
|---|
| 18299 | * whether MAC address is passed or not. If MAC address is | 
|---|
| 18300 | * passed, then even during MLO, link ID is not required. | 
|---|
| 18301 | */ | 
|---|
| 18302 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18303 | }, | 
|---|
| 18304 | { | 
|---|
| 18305 | .cmd = NL80211_CMD_GET_MPATH, | 
|---|
| 18306 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18307 | .doit = nl80211_get_mpath, | 
|---|
| 18308 | .dumpit = nl80211_dump_mpath, | 
|---|
| 18309 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18310 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18311 | }, | 
|---|
| 18312 | { | 
|---|
| 18313 | .cmd = NL80211_CMD_GET_MPP, | 
|---|
| 18314 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18315 | .doit = nl80211_get_mpp, | 
|---|
| 18316 | .dumpit = nl80211_dump_mpp, | 
|---|
| 18317 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18318 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18319 | }, | 
|---|
| 18320 | { | 
|---|
| 18321 | .cmd = NL80211_CMD_SET_MPATH, | 
|---|
| 18322 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18323 | .doit = nl80211_set_mpath, | 
|---|
| 18324 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18325 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18326 | }, | 
|---|
| 18327 | { | 
|---|
| 18328 | .cmd = NL80211_CMD_NEW_MPATH, | 
|---|
| 18329 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18330 | .doit = nl80211_new_mpath, | 
|---|
| 18331 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18332 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18333 | }, | 
|---|
| 18334 | { | 
|---|
| 18335 | .cmd = NL80211_CMD_DEL_MPATH, | 
|---|
| 18336 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18337 | .doit = nl80211_del_mpath, | 
|---|
| 18338 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18339 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18340 | }, | 
|---|
| 18341 | { | 
|---|
| 18342 | .cmd = NL80211_CMD_SET_BSS, | 
|---|
| 18343 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18344 | .doit = nl80211_set_bss, | 
|---|
| 18345 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18346 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18347 | NL80211_FLAG_MLO_VALID_LINK_ID), | 
|---|
| 18348 | }, | 
|---|
| 18349 | { | 
|---|
| 18350 | .cmd = NL80211_CMD_GET_REG, | 
|---|
| 18351 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18352 | .doit = nl80211_get_reg_do, | 
|---|
| 18353 | .dumpit = nl80211_get_reg_dump, | 
|---|
| 18354 | /* can be retrieved by unprivileged users */ | 
|---|
| 18355 | }, | 
|---|
| 18356 | #ifdef CONFIG_CFG80211_CRDA_SUPPORT | 
|---|
| 18357 | { | 
|---|
| 18358 | .cmd = NL80211_CMD_SET_REG, | 
|---|
| 18359 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18360 | .doit = nl80211_set_reg, | 
|---|
| 18361 | .flags = GENL_ADMIN_PERM, | 
|---|
| 18362 | }, | 
|---|
| 18363 | #endif | 
|---|
| 18364 | { | 
|---|
| 18365 | .cmd = NL80211_CMD_REQ_SET_REG, | 
|---|
| 18366 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18367 | .doit = nl80211_req_set_reg, | 
|---|
| 18368 | .flags = GENL_ADMIN_PERM, | 
|---|
| 18369 | }, | 
|---|
| 18370 | { | 
|---|
| 18371 | .cmd = NL80211_CMD_RELOAD_REGDB, | 
|---|
| 18372 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18373 | .doit = nl80211_reload_regdb, | 
|---|
| 18374 | .flags = GENL_ADMIN_PERM, | 
|---|
| 18375 | }, | 
|---|
| 18376 | { | 
|---|
| 18377 | .cmd = NL80211_CMD_GET_MESH_CONFIG, | 
|---|
| 18378 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18379 | .doit = nl80211_get_mesh_config, | 
|---|
| 18380 | /* can be retrieved by unprivileged users */ | 
|---|
| 18381 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18382 | }, | 
|---|
| 18383 | { | 
|---|
| 18384 | .cmd = NL80211_CMD_SET_MESH_CONFIG, | 
|---|
| 18385 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18386 | .doit = nl80211_update_mesh_config, | 
|---|
| 18387 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18388 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18389 | }, | 
|---|
| 18390 | { | 
|---|
| 18391 | .cmd = NL80211_CMD_TRIGGER_SCAN, | 
|---|
| 18392 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18393 | .doit = nl80211_trigger_scan, | 
|---|
| 18394 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18395 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), | 
|---|
| 18396 | }, | 
|---|
| 18397 | { | 
|---|
| 18398 | .cmd = NL80211_CMD_ABORT_SCAN, | 
|---|
| 18399 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18400 | .doit = nl80211_abort_scan, | 
|---|
| 18401 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18402 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), | 
|---|
| 18403 | }, | 
|---|
| 18404 | { | 
|---|
| 18405 | .cmd = NL80211_CMD_GET_SCAN, | 
|---|
| 18406 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18407 | .dumpit = nl80211_dump_scan, | 
|---|
| 18408 | }, | 
|---|
| 18409 | { | 
|---|
| 18410 | .cmd = NL80211_CMD_START_SCHED_SCAN, | 
|---|
| 18411 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18412 | .doit = nl80211_start_sched_scan, | 
|---|
| 18413 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18414 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18415 | }, | 
|---|
| 18416 | { | 
|---|
| 18417 | .cmd = NL80211_CMD_STOP_SCHED_SCAN, | 
|---|
| 18418 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18419 | .doit = nl80211_stop_sched_scan, | 
|---|
| 18420 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18421 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18422 | }, | 
|---|
| 18423 | { | 
|---|
| 18424 | .cmd = NL80211_CMD_AUTHENTICATE, | 
|---|
| 18425 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18426 | .doit = nl80211_authenticate, | 
|---|
| 18427 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18428 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18429 | NL80211_FLAG_CLEAR_SKB), | 
|---|
| 18430 | }, | 
|---|
| 18431 | { | 
|---|
| 18432 | .cmd = NL80211_CMD_ASSOCIATE, | 
|---|
| 18433 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18434 | .doit = nl80211_associate, | 
|---|
| 18435 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18436 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18437 | NL80211_FLAG_CLEAR_SKB), | 
|---|
| 18438 | }, | 
|---|
| 18439 | { | 
|---|
| 18440 | .cmd = NL80211_CMD_DEAUTHENTICATE, | 
|---|
| 18441 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18442 | .doit = nl80211_deauthenticate, | 
|---|
| 18443 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18444 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18445 | }, | 
|---|
| 18446 | { | 
|---|
| 18447 | .cmd = NL80211_CMD_DISASSOCIATE, | 
|---|
| 18448 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18449 | .doit = nl80211_disassociate, | 
|---|
| 18450 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18451 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18452 | }, | 
|---|
| 18453 | { | 
|---|
| 18454 | .cmd = NL80211_CMD_JOIN_IBSS, | 
|---|
| 18455 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18456 | .doit = nl80211_join_ibss, | 
|---|
| 18457 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18458 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18459 | }, | 
|---|
| 18460 | { | 
|---|
| 18461 | .cmd = NL80211_CMD_LEAVE_IBSS, | 
|---|
| 18462 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18463 | .doit = nl80211_leave_ibss, | 
|---|
| 18464 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18465 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18466 | }, | 
|---|
| 18467 | #ifdef CONFIG_NL80211_TESTMODE | 
|---|
| 18468 | { | 
|---|
| 18469 | .cmd = NL80211_CMD_TESTMODE, | 
|---|
| 18470 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18471 | .doit = nl80211_testmode_do, | 
|---|
| 18472 | .dumpit = nl80211_testmode_dump, | 
|---|
| 18473 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18474 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY), | 
|---|
| 18475 | }, | 
|---|
| 18476 | #endif | 
|---|
| 18477 | { | 
|---|
| 18478 | .cmd = NL80211_CMD_CONNECT, | 
|---|
| 18479 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18480 | .doit = nl80211_connect, | 
|---|
| 18481 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18482 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18483 | NL80211_FLAG_CLEAR_SKB), | 
|---|
| 18484 | }, | 
|---|
| 18485 | { | 
|---|
| 18486 | .cmd = NL80211_CMD_UPDATE_CONNECT_PARAMS, | 
|---|
| 18487 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18488 | .doit = nl80211_update_connect_params, | 
|---|
| 18489 | .flags = GENL_ADMIN_PERM, | 
|---|
| 18490 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18491 | NL80211_FLAG_CLEAR_SKB), | 
|---|
| 18492 | }, | 
|---|
| 18493 | { | 
|---|
| 18494 | .cmd = NL80211_CMD_DISCONNECT, | 
|---|
| 18495 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18496 | .doit = nl80211_disconnect, | 
|---|
| 18497 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18498 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18499 | }, | 
|---|
| 18500 | { | 
|---|
| 18501 | .cmd = NL80211_CMD_SET_WIPHY_NETNS, | 
|---|
| 18502 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18503 | .doit = nl80211_wiphy_netns, | 
|---|
| 18504 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18505 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY | | 
|---|
| 18506 | NL80211_FLAG_NEED_RTNL | | 
|---|
| 18507 | NL80211_FLAG_NO_WIPHY_MTX), | 
|---|
| 18508 | }, | 
|---|
| 18509 | { | 
|---|
| 18510 | .cmd = NL80211_CMD_GET_SURVEY, | 
|---|
| 18511 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18512 | .dumpit = nl80211_dump_survey, | 
|---|
| 18513 | }, | 
|---|
| 18514 | { | 
|---|
| 18515 | .cmd = NL80211_CMD_SET_PMKSA, | 
|---|
| 18516 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18517 | .doit = nl80211_set_pmksa, | 
|---|
| 18518 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18519 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18520 | NL80211_FLAG_CLEAR_SKB), | 
|---|
| 18521 | }, | 
|---|
| 18522 | { | 
|---|
| 18523 | .cmd = NL80211_CMD_DEL_PMKSA, | 
|---|
| 18524 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18525 | .doit = nl80211_del_pmksa, | 
|---|
| 18526 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18527 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18528 | }, | 
|---|
| 18529 | { | 
|---|
| 18530 | .cmd = NL80211_CMD_FLUSH_PMKSA, | 
|---|
| 18531 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18532 | .doit = nl80211_flush_pmksa, | 
|---|
| 18533 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18534 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18535 | }, | 
|---|
| 18536 | { | 
|---|
| 18537 | .cmd = NL80211_CMD_REMAIN_ON_CHANNEL, | 
|---|
| 18538 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18539 | .doit = nl80211_remain_on_channel, | 
|---|
| 18540 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18541 | /* FIXME: requiring a link ID here is probably not good */ | 
|---|
| 18542 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP | | 
|---|
| 18543 | NL80211_FLAG_MLO_VALID_LINK_ID), | 
|---|
| 18544 | }, | 
|---|
| 18545 | { | 
|---|
| 18546 | .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, | 
|---|
| 18547 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18548 | .doit = nl80211_cancel_remain_on_channel, | 
|---|
| 18549 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18550 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), | 
|---|
| 18551 | }, | 
|---|
| 18552 | { | 
|---|
| 18553 | .cmd = NL80211_CMD_SET_TX_BITRATE_MASK, | 
|---|
| 18554 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18555 | .doit = nl80211_set_tx_bitrate_mask, | 
|---|
| 18556 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18557 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV | | 
|---|
| 18558 | NL80211_FLAG_MLO_VALID_LINK_ID), | 
|---|
| 18559 | }, | 
|---|
| 18560 | { | 
|---|
| 18561 | .cmd = NL80211_CMD_REGISTER_FRAME, | 
|---|
| 18562 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18563 | .doit = nl80211_register_mgmt, | 
|---|
| 18564 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18565 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV), | 
|---|
| 18566 | }, | 
|---|
| 18567 | { | 
|---|
| 18568 | .cmd = NL80211_CMD_FRAME, | 
|---|
| 18569 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18570 | .doit = nl80211_tx_mgmt, | 
|---|
| 18571 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18572 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), | 
|---|
| 18573 | }, | 
|---|
| 18574 | { | 
|---|
| 18575 | .cmd = NL80211_CMD_FRAME_WAIT_CANCEL, | 
|---|
| 18576 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18577 | .doit = nl80211_tx_mgmt_cancel_wait, | 
|---|
| 18578 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18579 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), | 
|---|
| 18580 | }, | 
|---|
| 18581 | { | 
|---|
| 18582 | .cmd = NL80211_CMD_SET_POWER_SAVE, | 
|---|
| 18583 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18584 | .doit = nl80211_set_power_save, | 
|---|
| 18585 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18586 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV), | 
|---|
| 18587 | }, | 
|---|
| 18588 | { | 
|---|
| 18589 | .cmd = NL80211_CMD_GET_POWER_SAVE, | 
|---|
| 18590 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18591 | .doit = nl80211_get_power_save, | 
|---|
| 18592 | /* can be retrieved by unprivileged users */ | 
|---|
| 18593 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV), | 
|---|
| 18594 | }, | 
|---|
| 18595 | { | 
|---|
| 18596 | .cmd = NL80211_CMD_SET_CQM, | 
|---|
| 18597 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18598 | .doit = nl80211_set_cqm, | 
|---|
| 18599 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18600 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV), | 
|---|
| 18601 | }, | 
|---|
| 18602 | { | 
|---|
| 18603 | .cmd = NL80211_CMD_SET_CHANNEL, | 
|---|
| 18604 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18605 | .doit = nl80211_set_channel, | 
|---|
| 18606 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18607 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV | | 
|---|
| 18608 | NL80211_FLAG_MLO_VALID_LINK_ID), | 
|---|
| 18609 | }, | 
|---|
| 18610 | { | 
|---|
| 18611 | .cmd = NL80211_CMD_JOIN_MESH, | 
|---|
| 18612 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18613 | .doit = nl80211_join_mesh, | 
|---|
| 18614 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18615 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18616 | }, | 
|---|
| 18617 | { | 
|---|
| 18618 | .cmd = NL80211_CMD_LEAVE_MESH, | 
|---|
| 18619 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18620 | .doit = nl80211_leave_mesh, | 
|---|
| 18621 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18622 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18623 | }, | 
|---|
| 18624 | { | 
|---|
| 18625 | .cmd = NL80211_CMD_JOIN_OCB, | 
|---|
| 18626 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18627 | .doit = nl80211_join_ocb, | 
|---|
| 18628 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18629 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18630 | }, | 
|---|
| 18631 | { | 
|---|
| 18632 | .cmd = NL80211_CMD_LEAVE_OCB, | 
|---|
| 18633 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18634 | .doit = nl80211_leave_ocb, | 
|---|
| 18635 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18636 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18637 | }, | 
|---|
| 18638 | #ifdef CONFIG_PM | 
|---|
| 18639 | { | 
|---|
| 18640 | .cmd = NL80211_CMD_GET_WOWLAN, | 
|---|
| 18641 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18642 | .doit = nl80211_get_wowlan, | 
|---|
| 18643 | /* can be retrieved by unprivileged users */ | 
|---|
| 18644 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY), | 
|---|
| 18645 | }, | 
|---|
| 18646 | { | 
|---|
| 18647 | .cmd = NL80211_CMD_SET_WOWLAN, | 
|---|
| 18648 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18649 | .doit = nl80211_set_wowlan, | 
|---|
| 18650 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18651 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY), | 
|---|
| 18652 | }, | 
|---|
| 18653 | #endif | 
|---|
| 18654 | { | 
|---|
| 18655 | .cmd = NL80211_CMD_SET_REKEY_OFFLOAD, | 
|---|
| 18656 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18657 | .doit = nl80211_set_rekey_data, | 
|---|
| 18658 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18659 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18660 | NL80211_FLAG_CLEAR_SKB), | 
|---|
| 18661 | }, | 
|---|
| 18662 | { | 
|---|
| 18663 | .cmd = NL80211_CMD_TDLS_MGMT, | 
|---|
| 18664 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18665 | .doit = nl80211_tdls_mgmt, | 
|---|
| 18666 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18667 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18668 | NL80211_FLAG_MLO_VALID_LINK_ID), | 
|---|
| 18669 | }, | 
|---|
| 18670 | { | 
|---|
| 18671 | .cmd = NL80211_CMD_TDLS_OPER, | 
|---|
| 18672 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18673 | .doit = nl80211_tdls_oper, | 
|---|
| 18674 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18675 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18676 | }, | 
|---|
| 18677 | { | 
|---|
| 18678 | .cmd = NL80211_CMD_UNEXPECTED_FRAME, | 
|---|
| 18679 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18680 | .doit = nl80211_register_unexpected_frame, | 
|---|
| 18681 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18682 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV), | 
|---|
| 18683 | }, | 
|---|
| 18684 | { | 
|---|
| 18685 | .cmd = NL80211_CMD_PROBE_CLIENT, | 
|---|
| 18686 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18687 | .doit = nl80211_probe_client, | 
|---|
| 18688 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18689 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18690 | }, | 
|---|
| 18691 | { | 
|---|
| 18692 | .cmd = NL80211_CMD_REGISTER_BEACONS, | 
|---|
| 18693 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18694 | .doit = nl80211_register_beacons, | 
|---|
| 18695 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18696 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY), | 
|---|
| 18697 | }, | 
|---|
| 18698 | { | 
|---|
| 18699 | .cmd = NL80211_CMD_SET_NOACK_MAP, | 
|---|
| 18700 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18701 | .doit = nl80211_set_noack_map, | 
|---|
| 18702 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18703 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV), | 
|---|
| 18704 | }, | 
|---|
| 18705 | { | 
|---|
| 18706 | .cmd = NL80211_CMD_START_P2P_DEVICE, | 
|---|
| 18707 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18708 | .doit = nl80211_start_p2p_device, | 
|---|
| 18709 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18710 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV | | 
|---|
| 18711 | NL80211_FLAG_NEED_RTNL), | 
|---|
| 18712 | }, | 
|---|
| 18713 | { | 
|---|
| 18714 | .cmd = NL80211_CMD_STOP_P2P_DEVICE, | 
|---|
| 18715 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18716 | .doit = nl80211_stop_p2p_device, | 
|---|
| 18717 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18718 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP | | 
|---|
| 18719 | NL80211_FLAG_NEED_RTNL), | 
|---|
| 18720 | }, | 
|---|
| 18721 | { | 
|---|
| 18722 | .cmd = NL80211_CMD_START_NAN, | 
|---|
| 18723 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18724 | .doit = nl80211_start_nan, | 
|---|
| 18725 | .flags = GENL_ADMIN_PERM, | 
|---|
| 18726 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV | | 
|---|
| 18727 | NL80211_FLAG_NEED_RTNL), | 
|---|
| 18728 | }, | 
|---|
| 18729 | { | 
|---|
| 18730 | .cmd = NL80211_CMD_STOP_NAN, | 
|---|
| 18731 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18732 | .doit = nl80211_stop_nan, | 
|---|
| 18733 | .flags = GENL_ADMIN_PERM, | 
|---|
| 18734 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP | | 
|---|
| 18735 | NL80211_FLAG_NEED_RTNL), | 
|---|
| 18736 | }, | 
|---|
| 18737 | { | 
|---|
| 18738 | .cmd = NL80211_CMD_ADD_NAN_FUNCTION, | 
|---|
| 18739 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18740 | .doit = nl80211_nan_add_func, | 
|---|
| 18741 | .flags = GENL_ADMIN_PERM, | 
|---|
| 18742 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), | 
|---|
| 18743 | }, | 
|---|
| 18744 | { | 
|---|
| 18745 | .cmd = NL80211_CMD_DEL_NAN_FUNCTION, | 
|---|
| 18746 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18747 | .doit = nl80211_nan_del_func, | 
|---|
| 18748 | .flags = GENL_ADMIN_PERM, | 
|---|
| 18749 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), | 
|---|
| 18750 | }, | 
|---|
| 18751 | { | 
|---|
| 18752 | .cmd = NL80211_CMD_CHANGE_NAN_CONFIG, | 
|---|
| 18753 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18754 | .doit = nl80211_nan_change_config, | 
|---|
| 18755 | .flags = GENL_ADMIN_PERM, | 
|---|
| 18756 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), | 
|---|
| 18757 | }, | 
|---|
| 18758 | { | 
|---|
| 18759 | .cmd = NL80211_CMD_SET_MCAST_RATE, | 
|---|
| 18760 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18761 | .doit = nl80211_set_mcast_rate, | 
|---|
| 18762 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18763 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV), | 
|---|
| 18764 | }, | 
|---|
| 18765 | { | 
|---|
| 18766 | .cmd = NL80211_CMD_SET_MAC_ACL, | 
|---|
| 18767 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18768 | .doit = nl80211_set_mac_acl, | 
|---|
| 18769 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18770 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV | | 
|---|
| 18771 | NL80211_FLAG_MLO_UNSUPPORTED), | 
|---|
| 18772 | }, | 
|---|
| 18773 | { | 
|---|
| 18774 | .cmd = NL80211_CMD_RADAR_DETECT, | 
|---|
| 18775 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18776 | .doit = nl80211_start_radar_detection, | 
|---|
| 18777 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18778 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18779 | NL80211_FLAG_NO_WIPHY_MTX | | 
|---|
| 18780 | NL80211_FLAG_MLO_VALID_LINK_ID), | 
|---|
| 18781 | }, | 
|---|
| 18782 | { | 
|---|
| 18783 | .cmd = NL80211_CMD_GET_PROTOCOL_FEATURES, | 
|---|
| 18784 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18785 | .doit = nl80211_get_protocol_features, | 
|---|
| 18786 | }, | 
|---|
| 18787 | { | 
|---|
| 18788 | .cmd = NL80211_CMD_UPDATE_FT_IES, | 
|---|
| 18789 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18790 | .doit = nl80211_update_ft_ies, | 
|---|
| 18791 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18792 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18793 | }, | 
|---|
| 18794 | { | 
|---|
| 18795 | .cmd = NL80211_CMD_CRIT_PROTOCOL_START, | 
|---|
| 18796 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18797 | .doit = nl80211_crit_protocol_start, | 
|---|
| 18798 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18799 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), | 
|---|
| 18800 | }, | 
|---|
| 18801 | { | 
|---|
| 18802 | .cmd = NL80211_CMD_CRIT_PROTOCOL_STOP, | 
|---|
| 18803 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18804 | .doit = nl80211_crit_protocol_stop, | 
|---|
| 18805 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18806 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), | 
|---|
| 18807 | }, | 
|---|
| 18808 | { | 
|---|
| 18809 | .cmd = NL80211_CMD_GET_COALESCE, | 
|---|
| 18810 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18811 | .doit = nl80211_get_coalesce, | 
|---|
| 18812 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY), | 
|---|
| 18813 | }, | 
|---|
| 18814 | { | 
|---|
| 18815 | .cmd = NL80211_CMD_SET_COALESCE, | 
|---|
| 18816 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18817 | .doit = nl80211_set_coalesce, | 
|---|
| 18818 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18819 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY), | 
|---|
| 18820 | }, | 
|---|
| 18821 | { | 
|---|
| 18822 | .cmd = NL80211_CMD_CHANNEL_SWITCH, | 
|---|
| 18823 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18824 | .doit = nl80211_channel_switch, | 
|---|
| 18825 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18826 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18827 | NL80211_FLAG_MLO_VALID_LINK_ID), | 
|---|
| 18828 | }, | 
|---|
| 18829 | { | 
|---|
| 18830 | .cmd = NL80211_CMD_VENDOR, | 
|---|
| 18831 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18832 | .doit = nl80211_vendor_cmd, | 
|---|
| 18833 | .dumpit = nl80211_vendor_cmd_dump, | 
|---|
| 18834 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18835 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY | | 
|---|
| 18836 | NL80211_FLAG_CLEAR_SKB), | 
|---|
| 18837 | }, | 
|---|
| 18838 | { | 
|---|
| 18839 | .cmd = NL80211_CMD_SET_QOS_MAP, | 
|---|
| 18840 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18841 | .doit = nl80211_set_qos_map, | 
|---|
| 18842 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18843 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18844 | }, | 
|---|
| 18845 | { | 
|---|
| 18846 | .cmd = NL80211_CMD_ADD_TX_TS, | 
|---|
| 18847 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18848 | .doit = nl80211_add_tx_ts, | 
|---|
| 18849 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18850 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18851 | NL80211_FLAG_MLO_UNSUPPORTED), | 
|---|
| 18852 | }, | 
|---|
| 18853 | { | 
|---|
| 18854 | .cmd = NL80211_CMD_DEL_TX_TS, | 
|---|
| 18855 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18856 | .doit = nl80211_del_tx_ts, | 
|---|
| 18857 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18858 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18859 | }, | 
|---|
| 18860 | { | 
|---|
| 18861 | .cmd = NL80211_CMD_TDLS_CHANNEL_SWITCH, | 
|---|
| 18862 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18863 | .doit = nl80211_tdls_channel_switch, | 
|---|
| 18864 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18865 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18866 | }, | 
|---|
| 18867 | { | 
|---|
| 18868 | .cmd = NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH, | 
|---|
| 18869 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18870 | .doit = nl80211_tdls_cancel_channel_switch, | 
|---|
| 18871 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18872 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18873 | }, | 
|---|
| 18874 | { | 
|---|
| 18875 | .cmd = NL80211_CMD_SET_MULTICAST_TO_UNICAST, | 
|---|
| 18876 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18877 | .doit = nl80211_set_multicast_to_unicast, | 
|---|
| 18878 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18879 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV), | 
|---|
| 18880 | }, | 
|---|
| 18881 | { | 
|---|
| 18882 | .cmd = NL80211_CMD_SET_PMK, | 
|---|
| 18883 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18884 | .doit = nl80211_set_pmk, | 
|---|
| 18885 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18886 | NL80211_FLAG_CLEAR_SKB), | 
|---|
| 18887 | }, | 
|---|
| 18888 | { | 
|---|
| 18889 | .cmd = NL80211_CMD_DEL_PMK, | 
|---|
| 18890 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18891 | .doit = nl80211_del_pmk, | 
|---|
| 18892 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18893 | }, | 
|---|
| 18894 | { | 
|---|
| 18895 | .cmd = NL80211_CMD_EXTERNAL_AUTH, | 
|---|
| 18896 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18897 | .doit = nl80211_external_auth, | 
|---|
| 18898 | .flags = GENL_ADMIN_PERM, | 
|---|
| 18899 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18900 | }, | 
|---|
| 18901 | { | 
|---|
| 18902 | .cmd = NL80211_CMD_CONTROL_PORT_FRAME, | 
|---|
| 18903 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18904 | .doit = nl80211_tx_control_port, | 
|---|
| 18905 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18906 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18907 | }, | 
|---|
| 18908 | { | 
|---|
| 18909 | .cmd = NL80211_CMD_GET_FTM_RESPONDER_STATS, | 
|---|
| 18910 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18911 | .doit = nl80211_get_ftm_responder_stats, | 
|---|
| 18912 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV | | 
|---|
| 18913 | NL80211_FLAG_MLO_VALID_LINK_ID), | 
|---|
| 18914 | }, | 
|---|
| 18915 | { | 
|---|
| 18916 | .cmd = NL80211_CMD_PEER_MEASUREMENT_START, | 
|---|
| 18917 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18918 | .doit = nl80211_pmsr_start, | 
|---|
| 18919 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18920 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), | 
|---|
| 18921 | }, | 
|---|
| 18922 | { | 
|---|
| 18923 | .cmd = NL80211_CMD_NOTIFY_RADAR, | 
|---|
| 18924 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18925 | .doit = nl80211_notify_radar_detection, | 
|---|
| 18926 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18927 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18928 | }, | 
|---|
| 18929 | { | 
|---|
| 18930 | .cmd = NL80211_CMD_UPDATE_OWE_INFO, | 
|---|
| 18931 | .doit = nl80211_update_owe_info, | 
|---|
| 18932 | .flags = GENL_ADMIN_PERM, | 
|---|
| 18933 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18934 | }, | 
|---|
| 18935 | { | 
|---|
| 18936 | .cmd = NL80211_CMD_PROBE_MESH_LINK, | 
|---|
| 18937 | .doit = nl80211_probe_mesh_link, | 
|---|
| 18938 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18939 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18940 | }, | 
|---|
| 18941 | { | 
|---|
| 18942 | .cmd = NL80211_CMD_SET_TID_CONFIG, | 
|---|
| 18943 | .doit = nl80211_set_tid_config, | 
|---|
| 18944 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18945 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV | | 
|---|
| 18946 | NL80211_FLAG_MLO_VALID_LINK_ID), | 
|---|
| 18947 | }, | 
|---|
| 18948 | { | 
|---|
| 18949 | .cmd = NL80211_CMD_SET_SAR_SPECS, | 
|---|
| 18950 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18951 | .doit = nl80211_set_sar_specs, | 
|---|
| 18952 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18953 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY | | 
|---|
| 18954 | NL80211_FLAG_NEED_RTNL), | 
|---|
| 18955 | }, | 
|---|
| 18956 | { | 
|---|
| 18957 | .cmd = NL80211_CMD_COLOR_CHANGE_REQUEST, | 
|---|
| 18958 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18959 | .doit = nl80211_color_change, | 
|---|
| 18960 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18961 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18962 | NL80211_FLAG_MLO_VALID_LINK_ID), | 
|---|
| 18963 | }, | 
|---|
| 18964 | { | 
|---|
| 18965 | .cmd = NL80211_CMD_SET_FILS_AAD, | 
|---|
| 18966 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, | 
|---|
| 18967 | .doit = nl80211_set_fils_aad, | 
|---|
| 18968 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18969 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18970 | }, | 
|---|
| 18971 | { | 
|---|
| 18972 | .cmd = NL80211_CMD_ADD_LINK, | 
|---|
| 18973 | .doit = nl80211_add_link, | 
|---|
| 18974 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18975 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 18976 | }, | 
|---|
| 18977 | { | 
|---|
| 18978 | .cmd = NL80211_CMD_REMOVE_LINK, | 
|---|
| 18979 | .doit = nl80211_remove_link, | 
|---|
| 18980 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18981 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18982 | NL80211_FLAG_MLO_VALID_LINK_ID), | 
|---|
| 18983 | }, | 
|---|
| 18984 | { | 
|---|
| 18985 | .cmd = NL80211_CMD_ADD_LINK_STA, | 
|---|
| 18986 | .doit = nl80211_add_link_station, | 
|---|
| 18987 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18988 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18989 | NL80211_FLAG_MLO_VALID_LINK_ID), | 
|---|
| 18990 | }, | 
|---|
| 18991 | { | 
|---|
| 18992 | .cmd = NL80211_CMD_MODIFY_LINK_STA, | 
|---|
| 18993 | .doit = nl80211_modify_link_station, | 
|---|
| 18994 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 18995 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 18996 | NL80211_FLAG_MLO_VALID_LINK_ID), | 
|---|
| 18997 | }, | 
|---|
| 18998 | { | 
|---|
| 18999 | .cmd = NL80211_CMD_REMOVE_LINK_STA, | 
|---|
| 19000 | .doit = nl80211_remove_link_station, | 
|---|
| 19001 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 19002 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | | 
|---|
| 19003 | NL80211_FLAG_MLO_VALID_LINK_ID), | 
|---|
| 19004 | }, | 
|---|
| 19005 | { | 
|---|
| 19006 | .cmd = NL80211_CMD_SET_HW_TIMESTAMP, | 
|---|
| 19007 | .doit = nl80211_set_hw_timestamp, | 
|---|
| 19008 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 19009 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 19010 | }, | 
|---|
| 19011 | { | 
|---|
| 19012 | .cmd = NL80211_CMD_SET_TID_TO_LINK_MAPPING, | 
|---|
| 19013 | .doit = nl80211_set_ttlm, | 
|---|
| 19014 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 19015 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 19016 | }, | 
|---|
| 19017 | { | 
|---|
| 19018 | .cmd = NL80211_CMD_ASSOC_MLO_RECONF, | 
|---|
| 19019 | .doit = nl80211_assoc_ml_reconf, | 
|---|
| 19020 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 19021 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 19022 | }, | 
|---|
| 19023 | { | 
|---|
| 19024 | .cmd = NL80211_CMD_EPCS_CFG, | 
|---|
| 19025 | .doit = nl80211_epcs_cfg, | 
|---|
| 19026 | .flags = GENL_UNS_ADMIN_PERM, | 
|---|
| 19027 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), | 
|---|
| 19028 | }, | 
|---|
| 19029 | }; | 
|---|
| 19030 |  | 
|---|
| 19031 | static struct genl_family nl80211_fam __ro_after_init = { | 
|---|
| 19032 | .name = NL80211_GENL_NAME,	/* have users key off the name instead */ | 
|---|
| 19033 | .hdrsize = 0,			/* no private header */ | 
|---|
| 19034 | .version = 1,			/* no particular meaning now */ | 
|---|
| 19035 | .maxattr = NL80211_ATTR_MAX, | 
|---|
| 19036 | .policy = nl80211_policy, | 
|---|
| 19037 | .netnsok = true, | 
|---|
| 19038 | .pre_doit = nl80211_pre_doit, | 
|---|
| 19039 | .post_doit = nl80211_post_doit, | 
|---|
| 19040 | .module = THIS_MODULE, | 
|---|
| 19041 | .ops = nl80211_ops, | 
|---|
| 19042 | .n_ops = ARRAY_SIZE(nl80211_ops), | 
|---|
| 19043 | .small_ops = nl80211_small_ops, | 
|---|
| 19044 | .n_small_ops = ARRAY_SIZE(nl80211_small_ops), | 
|---|
| 19045 | .resv_start_op = NL80211_CMD_REMOVE_LINK_STA + 1, | 
|---|
| 19046 | .mcgrps = nl80211_mcgrps, | 
|---|
| 19047 | .n_mcgrps = ARRAY_SIZE(nl80211_mcgrps), | 
|---|
| 19048 | .parallel_ops = true, | 
|---|
| 19049 | }; | 
|---|
| 19050 |  | 
|---|
| 19051 | /* notification functions */ | 
|---|
| 19052 |  | 
|---|
| 19053 | void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev, | 
|---|
| 19054 | enum nl80211_commands cmd) | 
|---|
| 19055 | { | 
|---|
| 19056 | struct sk_buff *msg; | 
|---|
| 19057 | struct nl80211_dump_wiphy_state state = {}; | 
|---|
| 19058 |  | 
|---|
| 19059 | WARN_ON(cmd != NL80211_CMD_NEW_WIPHY && | 
|---|
| 19060 | cmd != NL80211_CMD_DEL_WIPHY); | 
|---|
| 19061 |  | 
|---|
| 19062 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 19063 | if (!msg) | 
|---|
| 19064 | return; | 
|---|
| 19065 |  | 
|---|
| 19066 | if (nl80211_send_wiphy(rdev, cmd, msg, portid: 0, seq: 0, flags: 0, state: &state) < 0) { | 
|---|
| 19067 | nlmsg_free(skb: msg); | 
|---|
| 19068 | return; | 
|---|
| 19069 | } | 
|---|
| 19070 |  | 
|---|
| 19071 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 19072 | group: NL80211_MCGRP_CONFIG, GFP_KERNEL); | 
|---|
| 19073 | } | 
|---|
| 19074 |  | 
|---|
| 19075 | void nl80211_notify_iface(struct cfg80211_registered_device *rdev, | 
|---|
| 19076 | struct wireless_dev *wdev, | 
|---|
| 19077 | enum nl80211_commands cmd) | 
|---|
| 19078 | { | 
|---|
| 19079 | struct sk_buff *msg; | 
|---|
| 19080 |  | 
|---|
| 19081 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 19082 | if (!msg) | 
|---|
| 19083 | return; | 
|---|
| 19084 |  | 
|---|
| 19085 | if (nl80211_send_iface(msg, portid: 0, seq: 0, flags: 0, rdev, wdev, cmd) < 0) { | 
|---|
| 19086 | nlmsg_free(skb: msg); | 
|---|
| 19087 | return; | 
|---|
| 19088 | } | 
|---|
| 19089 |  | 
|---|
| 19090 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 19091 | group: NL80211_MCGRP_CONFIG, GFP_KERNEL); | 
|---|
| 19092 | } | 
|---|
| 19093 |  | 
|---|
| 19094 | static int nl80211_add_scan_req(struct sk_buff *msg, | 
|---|
| 19095 | struct cfg80211_registered_device *rdev) | 
|---|
| 19096 | { | 
|---|
| 19097 | struct cfg80211_scan_request_int *req = rdev->scan_req; | 
|---|
| 19098 | struct nlattr *nest; | 
|---|
| 19099 | int i; | 
|---|
| 19100 | struct cfg80211_scan_info *info; | 
|---|
| 19101 |  | 
|---|
| 19102 | if (WARN_ON(!req)) | 
|---|
| 19103 | return 0; | 
|---|
| 19104 |  | 
|---|
| 19105 | nest = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_SCAN_SSIDS); | 
|---|
| 19106 | if (!nest) | 
|---|
| 19107 | goto nla_put_failure; | 
|---|
| 19108 | for (i = 0; i < req->req.n_ssids; i++) { | 
|---|
| 19109 | if (nla_put(skb: msg, attrtype: i, attrlen: req->req.ssids[i].ssid_len, | 
|---|
| 19110 | data: req->req.ssids[i].ssid)) | 
|---|
| 19111 | goto nla_put_failure; | 
|---|
| 19112 | } | 
|---|
| 19113 | nla_nest_end(skb: msg, start: nest); | 
|---|
| 19114 |  | 
|---|
| 19115 | if (req->req.flags & NL80211_SCAN_FLAG_FREQ_KHZ) { | 
|---|
| 19116 | nest = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_SCAN_FREQ_KHZ); | 
|---|
| 19117 | if (!nest) | 
|---|
| 19118 | goto nla_put_failure; | 
|---|
| 19119 | for (i = 0; i < req->req.n_channels; i++) { | 
|---|
| 19120 | if (nla_put_u32(skb: msg, attrtype: i, | 
|---|
| 19121 | value: ieee80211_channel_to_khz(chan: req->req.channels[i]))) | 
|---|
| 19122 | goto nla_put_failure; | 
|---|
| 19123 | } | 
|---|
| 19124 | nla_nest_end(skb: msg, start: nest); | 
|---|
| 19125 | } else { | 
|---|
| 19126 | nest = nla_nest_start_noflag(skb: msg, | 
|---|
| 19127 | attrtype: NL80211_ATTR_SCAN_FREQUENCIES); | 
|---|
| 19128 | if (!nest) | 
|---|
| 19129 | goto nla_put_failure; | 
|---|
| 19130 | for (i = 0; i < req->req.n_channels; i++) { | 
|---|
| 19131 | if (nla_put_u32(skb: msg, attrtype: i, | 
|---|
| 19132 | value: req->req.channels[i]->center_freq)) | 
|---|
| 19133 | goto nla_put_failure; | 
|---|
| 19134 | } | 
|---|
| 19135 | nla_nest_end(skb: msg, start: nest); | 
|---|
| 19136 | } | 
|---|
| 19137 |  | 
|---|
| 19138 | if (req->req.ie && | 
|---|
| 19139 | nla_put(skb: msg, NL80211_ATTR_IE, attrlen: req->req.ie_len, data: req->req.ie)) | 
|---|
| 19140 | goto nla_put_failure; | 
|---|
| 19141 |  | 
|---|
| 19142 | if (req->req.flags && | 
|---|
| 19143 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_SCAN_FLAGS, value: req->req.flags)) | 
|---|
| 19144 | goto nla_put_failure; | 
|---|
| 19145 |  | 
|---|
| 19146 | info = rdev->int_scan_req ? &rdev->int_scan_req->info : | 
|---|
| 19147 | &rdev->scan_req->info; | 
|---|
| 19148 | if (info->scan_start_tsf && | 
|---|
| 19149 | (nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_SCAN_START_TIME_TSF, | 
|---|
| 19150 | value: info->scan_start_tsf, padattr: NL80211_BSS_PAD) || | 
|---|
| 19151 | nla_put(skb: msg, attrtype: NL80211_ATTR_SCAN_START_TIME_TSF_BSSID, ETH_ALEN, | 
|---|
| 19152 | data: info->tsf_bssid))) | 
|---|
| 19153 | goto nla_put_failure; | 
|---|
| 19154 |  | 
|---|
| 19155 | return 0; | 
|---|
| 19156 | nla_put_failure: | 
|---|
| 19157 | return -ENOBUFS; | 
|---|
| 19158 | } | 
|---|
| 19159 |  | 
|---|
| 19160 | static int nl80211_prep_scan_msg(struct sk_buff *msg, | 
|---|
| 19161 | struct cfg80211_registered_device *rdev, | 
|---|
| 19162 | struct wireless_dev *wdev, | 
|---|
| 19163 | u32 portid, u32 seq, int flags, | 
|---|
| 19164 | u32 cmd) | 
|---|
| 19165 | { | 
|---|
| 19166 | void *hdr; | 
|---|
| 19167 |  | 
|---|
| 19168 | hdr = nl80211hdr_put(skb: msg, portid, seq, flags, cmd); | 
|---|
| 19169 | if (!hdr) | 
|---|
| 19170 | return -1; | 
|---|
| 19171 |  | 
|---|
| 19172 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 19173 | (wdev->netdev && nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, | 
|---|
| 19174 | value: wdev->netdev->ifindex)) || | 
|---|
| 19175 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), | 
|---|
| 19176 | padattr: NL80211_ATTR_PAD)) | 
|---|
| 19177 | goto nla_put_failure; | 
|---|
| 19178 |  | 
|---|
| 19179 | /* ignore errors and send incomplete event anyway */ | 
|---|
| 19180 | nl80211_add_scan_req(msg, rdev); | 
|---|
| 19181 |  | 
|---|
| 19182 | genlmsg_end(skb: msg, hdr); | 
|---|
| 19183 | return 0; | 
|---|
| 19184 |  | 
|---|
| 19185 | nla_put_failure: | 
|---|
| 19186 | genlmsg_cancel(skb: msg, hdr); | 
|---|
| 19187 | return -EMSGSIZE; | 
|---|
| 19188 | } | 
|---|
| 19189 |  | 
|---|
| 19190 | static int | 
|---|
| 19191 | nl80211_prep_sched_scan_msg(struct sk_buff *msg, | 
|---|
| 19192 | struct cfg80211_sched_scan_request *req, u32 cmd) | 
|---|
| 19193 | { | 
|---|
| 19194 | void *hdr; | 
|---|
| 19195 |  | 
|---|
| 19196 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd); | 
|---|
| 19197 | if (!hdr) | 
|---|
| 19198 | return -1; | 
|---|
| 19199 |  | 
|---|
| 19200 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, | 
|---|
| 19201 | value: wiphy_to_rdev(wiphy: req->wiphy)->wiphy_idx) || | 
|---|
| 19202 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: req->dev->ifindex) || | 
|---|
| 19203 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: req->reqid, | 
|---|
| 19204 | padattr: NL80211_ATTR_PAD)) | 
|---|
| 19205 | goto nla_put_failure; | 
|---|
| 19206 |  | 
|---|
| 19207 | genlmsg_end(skb: msg, hdr); | 
|---|
| 19208 | return 0; | 
|---|
| 19209 |  | 
|---|
| 19210 | nla_put_failure: | 
|---|
| 19211 | genlmsg_cancel(skb: msg, hdr); | 
|---|
| 19212 | return -EMSGSIZE; | 
|---|
| 19213 | } | 
|---|
| 19214 |  | 
|---|
| 19215 | void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, | 
|---|
| 19216 | struct wireless_dev *wdev) | 
|---|
| 19217 | { | 
|---|
| 19218 | struct sk_buff *msg; | 
|---|
| 19219 |  | 
|---|
| 19220 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 19221 | if (!msg) | 
|---|
| 19222 | return; | 
|---|
| 19223 |  | 
|---|
| 19224 | if (nl80211_prep_scan_msg(msg, rdev, wdev, portid: 0, seq: 0, flags: 0, | 
|---|
| 19225 | cmd: NL80211_CMD_TRIGGER_SCAN) < 0) { | 
|---|
| 19226 | nlmsg_free(skb: msg); | 
|---|
| 19227 | return; | 
|---|
| 19228 | } | 
|---|
| 19229 |  | 
|---|
| 19230 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 19231 | group: NL80211_MCGRP_SCAN, GFP_KERNEL); | 
|---|
| 19232 | } | 
|---|
| 19233 |  | 
|---|
| 19234 | struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev, | 
|---|
| 19235 | struct wireless_dev *wdev, bool aborted) | 
|---|
| 19236 | { | 
|---|
| 19237 | struct sk_buff *msg; | 
|---|
| 19238 |  | 
|---|
| 19239 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 19240 | if (!msg) | 
|---|
| 19241 | return NULL; | 
|---|
| 19242 |  | 
|---|
| 19243 | if (nl80211_prep_scan_msg(msg, rdev, wdev, portid: 0, seq: 0, flags: 0, | 
|---|
| 19244 | cmd: aborted ? NL80211_CMD_SCAN_ABORTED : | 
|---|
| 19245 | NL80211_CMD_NEW_SCAN_RESULTS) < 0) { | 
|---|
| 19246 | nlmsg_free(skb: msg); | 
|---|
| 19247 | return NULL; | 
|---|
| 19248 | } | 
|---|
| 19249 |  | 
|---|
| 19250 | return msg; | 
|---|
| 19251 | } | 
|---|
| 19252 |  | 
|---|
| 19253 | /* send message created by nl80211_build_scan_msg() */ | 
|---|
| 19254 | void nl80211_send_scan_msg(struct cfg80211_registered_device *rdev, | 
|---|
| 19255 | struct sk_buff *msg) | 
|---|
| 19256 | { | 
|---|
| 19257 | if (!msg) | 
|---|
| 19258 | return; | 
|---|
| 19259 |  | 
|---|
| 19260 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 19261 | group: NL80211_MCGRP_SCAN, GFP_KERNEL); | 
|---|
| 19262 | } | 
|---|
| 19263 |  | 
|---|
| 19264 | void nl80211_send_sched_scan(struct cfg80211_sched_scan_request *req, u32 cmd) | 
|---|
| 19265 | { | 
|---|
| 19266 | struct sk_buff *msg; | 
|---|
| 19267 |  | 
|---|
| 19268 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 19269 | if (!msg) | 
|---|
| 19270 | return; | 
|---|
| 19271 |  | 
|---|
| 19272 | if (nl80211_prep_sched_scan_msg(msg, req, cmd) < 0) { | 
|---|
| 19273 | nlmsg_free(skb: msg); | 
|---|
| 19274 | return; | 
|---|
| 19275 | } | 
|---|
| 19276 |  | 
|---|
| 19277 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: req->wiphy), skb: msg, portid: 0, | 
|---|
| 19278 | group: NL80211_MCGRP_SCAN, GFP_KERNEL); | 
|---|
| 19279 | } | 
|---|
| 19280 |  | 
|---|
| 19281 | static bool nl80211_reg_change_event_fill(struct sk_buff *msg, | 
|---|
| 19282 | struct regulatory_request *request) | 
|---|
| 19283 | { | 
|---|
| 19284 | /* Userspace can always count this one always being set */ | 
|---|
| 19285 | if (nla_put_u8(skb: msg, NL80211_ATTR_REG_INITIATOR, value: request->initiator)) | 
|---|
| 19286 | goto nla_put_failure; | 
|---|
| 19287 |  | 
|---|
| 19288 | if (request->alpha2[0] == '0' && request->alpha2[1] == '0') { | 
|---|
| 19289 | if (nla_put_u8(skb: msg, NL80211_ATTR_REG_TYPE, | 
|---|
| 19290 | value: NL80211_REGDOM_TYPE_WORLD)) | 
|---|
| 19291 | goto nla_put_failure; | 
|---|
| 19292 | } else if (request->alpha2[0] == '9' && request->alpha2[1] == '9') { | 
|---|
| 19293 | if (nla_put_u8(skb: msg, NL80211_ATTR_REG_TYPE, | 
|---|
| 19294 | value: NL80211_REGDOM_TYPE_CUSTOM_WORLD)) | 
|---|
| 19295 | goto nla_put_failure; | 
|---|
| 19296 | } else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') || | 
|---|
| 19297 | request->intersect) { | 
|---|
| 19298 | if (nla_put_u8(skb: msg, NL80211_ATTR_REG_TYPE, | 
|---|
| 19299 | value: NL80211_REGDOM_TYPE_INTERSECTION)) | 
|---|
| 19300 | goto nla_put_failure; | 
|---|
| 19301 | } else { | 
|---|
| 19302 | if (nla_put_u8(skb: msg, NL80211_ATTR_REG_TYPE, | 
|---|
| 19303 | value: NL80211_REGDOM_TYPE_COUNTRY) || | 
|---|
| 19304 | nla_put_string(skb: msg, attrtype: NL80211_ATTR_REG_ALPHA2, | 
|---|
| 19305 | str: request->alpha2)) | 
|---|
| 19306 | goto nla_put_failure; | 
|---|
| 19307 | } | 
|---|
| 19308 |  | 
|---|
| 19309 | if (request->wiphy_idx != WIPHY_IDX_INVALID) { | 
|---|
| 19310 | struct wiphy *wiphy = wiphy_idx_to_wiphy(wiphy_idx: request->wiphy_idx); | 
|---|
| 19311 |  | 
|---|
| 19312 | if (wiphy && | 
|---|
| 19313 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: request->wiphy_idx)) | 
|---|
| 19314 | goto nla_put_failure; | 
|---|
| 19315 |  | 
|---|
| 19316 | if (wiphy && | 
|---|
| 19317 | wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED && | 
|---|
| 19318 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_WIPHY_SELF_MANAGED_REG)) | 
|---|
| 19319 | goto nla_put_failure; | 
|---|
| 19320 | } | 
|---|
| 19321 |  | 
|---|
| 19322 | return true; | 
|---|
| 19323 |  | 
|---|
| 19324 | nla_put_failure: | 
|---|
| 19325 | return false; | 
|---|
| 19326 | } | 
|---|
| 19327 |  | 
|---|
| 19328 | /* | 
|---|
| 19329 | * This can happen on global regulatory changes or device specific settings | 
|---|
| 19330 | * based on custom regulatory domains. | 
|---|
| 19331 | */ | 
|---|
| 19332 | void nl80211_common_reg_change_event(enum nl80211_commands cmd_id, | 
|---|
| 19333 | struct regulatory_request *request) | 
|---|
| 19334 | { | 
|---|
| 19335 | struct sk_buff *msg; | 
|---|
| 19336 | void *hdr; | 
|---|
| 19337 |  | 
|---|
| 19338 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 19339 | if (!msg) | 
|---|
| 19340 | return; | 
|---|
| 19341 |  | 
|---|
| 19342 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: cmd_id); | 
|---|
| 19343 | if (!hdr) | 
|---|
| 19344 | goto nla_put_failure; | 
|---|
| 19345 |  | 
|---|
| 19346 | if (!nl80211_reg_change_event_fill(msg, request)) | 
|---|
| 19347 | goto nla_put_failure; | 
|---|
| 19348 |  | 
|---|
| 19349 | genlmsg_end(skb: msg, hdr); | 
|---|
| 19350 |  | 
|---|
| 19351 | genlmsg_multicast_allns(family: &nl80211_fam, skb: msg, portid: 0, | 
|---|
| 19352 | group: NL80211_MCGRP_REGULATORY); | 
|---|
| 19353 |  | 
|---|
| 19354 | return; | 
|---|
| 19355 |  | 
|---|
| 19356 | nla_put_failure: | 
|---|
| 19357 | nlmsg_free(skb: msg); | 
|---|
| 19358 | } | 
|---|
| 19359 |  | 
|---|
| 19360 | struct nl80211_mlme_event { | 
|---|
| 19361 | enum nl80211_commands cmd; | 
|---|
| 19362 | const u8 *buf; | 
|---|
| 19363 | size_t buf_len; | 
|---|
| 19364 | int uapsd_queues; | 
|---|
| 19365 | const u8 *req_ies; | 
|---|
| 19366 | size_t req_ies_len; | 
|---|
| 19367 | bool reconnect; | 
|---|
| 19368 | }; | 
|---|
| 19369 |  | 
|---|
| 19370 | static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, | 
|---|
| 19371 | struct net_device *netdev, | 
|---|
| 19372 | const struct nl80211_mlme_event *event, | 
|---|
| 19373 | gfp_t gfp) | 
|---|
| 19374 | { | 
|---|
| 19375 | struct sk_buff *msg; | 
|---|
| 19376 | void *hdr; | 
|---|
| 19377 |  | 
|---|
| 19378 | msg = nlmsg_new(payload: 100 + event->buf_len + event->req_ies_len, flags: gfp); | 
|---|
| 19379 | if (!msg) | 
|---|
| 19380 | return; | 
|---|
| 19381 |  | 
|---|
| 19382 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: event->cmd); | 
|---|
| 19383 | if (!hdr) { | 
|---|
| 19384 | nlmsg_free(skb: msg); | 
|---|
| 19385 | return; | 
|---|
| 19386 | } | 
|---|
| 19387 |  | 
|---|
| 19388 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 19389 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || | 
|---|
| 19390 | nla_put(skb: msg, NL80211_ATTR_FRAME, attrlen: event->buf_len, data: event->buf) || | 
|---|
| 19391 | (event->req_ies && | 
|---|
| 19392 | nla_put(skb: msg, attrtype: NL80211_ATTR_REQ_IE, attrlen: event->req_ies_len, | 
|---|
| 19393 | data: event->req_ies))) | 
|---|
| 19394 | goto nla_put_failure; | 
|---|
| 19395 |  | 
|---|
| 19396 | if (event->reconnect && | 
|---|
| 19397 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_RECONNECT_REQUESTED)) | 
|---|
| 19398 | goto nla_put_failure; | 
|---|
| 19399 |  | 
|---|
| 19400 | if (event->uapsd_queues >= 0) { | 
|---|
| 19401 | struct nlattr *nla_wmm = | 
|---|
| 19402 | nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_STA_WME); | 
|---|
| 19403 | if (!nla_wmm) | 
|---|
| 19404 | goto nla_put_failure; | 
|---|
| 19405 |  | 
|---|
| 19406 | if (nla_put_u8(skb: msg, attrtype: NL80211_STA_WME_UAPSD_QUEUES, | 
|---|
| 19407 | value: event->uapsd_queues)) | 
|---|
| 19408 | goto nla_put_failure; | 
|---|
| 19409 |  | 
|---|
| 19410 | nla_nest_end(skb: msg, start: nla_wmm); | 
|---|
| 19411 | } | 
|---|
| 19412 |  | 
|---|
| 19413 | genlmsg_end(skb: msg, hdr); | 
|---|
| 19414 |  | 
|---|
| 19415 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 19416 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 19417 | return; | 
|---|
| 19418 |  | 
|---|
| 19419 | nla_put_failure: | 
|---|
| 19420 | nlmsg_free(skb: msg); | 
|---|
| 19421 | } | 
|---|
| 19422 |  | 
|---|
| 19423 | void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, | 
|---|
| 19424 | struct net_device *netdev, const u8 *buf, | 
|---|
| 19425 | size_t len, gfp_t gfp) | 
|---|
| 19426 | { | 
|---|
| 19427 | struct nl80211_mlme_event event = { | 
|---|
| 19428 | .cmd = NL80211_CMD_AUTHENTICATE, | 
|---|
| 19429 | .buf = buf, | 
|---|
| 19430 | .buf_len = len, | 
|---|
| 19431 | .uapsd_queues = -1, | 
|---|
| 19432 | }; | 
|---|
| 19433 |  | 
|---|
| 19434 | nl80211_send_mlme_event(rdev, netdev, event: &event, gfp); | 
|---|
| 19435 | } | 
|---|
| 19436 |  | 
|---|
| 19437 | void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, | 
|---|
| 19438 | struct net_device *netdev, | 
|---|
| 19439 | const struct cfg80211_rx_assoc_resp_data *data) | 
|---|
| 19440 | { | 
|---|
| 19441 | struct nl80211_mlme_event event = { | 
|---|
| 19442 | .cmd = NL80211_CMD_ASSOCIATE, | 
|---|
| 19443 | .buf = data->buf, | 
|---|
| 19444 | .buf_len = data->len, | 
|---|
| 19445 | .uapsd_queues = data->uapsd_queues, | 
|---|
| 19446 | .req_ies = data->req_ies, | 
|---|
| 19447 | .req_ies_len = data->req_ies_len, | 
|---|
| 19448 | }; | 
|---|
| 19449 |  | 
|---|
| 19450 | nl80211_send_mlme_event(rdev, netdev, event: &event, GFP_KERNEL); | 
|---|
| 19451 | } | 
|---|
| 19452 |  | 
|---|
| 19453 | void nl80211_send_deauth(struct cfg80211_registered_device *rdev, | 
|---|
| 19454 | struct net_device *netdev, const u8 *buf, | 
|---|
| 19455 | size_t len, bool reconnect, gfp_t gfp) | 
|---|
| 19456 | { | 
|---|
| 19457 | struct nl80211_mlme_event event = { | 
|---|
| 19458 | .cmd = NL80211_CMD_DEAUTHENTICATE, | 
|---|
| 19459 | .buf = buf, | 
|---|
| 19460 | .buf_len = len, | 
|---|
| 19461 | .reconnect = reconnect, | 
|---|
| 19462 | .uapsd_queues = -1, | 
|---|
| 19463 | }; | 
|---|
| 19464 |  | 
|---|
| 19465 | nl80211_send_mlme_event(rdev, netdev, event: &event, gfp); | 
|---|
| 19466 | } | 
|---|
| 19467 |  | 
|---|
| 19468 | void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, | 
|---|
| 19469 | struct net_device *netdev, const u8 *buf, | 
|---|
| 19470 | size_t len, bool reconnect, gfp_t gfp) | 
|---|
| 19471 | { | 
|---|
| 19472 | struct nl80211_mlme_event event = { | 
|---|
| 19473 | .cmd = NL80211_CMD_DISASSOCIATE, | 
|---|
| 19474 | .buf = buf, | 
|---|
| 19475 | .buf_len = len, | 
|---|
| 19476 | .reconnect = reconnect, | 
|---|
| 19477 | .uapsd_queues = -1, | 
|---|
| 19478 | }; | 
|---|
| 19479 |  | 
|---|
| 19480 | nl80211_send_mlme_event(rdev, netdev, event: &event, gfp); | 
|---|
| 19481 | } | 
|---|
| 19482 |  | 
|---|
| 19483 | void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf, | 
|---|
| 19484 | size_t len) | 
|---|
| 19485 | { | 
|---|
| 19486 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 19487 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 19488 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 19489 | const struct ieee80211_mgmt *mgmt = (void *)buf; | 
|---|
| 19490 | struct nl80211_mlme_event event = { | 
|---|
| 19491 | .buf = buf, | 
|---|
| 19492 | .buf_len = len, | 
|---|
| 19493 | .uapsd_queues = -1, | 
|---|
| 19494 | }; | 
|---|
| 19495 |  | 
|---|
| 19496 | if (WARN_ON(len < 2)) | 
|---|
| 19497 | return; | 
|---|
| 19498 |  | 
|---|
| 19499 | if (ieee80211_is_deauth(fc: mgmt->frame_control)) { | 
|---|
| 19500 | event.cmd = NL80211_CMD_UNPROT_DEAUTHENTICATE; | 
|---|
| 19501 | } else if (ieee80211_is_disassoc(fc: mgmt->frame_control)) { | 
|---|
| 19502 | event.cmd = NL80211_CMD_UNPROT_DISASSOCIATE; | 
|---|
| 19503 | } else if (ieee80211_is_beacon(fc: mgmt->frame_control)) { | 
|---|
| 19504 | if (wdev->unprot_beacon_reported && | 
|---|
| 19505 | elapsed_jiffies_msecs(start: wdev->unprot_beacon_reported) < 10000) | 
|---|
| 19506 | return; | 
|---|
| 19507 | event.cmd = NL80211_CMD_UNPROT_BEACON; | 
|---|
| 19508 | wdev->unprot_beacon_reported = jiffies; | 
|---|
| 19509 | } else { | 
|---|
| 19510 | return; | 
|---|
| 19511 | } | 
|---|
| 19512 |  | 
|---|
| 19513 | trace_cfg80211_rx_unprot_mlme_mgmt(netdev: dev, buf, len); | 
|---|
| 19514 | nl80211_send_mlme_event(rdev, netdev: dev, event: &event, GFP_ATOMIC); | 
|---|
| 19515 | } | 
|---|
| 19516 | EXPORT_SYMBOL(cfg80211_rx_unprot_mlme_mgmt); | 
|---|
| 19517 |  | 
|---|
| 19518 | static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev, | 
|---|
| 19519 | struct net_device *netdev, int cmd, | 
|---|
| 19520 | const u8 *addr, gfp_t gfp) | 
|---|
| 19521 | { | 
|---|
| 19522 | struct sk_buff *msg; | 
|---|
| 19523 | void *hdr; | 
|---|
| 19524 |  | 
|---|
| 19525 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 19526 | if (!msg) | 
|---|
| 19527 | return; | 
|---|
| 19528 |  | 
|---|
| 19529 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd); | 
|---|
| 19530 | if (!hdr) { | 
|---|
| 19531 | nlmsg_free(skb: msg); | 
|---|
| 19532 | return; | 
|---|
| 19533 | } | 
|---|
| 19534 |  | 
|---|
| 19535 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 19536 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || | 
|---|
| 19537 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_TIMED_OUT) || | 
|---|
| 19538 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: addr)) | 
|---|
| 19539 | goto nla_put_failure; | 
|---|
| 19540 |  | 
|---|
| 19541 | genlmsg_end(skb: msg, hdr); | 
|---|
| 19542 |  | 
|---|
| 19543 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 19544 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 19545 | return; | 
|---|
| 19546 |  | 
|---|
| 19547 | nla_put_failure: | 
|---|
| 19548 | nlmsg_free(skb: msg); | 
|---|
| 19549 | } | 
|---|
| 19550 |  | 
|---|
| 19551 | void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, | 
|---|
| 19552 | struct net_device *netdev, const u8 *addr, | 
|---|
| 19553 | gfp_t gfp) | 
|---|
| 19554 | { | 
|---|
| 19555 | nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE, | 
|---|
| 19556 | addr, gfp); | 
|---|
| 19557 | } | 
|---|
| 19558 |  | 
|---|
| 19559 | void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, | 
|---|
| 19560 | struct net_device *netdev, const u8 *addr, | 
|---|
| 19561 | gfp_t gfp) | 
|---|
| 19562 | { | 
|---|
| 19563 | nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE, | 
|---|
| 19564 | addr, gfp); | 
|---|
| 19565 | } | 
|---|
| 19566 |  | 
|---|
| 19567 | void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, | 
|---|
| 19568 | struct net_device *netdev, | 
|---|
| 19569 | struct cfg80211_connect_resp_params *cr, | 
|---|
| 19570 | gfp_t gfp) | 
|---|
| 19571 | { | 
|---|
| 19572 | struct sk_buff *msg; | 
|---|
| 19573 | void *hdr; | 
|---|
| 19574 | unsigned int link; | 
|---|
| 19575 | size_t link_info_size = 0; | 
|---|
| 19576 | const u8 *connected_addr = cr->valid_links ? | 
|---|
| 19577 | cr->ap_mld_addr : cr->links[0].bssid; | 
|---|
| 19578 |  | 
|---|
| 19579 | if (cr->valid_links) { | 
|---|
| 19580 | for_each_valid_link(cr, link) { | 
|---|
| 19581 | /* Nested attribute header */ | 
|---|
| 19582 | link_info_size += NLA_HDRLEN; | 
|---|
| 19583 | /* Link ID */ | 
|---|
| 19584 | link_info_size += nla_total_size(payload: sizeof(u8)); | 
|---|
| 19585 | link_info_size += cr->links[link].addr ? | 
|---|
| 19586 | nla_total_size(ETH_ALEN) : 0; | 
|---|
| 19587 | link_info_size += (cr->links[link].bssid || | 
|---|
| 19588 | cr->links[link].bss) ? | 
|---|
| 19589 | nla_total_size(ETH_ALEN) : 0; | 
|---|
| 19590 | link_info_size += nla_total_size(payload: sizeof(u16)); | 
|---|
| 19591 | } | 
|---|
| 19592 | } | 
|---|
| 19593 |  | 
|---|
| 19594 | msg = nlmsg_new(payload: 100 + cr->req_ie_len + cr->resp_ie_len + | 
|---|
| 19595 | cr->fils.kek_len + cr->fils.pmk_len + | 
|---|
| 19596 | (cr->fils.pmkid ? WLAN_PMKID_LEN : 0) + link_info_size, | 
|---|
| 19597 | flags: gfp); | 
|---|
| 19598 | if (!msg) | 
|---|
| 19599 | return; | 
|---|
| 19600 |  | 
|---|
| 19601 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, NL80211_CMD_CONNECT); | 
|---|
| 19602 | if (!hdr) { | 
|---|
| 19603 | nlmsg_free(skb: msg); | 
|---|
| 19604 | return; | 
|---|
| 19605 | } | 
|---|
| 19606 |  | 
|---|
| 19607 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 19608 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || | 
|---|
| 19609 | (connected_addr && | 
|---|
| 19610 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: connected_addr)) || | 
|---|
| 19611 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_STATUS_CODE, | 
|---|
| 19612 | value: cr->status < 0 ? WLAN_STATUS_UNSPECIFIED_FAILURE : | 
|---|
| 19613 | cr->status) || | 
|---|
| 19614 | (cr->status < 0 && | 
|---|
| 19615 | (nla_put_flag(skb: msg, attrtype: NL80211_ATTR_TIMED_OUT) || | 
|---|
| 19616 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_TIMEOUT_REASON, | 
|---|
| 19617 | value: cr->timeout_reason))) || | 
|---|
| 19618 | (cr->req_ie && | 
|---|
| 19619 | nla_put(skb: msg, attrtype: NL80211_ATTR_REQ_IE, attrlen: cr->req_ie_len, data: cr->req_ie)) || | 
|---|
| 19620 | (cr->resp_ie && | 
|---|
| 19621 | nla_put(skb: msg, attrtype: NL80211_ATTR_RESP_IE, attrlen: cr->resp_ie_len, | 
|---|
| 19622 | data: cr->resp_ie)) || | 
|---|
| 19623 | (cr->fils.update_erp_next_seq_num && | 
|---|
| 19624 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, | 
|---|
| 19625 | value: cr->fils.erp_next_seq_num)) || | 
|---|
| 19626 | (cr->status == WLAN_STATUS_SUCCESS && | 
|---|
| 19627 | ((cr->fils.kek && | 
|---|
| 19628 | nla_put(skb: msg, attrtype: NL80211_ATTR_FILS_KEK, attrlen: cr->fils.kek_len, | 
|---|
| 19629 | data: cr->fils.kek)) || | 
|---|
| 19630 | (cr->fils.pmk && | 
|---|
| 19631 | nla_put(skb: msg, attrtype: NL80211_ATTR_PMK, attrlen: cr->fils.pmk_len, data: cr->fils.pmk)) || | 
|---|
| 19632 | (cr->fils.pmkid && | 
|---|
| 19633 | nla_put(skb: msg, attrtype: NL80211_ATTR_PMKID, WLAN_PMKID_LEN, data: cr->fils.pmkid))))) | 
|---|
| 19634 | goto nla_put_failure; | 
|---|
| 19635 |  | 
|---|
| 19636 | if (cr->valid_links) { | 
|---|
| 19637 | int i = 1; | 
|---|
| 19638 | struct nlattr *nested; | 
|---|
| 19639 |  | 
|---|
| 19640 | nested = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_MLO_LINKS); | 
|---|
| 19641 | if (!nested) | 
|---|
| 19642 | goto nla_put_failure; | 
|---|
| 19643 |  | 
|---|
| 19644 | for_each_valid_link(cr, link) { | 
|---|
| 19645 | struct nlattr *nested_mlo_links; | 
|---|
| 19646 | const u8 *bssid = cr->links[link].bss ? | 
|---|
| 19647 | cr->links[link].bss->bssid : | 
|---|
| 19648 | cr->links[link].bssid; | 
|---|
| 19649 |  | 
|---|
| 19650 | nested_mlo_links = nla_nest_start(skb: msg, attrtype: i); | 
|---|
| 19651 | if (!nested_mlo_links) | 
|---|
| 19652 | goto nla_put_failure; | 
|---|
| 19653 |  | 
|---|
| 19654 | if (nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: link) || | 
|---|
| 19655 | (bssid && | 
|---|
| 19656 | nla_put(skb: msg, attrtype: NL80211_ATTR_BSSID, ETH_ALEN, data: bssid)) || | 
|---|
| 19657 | (cr->links[link].addr && | 
|---|
| 19658 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, | 
|---|
| 19659 | data: cr->links[link].addr)) || | 
|---|
| 19660 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_STATUS_CODE, | 
|---|
| 19661 | value: cr->links[link].status)) | 
|---|
| 19662 | goto nla_put_failure; | 
|---|
| 19663 |  | 
|---|
| 19664 | nla_nest_end(skb: msg, start: nested_mlo_links); | 
|---|
| 19665 | i++; | 
|---|
| 19666 | } | 
|---|
| 19667 | nla_nest_end(skb: msg, start: nested); | 
|---|
| 19668 | } | 
|---|
| 19669 |  | 
|---|
| 19670 | genlmsg_end(skb: msg, hdr); | 
|---|
| 19671 |  | 
|---|
| 19672 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 19673 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 19674 | return; | 
|---|
| 19675 |  | 
|---|
| 19676 | nla_put_failure: | 
|---|
| 19677 | nlmsg_free(skb: msg); | 
|---|
| 19678 | } | 
|---|
| 19679 |  | 
|---|
| 19680 | void nl80211_send_roamed(struct cfg80211_registered_device *rdev, | 
|---|
| 19681 | struct net_device *netdev, | 
|---|
| 19682 | struct cfg80211_roam_info *info, gfp_t gfp) | 
|---|
| 19683 | { | 
|---|
| 19684 | struct sk_buff *msg; | 
|---|
| 19685 | void *hdr; | 
|---|
| 19686 | size_t link_info_size = 0; | 
|---|
| 19687 | unsigned int link; | 
|---|
| 19688 | const u8 *connected_addr = info->ap_mld_addr ? | 
|---|
| 19689 | info->ap_mld_addr : | 
|---|
| 19690 | (info->links[0].bss ? | 
|---|
| 19691 | info->links[0].bss->bssid : | 
|---|
| 19692 | info->links[0].bssid); | 
|---|
| 19693 |  | 
|---|
| 19694 | if (info->valid_links) { | 
|---|
| 19695 | for_each_valid_link(info, link) { | 
|---|
| 19696 | /* Nested attribute header */ | 
|---|
| 19697 | link_info_size += NLA_HDRLEN; | 
|---|
| 19698 | /* Link ID */ | 
|---|
| 19699 | link_info_size += nla_total_size(payload: sizeof(u8)); | 
|---|
| 19700 | link_info_size += info->links[link].addr ? | 
|---|
| 19701 | nla_total_size(ETH_ALEN) : 0; | 
|---|
| 19702 | link_info_size += (info->links[link].bssid || | 
|---|
| 19703 | info->links[link].bss) ? | 
|---|
| 19704 | nla_total_size(ETH_ALEN) : 0; | 
|---|
| 19705 | } | 
|---|
| 19706 | } | 
|---|
| 19707 |  | 
|---|
| 19708 | msg = nlmsg_new(payload: 100 + info->req_ie_len + info->resp_ie_len + | 
|---|
| 19709 | info->fils.kek_len + info->fils.pmk_len + | 
|---|
| 19710 | (info->fils.pmkid ? WLAN_PMKID_LEN : 0) + | 
|---|
| 19711 | link_info_size, flags: gfp); | 
|---|
| 19712 | if (!msg) | 
|---|
| 19713 | return; | 
|---|
| 19714 |  | 
|---|
| 19715 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_ROAM); | 
|---|
| 19716 | if (!hdr) { | 
|---|
| 19717 | nlmsg_free(skb: msg); | 
|---|
| 19718 | return; | 
|---|
| 19719 | } | 
|---|
| 19720 |  | 
|---|
| 19721 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 19722 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || | 
|---|
| 19723 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: connected_addr) || | 
|---|
| 19724 | (info->req_ie && | 
|---|
| 19725 | nla_put(skb: msg, attrtype: NL80211_ATTR_REQ_IE, attrlen: info->req_ie_len, | 
|---|
| 19726 | data: info->req_ie)) || | 
|---|
| 19727 | (info->resp_ie && | 
|---|
| 19728 | nla_put(skb: msg, attrtype: NL80211_ATTR_RESP_IE, attrlen: info->resp_ie_len, | 
|---|
| 19729 | data: info->resp_ie)) || | 
|---|
| 19730 | (info->fils.update_erp_next_seq_num && | 
|---|
| 19731 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, | 
|---|
| 19732 | value: info->fils.erp_next_seq_num)) || | 
|---|
| 19733 | (info->fils.kek && | 
|---|
| 19734 | nla_put(skb: msg, attrtype: NL80211_ATTR_FILS_KEK, attrlen: info->fils.kek_len, | 
|---|
| 19735 | data: info->fils.kek)) || | 
|---|
| 19736 | (info->fils.pmk && | 
|---|
| 19737 | nla_put(skb: msg, attrtype: NL80211_ATTR_PMK, attrlen: info->fils.pmk_len, data: info->fils.pmk)) || | 
|---|
| 19738 | (info->fils.pmkid && | 
|---|
| 19739 | nla_put(skb: msg, attrtype: NL80211_ATTR_PMKID, WLAN_PMKID_LEN, data: info->fils.pmkid))) | 
|---|
| 19740 | goto nla_put_failure; | 
|---|
| 19741 |  | 
|---|
| 19742 | if (info->valid_links) { | 
|---|
| 19743 | int i = 1; | 
|---|
| 19744 | struct nlattr *nested; | 
|---|
| 19745 |  | 
|---|
| 19746 | nested = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_MLO_LINKS); | 
|---|
| 19747 | if (!nested) | 
|---|
| 19748 | goto nla_put_failure; | 
|---|
| 19749 |  | 
|---|
| 19750 | for_each_valid_link(info, link) { | 
|---|
| 19751 | struct nlattr *nested_mlo_links; | 
|---|
| 19752 | const u8 *bssid = info->links[link].bss ? | 
|---|
| 19753 | info->links[link].bss->bssid : | 
|---|
| 19754 | info->links[link].bssid; | 
|---|
| 19755 |  | 
|---|
| 19756 | nested_mlo_links = nla_nest_start(skb: msg, attrtype: i); | 
|---|
| 19757 | if (!nested_mlo_links) | 
|---|
| 19758 | goto nla_put_failure; | 
|---|
| 19759 |  | 
|---|
| 19760 | if (nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: link) || | 
|---|
| 19761 | (bssid && | 
|---|
| 19762 | nla_put(skb: msg, attrtype: NL80211_ATTR_BSSID, ETH_ALEN, data: bssid)) || | 
|---|
| 19763 | (info->links[link].addr && | 
|---|
| 19764 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, | 
|---|
| 19765 | data: info->links[link].addr))) | 
|---|
| 19766 | goto nla_put_failure; | 
|---|
| 19767 |  | 
|---|
| 19768 | nla_nest_end(skb: msg, start: nested_mlo_links); | 
|---|
| 19769 | i++; | 
|---|
| 19770 | } | 
|---|
| 19771 | nla_nest_end(skb: msg, start: nested); | 
|---|
| 19772 | } | 
|---|
| 19773 |  | 
|---|
| 19774 | genlmsg_end(skb: msg, hdr); | 
|---|
| 19775 |  | 
|---|
| 19776 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 19777 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 19778 | return; | 
|---|
| 19779 |  | 
|---|
| 19780 | nla_put_failure: | 
|---|
| 19781 | nlmsg_free(skb: msg); | 
|---|
| 19782 | } | 
|---|
| 19783 |  | 
|---|
| 19784 | void nl80211_send_port_authorized(struct cfg80211_registered_device *rdev, | 
|---|
| 19785 | struct net_device *netdev, const u8 *peer_addr, | 
|---|
| 19786 | const u8 *td_bitmap, u8 td_bitmap_len) | 
|---|
| 19787 | { | 
|---|
| 19788 | struct sk_buff *msg; | 
|---|
| 19789 | void *hdr; | 
|---|
| 19790 |  | 
|---|
| 19791 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 19792 | if (!msg) | 
|---|
| 19793 | return; | 
|---|
| 19794 |  | 
|---|
| 19795 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_PORT_AUTHORIZED); | 
|---|
| 19796 | if (!hdr) { | 
|---|
| 19797 | nlmsg_free(skb: msg); | 
|---|
| 19798 | return; | 
|---|
| 19799 | } | 
|---|
| 19800 |  | 
|---|
| 19801 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 19802 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || | 
|---|
| 19803 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: peer_addr)) | 
|---|
| 19804 | goto nla_put_failure; | 
|---|
| 19805 |  | 
|---|
| 19806 | if (td_bitmap_len > 0 && td_bitmap && | 
|---|
| 19807 | nla_put(skb: msg, attrtype: NL80211_ATTR_TD_BITMAP, attrlen: td_bitmap_len, data: td_bitmap)) | 
|---|
| 19808 | goto nla_put_failure; | 
|---|
| 19809 |  | 
|---|
| 19810 | genlmsg_end(skb: msg, hdr); | 
|---|
| 19811 |  | 
|---|
| 19812 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 19813 | group: NL80211_MCGRP_MLME, GFP_KERNEL); | 
|---|
| 19814 | return; | 
|---|
| 19815 |  | 
|---|
| 19816 | nla_put_failure: | 
|---|
| 19817 | nlmsg_free(skb: msg); | 
|---|
| 19818 | } | 
|---|
| 19819 |  | 
|---|
| 19820 | void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, | 
|---|
| 19821 | struct net_device *netdev, u16 reason, | 
|---|
| 19822 | const u8 *ie, size_t ie_len, bool from_ap) | 
|---|
| 19823 | { | 
|---|
| 19824 | struct sk_buff *msg; | 
|---|
| 19825 | void *hdr; | 
|---|
| 19826 |  | 
|---|
| 19827 | msg = nlmsg_new(payload: 100 + ie_len, GFP_KERNEL); | 
|---|
| 19828 | if (!msg) | 
|---|
| 19829 | return; | 
|---|
| 19830 |  | 
|---|
| 19831 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_DISCONNECT); | 
|---|
| 19832 | if (!hdr) { | 
|---|
| 19833 | nlmsg_free(skb: msg); | 
|---|
| 19834 | return; | 
|---|
| 19835 | } | 
|---|
| 19836 |  | 
|---|
| 19837 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 19838 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || | 
|---|
| 19839 | (reason && | 
|---|
| 19840 | nla_put_u16(skb: msg, NL80211_ATTR_REASON_CODE, value: reason)) || | 
|---|
| 19841 | (from_ap && | 
|---|
| 19842 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_DISCONNECTED_BY_AP)) || | 
|---|
| 19843 | (ie && nla_put(skb: msg, NL80211_ATTR_IE, attrlen: ie_len, data: ie))) | 
|---|
| 19844 | goto nla_put_failure; | 
|---|
| 19845 |  | 
|---|
| 19846 | genlmsg_end(skb: msg, hdr); | 
|---|
| 19847 |  | 
|---|
| 19848 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 19849 | group: NL80211_MCGRP_MLME, GFP_KERNEL); | 
|---|
| 19850 | return; | 
|---|
| 19851 |  | 
|---|
| 19852 | nla_put_failure: | 
|---|
| 19853 | nlmsg_free(skb: msg); | 
|---|
| 19854 | } | 
|---|
| 19855 |  | 
|---|
| 19856 | void cfg80211_links_removed(struct net_device *dev, u16 link_mask) | 
|---|
| 19857 | { | 
|---|
| 19858 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 19859 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 19860 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 19861 | struct sk_buff *msg; | 
|---|
| 19862 | struct nlattr *links; | 
|---|
| 19863 | void *hdr; | 
|---|
| 19864 |  | 
|---|
| 19865 | lockdep_assert_wiphy(wdev->wiphy); | 
|---|
| 19866 | trace_cfg80211_links_removed(netdev: dev, link_mask); | 
|---|
| 19867 |  | 
|---|
| 19868 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && | 
|---|
| 19869 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) | 
|---|
| 19870 | return; | 
|---|
| 19871 |  | 
|---|
| 19872 | if (WARN_ON(!wdev->valid_links || !link_mask || | 
|---|
| 19873 | (wdev->valid_links & link_mask) != link_mask || | 
|---|
| 19874 | wdev->valid_links == link_mask)) | 
|---|
| 19875 | return; | 
|---|
| 19876 |  | 
|---|
| 19877 | cfg80211_wdev_release_link_bsses(wdev, link_mask); | 
|---|
| 19878 | wdev->valid_links &= ~link_mask; | 
|---|
| 19879 |  | 
|---|
| 19880 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 19881 | if (!msg) | 
|---|
| 19882 | return; | 
|---|
| 19883 |  | 
|---|
| 19884 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_LINKS_REMOVED); | 
|---|
| 19885 | if (!hdr) { | 
|---|
| 19886 | nlmsg_free(skb: msg); | 
|---|
| 19887 | return; | 
|---|
| 19888 | } | 
|---|
| 19889 |  | 
|---|
| 19890 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 19891 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex)) | 
|---|
| 19892 | goto nla_put_failure; | 
|---|
| 19893 |  | 
|---|
| 19894 | links = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_MLO_LINKS); | 
|---|
| 19895 | if (!links) | 
|---|
| 19896 | goto nla_put_failure; | 
|---|
| 19897 |  | 
|---|
| 19898 | while (link_mask) { | 
|---|
| 19899 | struct nlattr *link; | 
|---|
| 19900 | int link_id = __ffs(link_mask); | 
|---|
| 19901 |  | 
|---|
| 19902 | link = nla_nest_start(skb: msg, attrtype: link_id + 1); | 
|---|
| 19903 | if (!link) | 
|---|
| 19904 | goto nla_put_failure; | 
|---|
| 19905 |  | 
|---|
| 19906 | if (nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: link_id)) | 
|---|
| 19907 | goto nla_put_failure; | 
|---|
| 19908 |  | 
|---|
| 19909 | nla_nest_end(skb: msg, start: link); | 
|---|
| 19910 | link_mask &= ~(1 << link_id); | 
|---|
| 19911 | } | 
|---|
| 19912 |  | 
|---|
| 19913 | nla_nest_end(skb: msg, start: links); | 
|---|
| 19914 |  | 
|---|
| 19915 | genlmsg_end(skb: msg, hdr); | 
|---|
| 19916 |  | 
|---|
| 19917 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 19918 | group: NL80211_MCGRP_MLME, GFP_KERNEL); | 
|---|
| 19919 | return; | 
|---|
| 19920 |  | 
|---|
| 19921 | nla_put_failure: | 
|---|
| 19922 | nlmsg_free(skb: msg); | 
|---|
| 19923 | } | 
|---|
| 19924 | EXPORT_SYMBOL(cfg80211_links_removed); | 
|---|
| 19925 |  | 
|---|
| 19926 | void nl80211_mlo_reconf_add_done(struct net_device *dev, | 
|---|
| 19927 | struct cfg80211_mlo_reconf_done_data *data) | 
|---|
| 19928 | { | 
|---|
| 19929 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 19930 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 19931 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 19932 | struct nl80211_mlme_event event = { | 
|---|
| 19933 | .cmd = NL80211_CMD_ASSOC_MLO_RECONF, | 
|---|
| 19934 | .buf = data->buf, | 
|---|
| 19935 | .buf_len = data->len, | 
|---|
| 19936 | .uapsd_queues = -1, | 
|---|
| 19937 | }; | 
|---|
| 19938 |  | 
|---|
| 19939 | nl80211_send_mlme_event(rdev, netdev: dev, event: &event, GFP_KERNEL); | 
|---|
| 19940 | } | 
|---|
| 19941 | EXPORT_SYMBOL(nl80211_mlo_reconf_add_done); | 
|---|
| 19942 |  | 
|---|
| 19943 | void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, | 
|---|
| 19944 | struct net_device *netdev, const u8 *bssid, | 
|---|
| 19945 | gfp_t gfp) | 
|---|
| 19946 | { | 
|---|
| 19947 | struct sk_buff *msg; | 
|---|
| 19948 | void *hdr; | 
|---|
| 19949 |  | 
|---|
| 19950 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 19951 | if (!msg) | 
|---|
| 19952 | return; | 
|---|
| 19953 |  | 
|---|
| 19954 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_JOIN_IBSS); | 
|---|
| 19955 | if (!hdr) { | 
|---|
| 19956 | nlmsg_free(skb: msg); | 
|---|
| 19957 | return; | 
|---|
| 19958 | } | 
|---|
| 19959 |  | 
|---|
| 19960 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 19961 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || | 
|---|
| 19962 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: bssid)) | 
|---|
| 19963 | goto nla_put_failure; | 
|---|
| 19964 |  | 
|---|
| 19965 | genlmsg_end(skb: msg, hdr); | 
|---|
| 19966 |  | 
|---|
| 19967 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 19968 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 19969 | return; | 
|---|
| 19970 |  | 
|---|
| 19971 | nla_put_failure: | 
|---|
| 19972 | nlmsg_free(skb: msg); | 
|---|
| 19973 | } | 
|---|
| 19974 |  | 
|---|
| 19975 | void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr, | 
|---|
| 19976 | const u8 *ie, u8 ie_len, | 
|---|
| 19977 | int sig_dbm, gfp_t gfp) | 
|---|
| 19978 | { | 
|---|
| 19979 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 19980 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); | 
|---|
| 19981 | struct sk_buff *msg; | 
|---|
| 19982 | void *hdr; | 
|---|
| 19983 |  | 
|---|
| 19984 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT)) | 
|---|
| 19985 | return; | 
|---|
| 19986 |  | 
|---|
| 19987 | trace_cfg80211_notify_new_peer_candidate(netdev: dev, macaddr: addr); | 
|---|
| 19988 |  | 
|---|
| 19989 | msg = nlmsg_new(payload: 100 + ie_len, flags: gfp); | 
|---|
| 19990 | if (!msg) | 
|---|
| 19991 | return; | 
|---|
| 19992 |  | 
|---|
| 19993 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_NEW_PEER_CANDIDATE); | 
|---|
| 19994 | if (!hdr) { | 
|---|
| 19995 | nlmsg_free(skb: msg); | 
|---|
| 19996 | return; | 
|---|
| 19997 | } | 
|---|
| 19998 |  | 
|---|
| 19999 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 20000 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || | 
|---|
| 20001 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: addr) || | 
|---|
| 20002 | (ie_len && ie && | 
|---|
| 20003 | nla_put(skb: msg, NL80211_ATTR_IE, attrlen: ie_len, data: ie)) || | 
|---|
| 20004 | (sig_dbm && | 
|---|
| 20005 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_RX_SIGNAL_DBM, value: sig_dbm))) | 
|---|
| 20006 | goto nla_put_failure; | 
|---|
| 20007 |  | 
|---|
| 20008 | genlmsg_end(skb: msg, hdr); | 
|---|
| 20009 |  | 
|---|
| 20010 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 20011 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 20012 | return; | 
|---|
| 20013 |  | 
|---|
| 20014 | nla_put_failure: | 
|---|
| 20015 | nlmsg_free(skb: msg); | 
|---|
| 20016 | } | 
|---|
| 20017 | EXPORT_SYMBOL(cfg80211_notify_new_peer_candidate); | 
|---|
| 20018 |  | 
|---|
| 20019 | void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, | 
|---|
| 20020 | struct net_device *netdev, const u8 *addr, | 
|---|
| 20021 | enum nl80211_key_type key_type, int key_id, | 
|---|
| 20022 | const u8 *tsc, gfp_t gfp) | 
|---|
| 20023 | { | 
|---|
| 20024 | struct sk_buff *msg; | 
|---|
| 20025 | void *hdr; | 
|---|
| 20026 |  | 
|---|
| 20027 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 20028 | if (!msg) | 
|---|
| 20029 | return; | 
|---|
| 20030 |  | 
|---|
| 20031 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_MICHAEL_MIC_FAILURE); | 
|---|
| 20032 | if (!hdr) { | 
|---|
| 20033 | nlmsg_free(skb: msg); | 
|---|
| 20034 | return; | 
|---|
| 20035 | } | 
|---|
| 20036 |  | 
|---|
| 20037 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 20038 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || | 
|---|
| 20039 | (addr && nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: addr)) || | 
|---|
| 20040 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_KEY_TYPE, value: key_type) || | 
|---|
| 20041 | (key_id != -1 && | 
|---|
| 20042 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_KEY_IDX, value: key_id)) || | 
|---|
| 20043 | (tsc && nla_put(skb: msg, attrtype: NL80211_ATTR_KEY_SEQ, attrlen: 6, data: tsc))) | 
|---|
| 20044 | goto nla_put_failure; | 
|---|
| 20045 |  | 
|---|
| 20046 | genlmsg_end(skb: msg, hdr); | 
|---|
| 20047 |  | 
|---|
| 20048 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 20049 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 20050 | return; | 
|---|
| 20051 |  | 
|---|
| 20052 | nla_put_failure: | 
|---|
| 20053 | nlmsg_free(skb: msg); | 
|---|
| 20054 | } | 
|---|
| 20055 |  | 
|---|
| 20056 | void nl80211_send_beacon_hint_event(struct wiphy *wiphy, | 
|---|
| 20057 | struct ieee80211_channel *channel_before, | 
|---|
| 20058 | struct ieee80211_channel *channel_after) | 
|---|
| 20059 | { | 
|---|
| 20060 | struct sk_buff *msg; | 
|---|
| 20061 | void *hdr; | 
|---|
| 20062 | struct nlattr *nl_freq; | 
|---|
| 20063 |  | 
|---|
| 20064 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); | 
|---|
| 20065 | if (!msg) | 
|---|
| 20066 | return; | 
|---|
| 20067 |  | 
|---|
| 20068 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, NL80211_CMD_REG_BEACON_HINT); | 
|---|
| 20069 | if (!hdr) { | 
|---|
| 20070 | nlmsg_free(skb: msg); | 
|---|
| 20071 | return; | 
|---|
| 20072 | } | 
|---|
| 20073 |  | 
|---|
| 20074 | /* | 
|---|
| 20075 | * Since we are applying the beacon hint to a wiphy we know its | 
|---|
| 20076 | * wiphy_idx is valid | 
|---|
| 20077 | */ | 
|---|
| 20078 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: get_wiphy_idx(wiphy))) | 
|---|
| 20079 | goto nla_put_failure; | 
|---|
| 20080 |  | 
|---|
| 20081 | /* Before */ | 
|---|
| 20082 | nl_freq = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_FREQ_BEFORE); | 
|---|
| 20083 | if (!nl_freq) | 
|---|
| 20084 | goto nla_put_failure; | 
|---|
| 20085 |  | 
|---|
| 20086 | if (nl80211_msg_put_channel(msg, wiphy, chan: channel_before, large: false)) | 
|---|
| 20087 | goto nla_put_failure; | 
|---|
| 20088 | nla_nest_end(skb: msg, start: nl_freq); | 
|---|
| 20089 |  | 
|---|
| 20090 | /* After */ | 
|---|
| 20091 | nl_freq = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_FREQ_AFTER); | 
|---|
| 20092 | if (!nl_freq) | 
|---|
| 20093 | goto nla_put_failure; | 
|---|
| 20094 |  | 
|---|
| 20095 | if (nl80211_msg_put_channel(msg, wiphy, chan: channel_after, large: false)) | 
|---|
| 20096 | goto nla_put_failure; | 
|---|
| 20097 | nla_nest_end(skb: msg, start: nl_freq); | 
|---|
| 20098 |  | 
|---|
| 20099 | genlmsg_end(skb: msg, hdr); | 
|---|
| 20100 |  | 
|---|
| 20101 | genlmsg_multicast_allns(family: &nl80211_fam, skb: msg, portid: 0, | 
|---|
| 20102 | group: NL80211_MCGRP_REGULATORY); | 
|---|
| 20103 |  | 
|---|
| 20104 | return; | 
|---|
| 20105 |  | 
|---|
| 20106 | nla_put_failure: | 
|---|
| 20107 | nlmsg_free(skb: msg); | 
|---|
| 20108 | } | 
|---|
| 20109 |  | 
|---|
| 20110 | static void nl80211_send_remain_on_chan_event( | 
|---|
| 20111 | int cmd, struct cfg80211_registered_device *rdev, | 
|---|
| 20112 | struct wireless_dev *wdev, u64 cookie, | 
|---|
| 20113 | struct ieee80211_channel *chan, | 
|---|
| 20114 | unsigned int duration, gfp_t gfp) | 
|---|
| 20115 | { | 
|---|
| 20116 | struct sk_buff *msg; | 
|---|
| 20117 | void *hdr; | 
|---|
| 20118 |  | 
|---|
| 20119 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 20120 | if (!msg) | 
|---|
| 20121 | return; | 
|---|
| 20122 |  | 
|---|
| 20123 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd); | 
|---|
| 20124 | if (!hdr) { | 
|---|
| 20125 | nlmsg_free(skb: msg); | 
|---|
| 20126 | return; | 
|---|
| 20127 | } | 
|---|
| 20128 |  | 
|---|
| 20129 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 20130 | (wdev->netdev && nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, | 
|---|
| 20131 | value: wdev->netdev->ifindex)) || | 
|---|
| 20132 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), | 
|---|
| 20133 | padattr: NL80211_ATTR_PAD) || | 
|---|
| 20134 | nla_put_u32(skb: msg, NL80211_ATTR_WIPHY_FREQ, value: chan->center_freq) || | 
|---|
| 20135 | nla_put_u32(skb: msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, | 
|---|
| 20136 | value: NL80211_CHAN_NO_HT) || | 
|---|
| 20137 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: cookie, | 
|---|
| 20138 | padattr: NL80211_ATTR_PAD)) | 
|---|
| 20139 | goto nla_put_failure; | 
|---|
| 20140 |  | 
|---|
| 20141 | if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL && | 
|---|
| 20142 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_DURATION, value: duration)) | 
|---|
| 20143 | goto nla_put_failure; | 
|---|
| 20144 |  | 
|---|
| 20145 | genlmsg_end(skb: msg, hdr); | 
|---|
| 20146 |  | 
|---|
| 20147 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 20148 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 20149 | return; | 
|---|
| 20150 |  | 
|---|
| 20151 | nla_put_failure: | 
|---|
| 20152 | nlmsg_free(skb: msg); | 
|---|
| 20153 | } | 
|---|
| 20154 |  | 
|---|
| 20155 | void cfg80211_assoc_comeback(struct net_device *netdev, | 
|---|
| 20156 | const u8 *ap_addr, u32 timeout) | 
|---|
| 20157 | { | 
|---|
| 20158 | struct wireless_dev *wdev = netdev->ieee80211_ptr; | 
|---|
| 20159 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 20160 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 20161 | struct sk_buff *msg; | 
|---|
| 20162 | void *hdr; | 
|---|
| 20163 |  | 
|---|
| 20164 | trace_cfg80211_assoc_comeback(wdev, ap_addr, timeout); | 
|---|
| 20165 |  | 
|---|
| 20166 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 20167 | if (!msg) | 
|---|
| 20168 | return; | 
|---|
| 20169 |  | 
|---|
| 20170 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_ASSOC_COMEBACK); | 
|---|
| 20171 | if (!hdr) { | 
|---|
| 20172 | nlmsg_free(skb: msg); | 
|---|
| 20173 | return; | 
|---|
| 20174 | } | 
|---|
| 20175 |  | 
|---|
| 20176 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 20177 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || | 
|---|
| 20178 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: ap_addr) || | 
|---|
| 20179 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_TIMEOUT, value: timeout)) | 
|---|
| 20180 | goto nla_put_failure; | 
|---|
| 20181 |  | 
|---|
| 20182 | genlmsg_end(skb: msg, hdr); | 
|---|
| 20183 |  | 
|---|
| 20184 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 20185 | group: NL80211_MCGRP_MLME, GFP_KERNEL); | 
|---|
| 20186 | return; | 
|---|
| 20187 |  | 
|---|
| 20188 | nla_put_failure: | 
|---|
| 20189 | nlmsg_free(skb: msg); | 
|---|
| 20190 | } | 
|---|
| 20191 | EXPORT_SYMBOL(cfg80211_assoc_comeback); | 
|---|
| 20192 |  | 
|---|
| 20193 | void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie, | 
|---|
| 20194 | struct ieee80211_channel *chan, | 
|---|
| 20195 | unsigned int duration, gfp_t gfp) | 
|---|
| 20196 | { | 
|---|
| 20197 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 20198 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 20199 |  | 
|---|
| 20200 | trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration); | 
|---|
| 20201 | nl80211_send_remain_on_chan_event(cmd: NL80211_CMD_REMAIN_ON_CHANNEL, | 
|---|
| 20202 | rdev, wdev, cookie, chan, | 
|---|
| 20203 | duration, gfp); | 
|---|
| 20204 | } | 
|---|
| 20205 | EXPORT_SYMBOL(cfg80211_ready_on_channel); | 
|---|
| 20206 |  | 
|---|
| 20207 | void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie, | 
|---|
| 20208 | struct ieee80211_channel *chan, | 
|---|
| 20209 | gfp_t gfp) | 
|---|
| 20210 | { | 
|---|
| 20211 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 20212 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 20213 |  | 
|---|
| 20214 | trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan); | 
|---|
| 20215 | nl80211_send_remain_on_chan_event(cmd: NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, | 
|---|
| 20216 | rdev, wdev, cookie, chan, duration: 0, gfp); | 
|---|
| 20217 | } | 
|---|
| 20218 | EXPORT_SYMBOL(cfg80211_remain_on_channel_expired); | 
|---|
| 20219 |  | 
|---|
| 20220 | void cfg80211_tx_mgmt_expired(struct wireless_dev *wdev, u64 cookie, | 
|---|
| 20221 | struct ieee80211_channel *chan, | 
|---|
| 20222 | gfp_t gfp) | 
|---|
| 20223 | { | 
|---|
| 20224 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 20225 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 20226 |  | 
|---|
| 20227 | trace_cfg80211_tx_mgmt_expired(wdev, cookie, chan); | 
|---|
| 20228 | nl80211_send_remain_on_chan_event(cmd: NL80211_CMD_FRAME_WAIT_CANCEL, | 
|---|
| 20229 | rdev, wdev, cookie, chan, duration: 0, gfp); | 
|---|
| 20230 | } | 
|---|
| 20231 | EXPORT_SYMBOL(cfg80211_tx_mgmt_expired); | 
|---|
| 20232 |  | 
|---|
| 20233 | void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, | 
|---|
| 20234 | struct station_info *sinfo, gfp_t gfp) | 
|---|
| 20235 | { | 
|---|
| 20236 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | 
|---|
| 20237 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 20238 | struct sk_buff *msg; | 
|---|
| 20239 |  | 
|---|
| 20240 | trace_cfg80211_new_sta(netdev: dev, mac_addr, sinfo); | 
|---|
| 20241 |  | 
|---|
| 20242 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 20243 | if (!msg) | 
|---|
| 20244 | return; | 
|---|
| 20245 |  | 
|---|
| 20246 | if (nl80211_send_station(msg, cmd: NL80211_CMD_NEW_STATION, portid: 0, seq: 0, flags: 0, | 
|---|
| 20247 | rdev, dev, mac_addr, sinfo, link_stats: false) < 0) { | 
|---|
| 20248 | nlmsg_free(skb: msg); | 
|---|
| 20249 | return; | 
|---|
| 20250 | } | 
|---|
| 20251 |  | 
|---|
| 20252 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 20253 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 20254 | } | 
|---|
| 20255 | EXPORT_SYMBOL(cfg80211_new_sta); | 
|---|
| 20256 |  | 
|---|
| 20257 | void cfg80211_del_sta_sinfo(struct net_device *dev, const u8 *mac_addr, | 
|---|
| 20258 | struct station_info *sinfo, gfp_t gfp) | 
|---|
| 20259 | { | 
|---|
| 20260 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | 
|---|
| 20261 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 20262 | struct sk_buff *msg; | 
|---|
| 20263 | struct station_info empty_sinfo = {}; | 
|---|
| 20264 |  | 
|---|
| 20265 | if (!sinfo) | 
|---|
| 20266 | sinfo = &empty_sinfo; | 
|---|
| 20267 |  | 
|---|
| 20268 | trace_cfg80211_del_sta(netdev: dev, macaddr: mac_addr); | 
|---|
| 20269 |  | 
|---|
| 20270 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 20271 | if (!msg) { | 
|---|
| 20272 | cfg80211_sinfo_release_content(sinfo); | 
|---|
| 20273 | return; | 
|---|
| 20274 | } | 
|---|
| 20275 |  | 
|---|
| 20276 | if (nl80211_send_station(msg, cmd: NL80211_CMD_DEL_STATION, portid: 0, seq: 0, flags: 0, | 
|---|
| 20277 | rdev, dev, mac_addr, sinfo, link_stats: false) < 0) { | 
|---|
| 20278 | nlmsg_free(skb: msg); | 
|---|
| 20279 | return; | 
|---|
| 20280 | } | 
|---|
| 20281 |  | 
|---|
| 20282 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 20283 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 20284 | } | 
|---|
| 20285 | EXPORT_SYMBOL(cfg80211_del_sta_sinfo); | 
|---|
| 20286 |  | 
|---|
| 20287 | void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr, | 
|---|
| 20288 | enum nl80211_connect_failed_reason reason, | 
|---|
| 20289 | gfp_t gfp) | 
|---|
| 20290 | { | 
|---|
| 20291 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; | 
|---|
| 20292 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 20293 | struct sk_buff *msg; | 
|---|
| 20294 | void *hdr; | 
|---|
| 20295 |  | 
|---|
| 20296 | msg = nlmsg_new(NLMSG_GOODSIZE, flags: gfp); | 
|---|
| 20297 | if (!msg) | 
|---|
| 20298 | return; | 
|---|
| 20299 |  | 
|---|
| 20300 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_CONN_FAILED); | 
|---|
| 20301 | if (!hdr) { | 
|---|
| 20302 | nlmsg_free(skb: msg); | 
|---|
| 20303 | return; | 
|---|
| 20304 | } | 
|---|
| 20305 |  | 
|---|
| 20306 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || | 
|---|
| 20307 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: mac_addr) || | 
|---|
| 20308 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CONN_FAILED_REASON, value: reason)) | 
|---|
| 20309 | goto nla_put_failure; | 
|---|
| 20310 |  | 
|---|
| 20311 | genlmsg_end(skb: msg, hdr); | 
|---|
| 20312 |  | 
|---|
| 20313 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 20314 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 20315 | return; | 
|---|
| 20316 |  | 
|---|
| 20317 | nla_put_failure: | 
|---|
| 20318 | nlmsg_free(skb: msg); | 
|---|
| 20319 | } | 
|---|
| 20320 | EXPORT_SYMBOL(cfg80211_conn_failed); | 
|---|
| 20321 |  | 
|---|
| 20322 | static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd, | 
|---|
| 20323 | const u8 *addr, int link_id, gfp_t gfp) | 
|---|
| 20324 | { | 
|---|
| 20325 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 20326 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); | 
|---|
| 20327 | struct sk_buff *msg; | 
|---|
| 20328 | void *hdr; | 
|---|
| 20329 | u32 nlportid = READ_ONCE(wdev->ap_unexpected_nlportid); | 
|---|
| 20330 |  | 
|---|
| 20331 | if (!nlportid) | 
|---|
| 20332 | return false; | 
|---|
| 20333 |  | 
|---|
| 20334 | msg = nlmsg_new(payload: 100, flags: gfp); | 
|---|
| 20335 | if (!msg) | 
|---|
| 20336 | return true; | 
|---|
| 20337 |  | 
|---|
| 20338 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd); | 
|---|
| 20339 | if (!hdr) { | 
|---|
| 20340 | nlmsg_free(skb: msg); | 
|---|
| 20341 | return true; | 
|---|
| 20342 | } | 
|---|
| 20343 |  | 
|---|
| 20344 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 20345 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || | 
|---|
| 20346 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: addr) || | 
|---|
| 20347 | (link_id >= 0 && | 
|---|
| 20348 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: link_id))) | 
|---|
| 20349 | goto nla_put_failure; | 
|---|
| 20350 |  | 
|---|
| 20351 | genlmsg_end(skb: msg, hdr); | 
|---|
| 20352 | genlmsg_unicast(net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: nlportid); | 
|---|
| 20353 | return true; | 
|---|
| 20354 |  | 
|---|
| 20355 | nla_put_failure: | 
|---|
| 20356 | nlmsg_free(skb: msg); | 
|---|
| 20357 | return true; | 
|---|
| 20358 | } | 
|---|
| 20359 |  | 
|---|
| 20360 | bool cfg80211_rx_spurious_frame(struct net_device *dev, const u8 *addr, | 
|---|
| 20361 | int link_id, gfp_t gfp) | 
|---|
| 20362 | { | 
|---|
| 20363 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 20364 | bool ret; | 
|---|
| 20365 |  | 
|---|
| 20366 | trace_cfg80211_rx_spurious_frame(netdev: dev, addr, link_id); | 
|---|
| 20367 |  | 
|---|
| 20368 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && | 
|---|
| 20369 | wdev->iftype != NL80211_IFTYPE_P2P_GO)) { | 
|---|
| 20370 | trace_cfg80211_return_bool(ret: false); | 
|---|
| 20371 | return false; | 
|---|
| 20372 | } | 
|---|
| 20373 | ret = __nl80211_unexpected_frame(dev, cmd: NL80211_CMD_UNEXPECTED_FRAME, | 
|---|
| 20374 | addr, link_id, gfp); | 
|---|
| 20375 | trace_cfg80211_return_bool(ret); | 
|---|
| 20376 | return ret; | 
|---|
| 20377 | } | 
|---|
| 20378 | EXPORT_SYMBOL(cfg80211_rx_spurious_frame); | 
|---|
| 20379 |  | 
|---|
| 20380 | bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, const u8 *addr, | 
|---|
| 20381 | int link_id, gfp_t gfp) | 
|---|
| 20382 | { | 
|---|
| 20383 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 20384 | bool ret; | 
|---|
| 20385 |  | 
|---|
| 20386 | trace_cfg80211_rx_unexpected_4addr_frame(netdev: dev, addr, link_id); | 
|---|
| 20387 |  | 
|---|
| 20388 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && | 
|---|
| 20389 | wdev->iftype != NL80211_IFTYPE_P2P_GO && | 
|---|
| 20390 | wdev->iftype != NL80211_IFTYPE_AP_VLAN)) { | 
|---|
| 20391 | trace_cfg80211_return_bool(ret: false); | 
|---|
| 20392 | return false; | 
|---|
| 20393 | } | 
|---|
| 20394 | ret = __nl80211_unexpected_frame(dev, | 
|---|
| 20395 | cmd: NL80211_CMD_UNEXPECTED_4ADDR_FRAME, | 
|---|
| 20396 | addr, link_id, gfp); | 
|---|
| 20397 | trace_cfg80211_return_bool(ret); | 
|---|
| 20398 | return ret; | 
|---|
| 20399 | } | 
|---|
| 20400 | EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); | 
|---|
| 20401 |  | 
|---|
| 20402 | int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, | 
|---|
| 20403 | struct wireless_dev *wdev, u32 nlportid, | 
|---|
| 20404 | struct cfg80211_rx_info *info, gfp_t gfp) | 
|---|
| 20405 | { | 
|---|
| 20406 | struct net_device *netdev = wdev->netdev; | 
|---|
| 20407 | struct sk_buff *msg; | 
|---|
| 20408 | void *hdr; | 
|---|
| 20409 |  | 
|---|
| 20410 | msg = nlmsg_new(payload: 100 + info->len, flags: gfp); | 
|---|
| 20411 | if (!msg) | 
|---|
| 20412 | return -ENOMEM; | 
|---|
| 20413 |  | 
|---|
| 20414 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_FRAME); | 
|---|
| 20415 | if (!hdr) { | 
|---|
| 20416 | nlmsg_free(skb: msg); | 
|---|
| 20417 | return -ENOMEM; | 
|---|
| 20418 | } | 
|---|
| 20419 |  | 
|---|
| 20420 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 20421 | (netdev && nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, | 
|---|
| 20422 | value: netdev->ifindex)) || | 
|---|
| 20423 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), | 
|---|
| 20424 | padattr: NL80211_ATTR_PAD) || | 
|---|
| 20425 | (info->have_link_id && | 
|---|
| 20426 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: info->link_id)) || | 
|---|
| 20427 | nla_put_u32(skb: msg, NL80211_ATTR_WIPHY_FREQ, KHZ_TO_MHZ(info->freq)) || | 
|---|
| 20428 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY_FREQ_OFFSET, value: info->freq % 1000) || | 
|---|
| 20429 | (info->sig_dbm && | 
|---|
| 20430 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_RX_SIGNAL_DBM, value: info->sig_dbm)) || | 
|---|
| 20431 | nla_put(skb: msg, NL80211_ATTR_FRAME, attrlen: info->len, data: info->buf) || | 
|---|
| 20432 | (info->flags && | 
|---|
| 20433 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_RXMGMT_FLAGS, value: info->flags)) || | 
|---|
| 20434 | (info->rx_tstamp && nla_put_u64_64bit(skb: msg, | 
|---|
| 20435 | attrtype: NL80211_ATTR_RX_HW_TIMESTAMP, | 
|---|
| 20436 | value: info->rx_tstamp, | 
|---|
| 20437 | padattr: NL80211_ATTR_PAD)) || | 
|---|
| 20438 | (info->ack_tstamp && nla_put_u64_64bit(skb: msg, | 
|---|
| 20439 | attrtype: NL80211_ATTR_TX_HW_TIMESTAMP, | 
|---|
| 20440 | value: info->ack_tstamp, | 
|---|
| 20441 | padattr: NL80211_ATTR_PAD))) | 
|---|
| 20442 | goto nla_put_failure; | 
|---|
| 20443 |  | 
|---|
| 20444 | genlmsg_end(skb: msg, hdr); | 
|---|
| 20445 |  | 
|---|
| 20446 | return genlmsg_unicast(net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: nlportid); | 
|---|
| 20447 |  | 
|---|
| 20448 | nla_put_failure: | 
|---|
| 20449 | nlmsg_free(skb: msg); | 
|---|
| 20450 | return -ENOBUFS; | 
|---|
| 20451 | } | 
|---|
| 20452 |  | 
|---|
| 20453 | static void nl80211_frame_tx_status(struct wireless_dev *wdev, | 
|---|
| 20454 | struct cfg80211_tx_status *status, | 
|---|
| 20455 | gfp_t gfp, enum nl80211_commands command) | 
|---|
| 20456 | { | 
|---|
| 20457 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 20458 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 20459 | struct net_device *netdev = wdev->netdev; | 
|---|
| 20460 | struct sk_buff *msg; | 
|---|
| 20461 | void *hdr; | 
|---|
| 20462 |  | 
|---|
| 20463 | if (command == NL80211_CMD_FRAME_TX_STATUS) | 
|---|
| 20464 | trace_cfg80211_mgmt_tx_status(wdev, cookie: status->cookie, | 
|---|
| 20465 | ack: status->ack); | 
|---|
| 20466 | else | 
|---|
| 20467 | trace_cfg80211_control_port_tx_status(wdev, cookie: status->cookie, | 
|---|
| 20468 | ack: status->ack); | 
|---|
| 20469 |  | 
|---|
| 20470 | msg = nlmsg_new(payload: 100 + status->len, flags: gfp); | 
|---|
| 20471 | if (!msg) | 
|---|
| 20472 | return; | 
|---|
| 20473 |  | 
|---|
| 20474 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: command); | 
|---|
| 20475 | if (!hdr) { | 
|---|
| 20476 | nlmsg_free(skb: msg); | 
|---|
| 20477 | return; | 
|---|
| 20478 | } | 
|---|
| 20479 |  | 
|---|
| 20480 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 20481 | (netdev && nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, | 
|---|
| 20482 | value: netdev->ifindex)) || | 
|---|
| 20483 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), | 
|---|
| 20484 | padattr: NL80211_ATTR_PAD) || | 
|---|
| 20485 | nla_put(skb: msg, NL80211_ATTR_FRAME, attrlen: status->len, data: status->buf) || | 
|---|
| 20486 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: status->cookie, | 
|---|
| 20487 | padattr: NL80211_ATTR_PAD) || | 
|---|
| 20488 | (status->ack && nla_put_flag(skb: msg, attrtype: NL80211_ATTR_ACK)) || | 
|---|
| 20489 | (status->tx_tstamp && | 
|---|
| 20490 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_TX_HW_TIMESTAMP, | 
|---|
| 20491 | value: status->tx_tstamp, padattr: NL80211_ATTR_PAD)) || | 
|---|
| 20492 | (status->ack_tstamp && | 
|---|
| 20493 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_RX_HW_TIMESTAMP, | 
|---|
| 20494 | value: status->ack_tstamp, padattr: NL80211_ATTR_PAD))) | 
|---|
| 20495 | goto nla_put_failure; | 
|---|
| 20496 |  | 
|---|
| 20497 | genlmsg_end(skb: msg, hdr); | 
|---|
| 20498 |  | 
|---|
| 20499 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 20500 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 20501 | return; | 
|---|
| 20502 |  | 
|---|
| 20503 | nla_put_failure: | 
|---|
| 20504 | nlmsg_free(skb: msg); | 
|---|
| 20505 | } | 
|---|
| 20506 |  | 
|---|
| 20507 | void cfg80211_control_port_tx_status(struct wireless_dev *wdev, u64 cookie, | 
|---|
| 20508 | const u8 *buf, size_t len, bool ack, | 
|---|
| 20509 | gfp_t gfp) | 
|---|
| 20510 | { | 
|---|
| 20511 | struct cfg80211_tx_status status = { | 
|---|
| 20512 | .cookie = cookie, | 
|---|
| 20513 | .buf = buf, | 
|---|
| 20514 | .len = len, | 
|---|
| 20515 | .ack = ack | 
|---|
| 20516 | }; | 
|---|
| 20517 |  | 
|---|
| 20518 | nl80211_frame_tx_status(wdev, status: &status, gfp, | 
|---|
| 20519 | command: NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS); | 
|---|
| 20520 | } | 
|---|
| 20521 | EXPORT_SYMBOL(cfg80211_control_port_tx_status); | 
|---|
| 20522 |  | 
|---|
| 20523 | void cfg80211_mgmt_tx_status_ext(struct wireless_dev *wdev, | 
|---|
| 20524 | struct cfg80211_tx_status *status, gfp_t gfp) | 
|---|
| 20525 | { | 
|---|
| 20526 | nl80211_frame_tx_status(wdev, status, gfp, command: NL80211_CMD_FRAME_TX_STATUS); | 
|---|
| 20527 | } | 
|---|
| 20528 | EXPORT_SYMBOL(cfg80211_mgmt_tx_status_ext); | 
|---|
| 20529 |  | 
|---|
| 20530 | static int __nl80211_rx_control_port(struct net_device *dev, | 
|---|
| 20531 | struct sk_buff *skb, | 
|---|
| 20532 | bool unencrypted, | 
|---|
| 20533 | int link_id, | 
|---|
| 20534 | gfp_t gfp) | 
|---|
| 20535 | { | 
|---|
| 20536 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 20537 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); | 
|---|
| 20538 | struct ethhdr *ehdr = eth_hdr(skb); | 
|---|
| 20539 | const u8 *addr = ehdr->h_source; | 
|---|
| 20540 | u16 proto = be16_to_cpu(skb->protocol); | 
|---|
| 20541 | struct sk_buff *msg; | 
|---|
| 20542 | void *hdr; | 
|---|
| 20543 | struct nlattr *frame; | 
|---|
| 20544 |  | 
|---|
| 20545 | u32 nlportid = READ_ONCE(wdev->conn_owner_nlportid); | 
|---|
| 20546 |  | 
|---|
| 20547 | if (!nlportid) | 
|---|
| 20548 | return -ENOENT; | 
|---|
| 20549 |  | 
|---|
| 20550 | msg = nlmsg_new(payload: 100 + skb->len, flags: gfp); | 
|---|
| 20551 | if (!msg) | 
|---|
| 20552 | return -ENOMEM; | 
|---|
| 20553 |  | 
|---|
| 20554 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_CONTROL_PORT_FRAME); | 
|---|
| 20555 | if (!hdr) { | 
|---|
| 20556 | nlmsg_free(skb: msg); | 
|---|
| 20557 | return -ENOBUFS; | 
|---|
| 20558 | } | 
|---|
| 20559 |  | 
|---|
| 20560 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 20561 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || | 
|---|
| 20562 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), | 
|---|
| 20563 | padattr: NL80211_ATTR_PAD) || | 
|---|
| 20564 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: addr) || | 
|---|
| 20565 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_CONTROL_PORT_ETHERTYPE, value: proto) || | 
|---|
| 20566 | (link_id >= 0 && | 
|---|
| 20567 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: link_id)) || | 
|---|
| 20568 | (unencrypted && nla_put_flag(skb: msg, | 
|---|
| 20569 | attrtype: NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))) | 
|---|
| 20570 | goto nla_put_failure; | 
|---|
| 20571 |  | 
|---|
| 20572 | frame = nla_reserve(skb: msg, NL80211_ATTR_FRAME, attrlen: skb->len); | 
|---|
| 20573 | if (!frame) | 
|---|
| 20574 | goto nla_put_failure; | 
|---|
| 20575 |  | 
|---|
| 20576 | skb_copy_bits(skb, offset: 0, to: nla_data(nla: frame), len: skb->len); | 
|---|
| 20577 | genlmsg_end(skb: msg, hdr); | 
|---|
| 20578 |  | 
|---|
| 20579 | return genlmsg_unicast(net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: nlportid); | 
|---|
| 20580 |  | 
|---|
| 20581 | nla_put_failure: | 
|---|
| 20582 | nlmsg_free(skb: msg); | 
|---|
| 20583 | return -ENOBUFS; | 
|---|
| 20584 | } | 
|---|
| 20585 |  | 
|---|
| 20586 | bool cfg80211_rx_control_port(struct net_device *dev, struct sk_buff *skb, | 
|---|
| 20587 | bool unencrypted, int link_id) | 
|---|
| 20588 | { | 
|---|
| 20589 | int ret; | 
|---|
| 20590 |  | 
|---|
| 20591 | trace_cfg80211_rx_control_port(netdev: dev, skb, unencrypted, link_id); | 
|---|
| 20592 | ret = __nl80211_rx_control_port(dev, skb, unencrypted, link_id, | 
|---|
| 20593 | GFP_ATOMIC); | 
|---|
| 20594 | trace_cfg80211_return_bool(ret: ret == 0); | 
|---|
| 20595 | return ret == 0; | 
|---|
| 20596 | } | 
|---|
| 20597 | EXPORT_SYMBOL(cfg80211_rx_control_port); | 
|---|
| 20598 |  | 
|---|
| 20599 | static struct sk_buff *cfg80211_prepare_cqm(struct net_device *dev, | 
|---|
| 20600 | const char *mac, gfp_t gfp) | 
|---|
| 20601 | { | 
|---|
| 20602 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 20603 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); | 
|---|
| 20604 | struct sk_buff *msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 20605 | void **cb; | 
|---|
| 20606 |  | 
|---|
| 20607 | if (!msg) | 
|---|
| 20608 | return NULL; | 
|---|
| 20609 |  | 
|---|
| 20610 | cb = (void **)msg->cb; | 
|---|
| 20611 |  | 
|---|
| 20612 | cb[0] = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_NOTIFY_CQM); | 
|---|
| 20613 | if (!cb[0]) { | 
|---|
| 20614 | nlmsg_free(skb: msg); | 
|---|
| 20615 | return NULL; | 
|---|
| 20616 | } | 
|---|
| 20617 |  | 
|---|
| 20618 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 20619 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex)) | 
|---|
| 20620 | goto nla_put_failure; | 
|---|
| 20621 |  | 
|---|
| 20622 | if (mac && nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: mac)) | 
|---|
| 20623 | goto nla_put_failure; | 
|---|
| 20624 |  | 
|---|
| 20625 | cb[1] = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_CQM); | 
|---|
| 20626 | if (!cb[1]) | 
|---|
| 20627 | goto nla_put_failure; | 
|---|
| 20628 |  | 
|---|
| 20629 | cb[2] = rdev; | 
|---|
| 20630 |  | 
|---|
| 20631 | return msg; | 
|---|
| 20632 | nla_put_failure: | 
|---|
| 20633 | nlmsg_free(skb: msg); | 
|---|
| 20634 | return NULL; | 
|---|
| 20635 | } | 
|---|
| 20636 |  | 
|---|
| 20637 | static void cfg80211_send_cqm(struct sk_buff *msg, gfp_t gfp) | 
|---|
| 20638 | { | 
|---|
| 20639 | void **cb = (void **)msg->cb; | 
|---|
| 20640 | struct cfg80211_registered_device *rdev = cb[2]; | 
|---|
| 20641 |  | 
|---|
| 20642 | nla_nest_end(skb: msg, start: cb[1]); | 
|---|
| 20643 | genlmsg_end(skb: msg, hdr: cb[0]); | 
|---|
| 20644 |  | 
|---|
| 20645 | memset(s: msg->cb, c: 0, n: sizeof(msg->cb)); | 
|---|
| 20646 |  | 
|---|
| 20647 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 20648 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 20649 | } | 
|---|
| 20650 |  | 
|---|
| 20651 | void (struct net_device *dev, | 
|---|
| 20652 | enum nl80211_cqm_rssi_threshold_event , | 
|---|
| 20653 | s32 , gfp_t gfp) | 
|---|
| 20654 | { | 
|---|
| 20655 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 20656 | struct cfg80211_cqm_config *cqm_config; | 
|---|
| 20657 |  | 
|---|
| 20658 | trace_cfg80211_cqm_rssi_notify(netdev: dev, rssi_event, rssi_level); | 
|---|
| 20659 |  | 
|---|
| 20660 | if (WARN_ON(rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW && | 
|---|
| 20661 | rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH)) | 
|---|
| 20662 | return; | 
|---|
| 20663 |  | 
|---|
| 20664 | rcu_read_lock(); | 
|---|
| 20665 | cqm_config = rcu_dereference(wdev->cqm_config); | 
|---|
| 20666 | if (cqm_config) { | 
|---|
| 20667 | cqm_config->last_rssi_event_value = rssi_level; | 
|---|
| 20668 | cqm_config->last_rssi_event_type = rssi_event; | 
|---|
| 20669 | wiphy_work_queue(wiphy: wdev->wiphy, work: &wdev->cqm_rssi_work); | 
|---|
| 20670 | } | 
|---|
| 20671 | rcu_read_unlock(); | 
|---|
| 20672 | } | 
|---|
| 20673 | EXPORT_SYMBOL(cfg80211_cqm_rssi_notify); | 
|---|
| 20674 |  | 
|---|
| 20675 | void (struct wiphy *wiphy, struct wiphy_work *work) | 
|---|
| 20676 | { | 
|---|
| 20677 | struct wireless_dev *wdev = container_of(work, struct wireless_dev, | 
|---|
| 20678 | cqm_rssi_work); | 
|---|
| 20679 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 20680 | enum nl80211_cqm_rssi_threshold_event ; | 
|---|
| 20681 | struct cfg80211_cqm_config *cqm_config; | 
|---|
| 20682 | struct sk_buff *msg; | 
|---|
| 20683 | s32 ; | 
|---|
| 20684 |  | 
|---|
| 20685 | cqm_config = wiphy_dereference(wdev->wiphy, wdev->cqm_config); | 
|---|
| 20686 | if (!cqm_config) | 
|---|
| 20687 | return; | 
|---|
| 20688 |  | 
|---|
| 20689 | if (cqm_config->use_range_api) | 
|---|
| 20690 | cfg80211_cqm_rssi_update(rdev, dev: wdev->netdev, cqm_config); | 
|---|
| 20691 |  | 
|---|
| 20692 | rssi_level = cqm_config->last_rssi_event_value; | 
|---|
| 20693 | rssi_event = cqm_config->last_rssi_event_type; | 
|---|
| 20694 |  | 
|---|
| 20695 | msg = cfg80211_prepare_cqm(dev: wdev->netdev, NULL, GFP_KERNEL); | 
|---|
| 20696 | if (!msg) | 
|---|
| 20697 | return; | 
|---|
| 20698 |  | 
|---|
| 20699 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, | 
|---|
| 20700 | value: rssi_event)) | 
|---|
| 20701 | goto nla_put_failure; | 
|---|
| 20702 |  | 
|---|
| 20703 | if (rssi_level && nla_put_s32(skb: msg, attrtype: NL80211_ATTR_CQM_RSSI_LEVEL, | 
|---|
| 20704 | value: rssi_level)) | 
|---|
| 20705 | goto nla_put_failure; | 
|---|
| 20706 |  | 
|---|
| 20707 | cfg80211_send_cqm(msg, GFP_KERNEL); | 
|---|
| 20708 |  | 
|---|
| 20709 | return; | 
|---|
| 20710 |  | 
|---|
| 20711 | nla_put_failure: | 
|---|
| 20712 | nlmsg_free(skb: msg); | 
|---|
| 20713 | } | 
|---|
| 20714 |  | 
|---|
| 20715 | void cfg80211_cqm_txe_notify(struct net_device *dev, | 
|---|
| 20716 | const u8 *peer, u32 num_packets, | 
|---|
| 20717 | u32 rate, u32 intvl, gfp_t gfp) | 
|---|
| 20718 | { | 
|---|
| 20719 | struct sk_buff *msg; | 
|---|
| 20720 |  | 
|---|
| 20721 | msg = cfg80211_prepare_cqm(dev, mac: peer, gfp); | 
|---|
| 20722 | if (!msg) | 
|---|
| 20723 | return; | 
|---|
| 20724 |  | 
|---|
| 20725 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CQM_TXE_PKTS, value: num_packets)) | 
|---|
| 20726 | goto nla_put_failure; | 
|---|
| 20727 |  | 
|---|
| 20728 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CQM_TXE_RATE, value: rate)) | 
|---|
| 20729 | goto nla_put_failure; | 
|---|
| 20730 |  | 
|---|
| 20731 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CQM_TXE_INTVL, value: intvl)) | 
|---|
| 20732 | goto nla_put_failure; | 
|---|
| 20733 |  | 
|---|
| 20734 | cfg80211_send_cqm(msg, gfp); | 
|---|
| 20735 | return; | 
|---|
| 20736 |  | 
|---|
| 20737 | nla_put_failure: | 
|---|
| 20738 | nlmsg_free(skb: msg); | 
|---|
| 20739 | } | 
|---|
| 20740 | EXPORT_SYMBOL(cfg80211_cqm_txe_notify); | 
|---|
| 20741 |  | 
|---|
| 20742 | void cfg80211_cqm_pktloss_notify(struct net_device *dev, | 
|---|
| 20743 | const u8 *peer, u32 num_packets, gfp_t gfp) | 
|---|
| 20744 | { | 
|---|
| 20745 | struct sk_buff *msg; | 
|---|
| 20746 |  | 
|---|
| 20747 | trace_cfg80211_cqm_pktloss_notify(netdev: dev, peer, num_packets); | 
|---|
| 20748 |  | 
|---|
| 20749 | msg = cfg80211_prepare_cqm(dev, mac: peer, gfp); | 
|---|
| 20750 | if (!msg) | 
|---|
| 20751 | return; | 
|---|
| 20752 |  | 
|---|
| 20753 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CQM_PKT_LOSS_EVENT, value: num_packets)) | 
|---|
| 20754 | goto nla_put_failure; | 
|---|
| 20755 |  | 
|---|
| 20756 | cfg80211_send_cqm(msg, gfp); | 
|---|
| 20757 | return; | 
|---|
| 20758 |  | 
|---|
| 20759 | nla_put_failure: | 
|---|
| 20760 | nlmsg_free(skb: msg); | 
|---|
| 20761 | } | 
|---|
| 20762 | EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify); | 
|---|
| 20763 |  | 
|---|
| 20764 | void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp) | 
|---|
| 20765 | { | 
|---|
| 20766 | struct sk_buff *msg; | 
|---|
| 20767 |  | 
|---|
| 20768 | msg = cfg80211_prepare_cqm(dev, NULL, gfp); | 
|---|
| 20769 | if (!msg) | 
|---|
| 20770 | return; | 
|---|
| 20771 |  | 
|---|
| 20772 | if (nla_put_flag(skb: msg, attrtype: NL80211_ATTR_CQM_BEACON_LOSS_EVENT)) | 
|---|
| 20773 | goto nla_put_failure; | 
|---|
| 20774 |  | 
|---|
| 20775 | cfg80211_send_cqm(msg, gfp); | 
|---|
| 20776 | return; | 
|---|
| 20777 |  | 
|---|
| 20778 | nla_put_failure: | 
|---|
| 20779 | nlmsg_free(skb: msg); | 
|---|
| 20780 | } | 
|---|
| 20781 | EXPORT_SYMBOL(cfg80211_cqm_beacon_loss_notify); | 
|---|
| 20782 |  | 
|---|
| 20783 | static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, | 
|---|
| 20784 | struct net_device *netdev, const u8 *bssid, | 
|---|
| 20785 | const u8 *replay_ctr, gfp_t gfp) | 
|---|
| 20786 | { | 
|---|
| 20787 | struct sk_buff *msg; | 
|---|
| 20788 | struct nlattr *rekey_attr; | 
|---|
| 20789 | void *hdr; | 
|---|
| 20790 |  | 
|---|
| 20791 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 20792 | if (!msg) | 
|---|
| 20793 | return; | 
|---|
| 20794 |  | 
|---|
| 20795 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_SET_REKEY_OFFLOAD); | 
|---|
| 20796 | if (!hdr) { | 
|---|
| 20797 | nlmsg_free(skb: msg); | 
|---|
| 20798 | return; | 
|---|
| 20799 | } | 
|---|
| 20800 |  | 
|---|
| 20801 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 20802 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || | 
|---|
| 20803 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: bssid)) | 
|---|
| 20804 | goto nla_put_failure; | 
|---|
| 20805 |  | 
|---|
| 20806 | rekey_attr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_REKEY_DATA); | 
|---|
| 20807 | if (!rekey_attr) | 
|---|
| 20808 | goto nla_put_failure; | 
|---|
| 20809 |  | 
|---|
| 20810 | if (nla_put(skb: msg, attrtype: NL80211_REKEY_DATA_REPLAY_CTR, | 
|---|
| 20811 | NL80211_REPLAY_CTR_LEN, data: replay_ctr)) | 
|---|
| 20812 | goto nla_put_failure; | 
|---|
| 20813 |  | 
|---|
| 20814 | nla_nest_end(skb: msg, start: rekey_attr); | 
|---|
| 20815 |  | 
|---|
| 20816 | genlmsg_end(skb: msg, hdr); | 
|---|
| 20817 |  | 
|---|
| 20818 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 20819 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 20820 | return; | 
|---|
| 20821 |  | 
|---|
| 20822 | nla_put_failure: | 
|---|
| 20823 | nlmsg_free(skb: msg); | 
|---|
| 20824 | } | 
|---|
| 20825 |  | 
|---|
| 20826 | void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, | 
|---|
| 20827 | const u8 *replay_ctr, gfp_t gfp) | 
|---|
| 20828 | { | 
|---|
| 20829 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 20830 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 20831 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 20832 |  | 
|---|
| 20833 | trace_cfg80211_gtk_rekey_notify(netdev: dev, macaddr: bssid); | 
|---|
| 20834 | nl80211_gtk_rekey_notify(rdev, netdev: dev, bssid, replay_ctr, gfp); | 
|---|
| 20835 | } | 
|---|
| 20836 | EXPORT_SYMBOL(cfg80211_gtk_rekey_notify); | 
|---|
| 20837 |  | 
|---|
| 20838 | static void | 
|---|
| 20839 | nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, | 
|---|
| 20840 | struct net_device *netdev, int index, | 
|---|
| 20841 | const u8 *bssid, bool preauth, gfp_t gfp) | 
|---|
| 20842 | { | 
|---|
| 20843 | struct sk_buff *msg; | 
|---|
| 20844 | struct nlattr *attr; | 
|---|
| 20845 | void *hdr; | 
|---|
| 20846 |  | 
|---|
| 20847 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 20848 | if (!msg) | 
|---|
| 20849 | return; | 
|---|
| 20850 |  | 
|---|
| 20851 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_PMKSA_CANDIDATE); | 
|---|
| 20852 | if (!hdr) { | 
|---|
| 20853 | nlmsg_free(skb: msg); | 
|---|
| 20854 | return; | 
|---|
| 20855 | } | 
|---|
| 20856 |  | 
|---|
| 20857 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 20858 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex)) | 
|---|
| 20859 | goto nla_put_failure; | 
|---|
| 20860 |  | 
|---|
| 20861 | attr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_PMKSA_CANDIDATE); | 
|---|
| 20862 | if (!attr) | 
|---|
| 20863 | goto nla_put_failure; | 
|---|
| 20864 |  | 
|---|
| 20865 | if (nla_put_u32(skb: msg, attrtype: NL80211_PMKSA_CANDIDATE_INDEX, value: index) || | 
|---|
| 20866 | nla_put(skb: msg, attrtype: NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, data: bssid) || | 
|---|
| 20867 | (preauth && | 
|---|
| 20868 | nla_put_flag(skb: msg, attrtype: NL80211_PMKSA_CANDIDATE_PREAUTH))) | 
|---|
| 20869 | goto nla_put_failure; | 
|---|
| 20870 |  | 
|---|
| 20871 | nla_nest_end(skb: msg, start: attr); | 
|---|
| 20872 |  | 
|---|
| 20873 | genlmsg_end(skb: msg, hdr); | 
|---|
| 20874 |  | 
|---|
| 20875 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 20876 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 20877 | return; | 
|---|
| 20878 |  | 
|---|
| 20879 | nla_put_failure: | 
|---|
| 20880 | nlmsg_free(skb: msg); | 
|---|
| 20881 | } | 
|---|
| 20882 |  | 
|---|
| 20883 | void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, | 
|---|
| 20884 | const u8 *bssid, bool preauth, gfp_t gfp) | 
|---|
| 20885 | { | 
|---|
| 20886 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 20887 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 20888 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 20889 |  | 
|---|
| 20890 | trace_cfg80211_pmksa_candidate_notify(netdev: dev, index, bssid, preauth); | 
|---|
| 20891 | nl80211_pmksa_candidate_notify(rdev, netdev: dev, index, bssid, preauth, gfp); | 
|---|
| 20892 | } | 
|---|
| 20893 | EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); | 
|---|
| 20894 |  | 
|---|
| 20895 | static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, | 
|---|
| 20896 | struct net_device *netdev, | 
|---|
| 20897 | unsigned int link_id, | 
|---|
| 20898 | struct cfg80211_chan_def *chandef, | 
|---|
| 20899 | gfp_t gfp, | 
|---|
| 20900 | enum nl80211_commands notif, | 
|---|
| 20901 | u8 count, bool quiet) | 
|---|
| 20902 | { | 
|---|
| 20903 | struct wireless_dev *wdev = netdev->ieee80211_ptr; | 
|---|
| 20904 | struct sk_buff *msg; | 
|---|
| 20905 | void *hdr; | 
|---|
| 20906 |  | 
|---|
| 20907 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 20908 | if (!msg) | 
|---|
| 20909 | return; | 
|---|
| 20910 |  | 
|---|
| 20911 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: notif); | 
|---|
| 20912 | if (!hdr) { | 
|---|
| 20913 | nlmsg_free(skb: msg); | 
|---|
| 20914 | return; | 
|---|
| 20915 | } | 
|---|
| 20916 |  | 
|---|
| 20917 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex)) | 
|---|
| 20918 | goto nla_put_failure; | 
|---|
| 20919 |  | 
|---|
| 20920 | if (wdev->valid_links && | 
|---|
| 20921 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: link_id)) | 
|---|
| 20922 | goto nla_put_failure; | 
|---|
| 20923 |  | 
|---|
| 20924 | if (nl80211_send_chandef(msg, chandef)) | 
|---|
| 20925 | goto nla_put_failure; | 
|---|
| 20926 |  | 
|---|
| 20927 | if (notif == NL80211_CMD_CH_SWITCH_STARTED_NOTIFY) { | 
|---|
| 20928 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CH_SWITCH_COUNT, value: count)) | 
|---|
| 20929 | goto nla_put_failure; | 
|---|
| 20930 | if (quiet && | 
|---|
| 20931 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_CH_SWITCH_BLOCK_TX)) | 
|---|
| 20932 | goto nla_put_failure; | 
|---|
| 20933 | } | 
|---|
| 20934 |  | 
|---|
| 20935 | genlmsg_end(skb: msg, hdr); | 
|---|
| 20936 |  | 
|---|
| 20937 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 20938 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 20939 | return; | 
|---|
| 20940 |  | 
|---|
| 20941 | nla_put_failure: | 
|---|
| 20942 | nlmsg_free(skb: msg); | 
|---|
| 20943 | } | 
|---|
| 20944 |  | 
|---|
| 20945 | void cfg80211_ch_switch_notify(struct net_device *dev, | 
|---|
| 20946 | struct cfg80211_chan_def *chandef, | 
|---|
| 20947 | unsigned int link_id) | 
|---|
| 20948 | { | 
|---|
| 20949 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 20950 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 20951 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 20952 |  | 
|---|
| 20953 | lockdep_assert_wiphy(wdev->wiphy); | 
|---|
| 20954 | WARN_INVALID_LINK_ID(wdev, link_id); | 
|---|
| 20955 |  | 
|---|
| 20956 | trace_cfg80211_ch_switch_notify(netdev: dev, chandef, link_id); | 
|---|
| 20957 |  | 
|---|
| 20958 | switch (wdev->iftype) { | 
|---|
| 20959 | case NL80211_IFTYPE_STATION: | 
|---|
| 20960 | case NL80211_IFTYPE_P2P_CLIENT: | 
|---|
| 20961 | if (!WARN_ON(!wdev->links[link_id].client.current_bss)) | 
|---|
| 20962 | cfg80211_update_assoc_bss_entry(wdev, link: link_id, | 
|---|
| 20963 | channel: chandef->chan); | 
|---|
| 20964 | break; | 
|---|
| 20965 | case NL80211_IFTYPE_MESH_POINT: | 
|---|
| 20966 | wdev->u.mesh.chandef = *chandef; | 
|---|
| 20967 | wdev->u.mesh.preset_chandef = *chandef; | 
|---|
| 20968 | break; | 
|---|
| 20969 | case NL80211_IFTYPE_AP: | 
|---|
| 20970 | case NL80211_IFTYPE_P2P_GO: | 
|---|
| 20971 | wdev->links[link_id].ap.chandef = *chandef; | 
|---|
| 20972 | break; | 
|---|
| 20973 | case NL80211_IFTYPE_ADHOC: | 
|---|
| 20974 | wdev->u.ibss.chandef = *chandef; | 
|---|
| 20975 | break; | 
|---|
| 20976 | default: | 
|---|
| 20977 | WARN_ON(1); | 
|---|
| 20978 | break; | 
|---|
| 20979 | } | 
|---|
| 20980 |  | 
|---|
| 20981 | cfg80211_schedule_channels_check(wdev); | 
|---|
| 20982 | cfg80211_sched_dfs_chan_update(rdev); | 
|---|
| 20983 |  | 
|---|
| 20984 | nl80211_ch_switch_notify(rdev, netdev: dev, link_id, chandef, GFP_KERNEL, | 
|---|
| 20985 | notif: NL80211_CMD_CH_SWITCH_NOTIFY, count: 0, quiet: false); | 
|---|
| 20986 | } | 
|---|
| 20987 | EXPORT_SYMBOL(cfg80211_ch_switch_notify); | 
|---|
| 20988 |  | 
|---|
| 20989 | void cfg80211_ch_switch_started_notify(struct net_device *dev, | 
|---|
| 20990 | struct cfg80211_chan_def *chandef, | 
|---|
| 20991 | unsigned int link_id, u8 count, | 
|---|
| 20992 | bool quiet) | 
|---|
| 20993 | { | 
|---|
| 20994 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 20995 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 20996 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 20997 |  | 
|---|
| 20998 | lockdep_assert_wiphy(wdev->wiphy); | 
|---|
| 20999 | WARN_INVALID_LINK_ID(wdev, link_id); | 
|---|
| 21000 |  | 
|---|
| 21001 | trace_cfg80211_ch_switch_started_notify(netdev: dev, chandef, link_id); | 
|---|
| 21002 |  | 
|---|
| 21003 |  | 
|---|
| 21004 | nl80211_ch_switch_notify(rdev, netdev: dev, link_id, chandef, GFP_KERNEL, | 
|---|
| 21005 | notif: NL80211_CMD_CH_SWITCH_STARTED_NOTIFY, | 
|---|
| 21006 | count, quiet); | 
|---|
| 21007 | } | 
|---|
| 21008 | EXPORT_SYMBOL(cfg80211_ch_switch_started_notify); | 
|---|
| 21009 |  | 
|---|
| 21010 | int cfg80211_bss_color_notify(struct net_device *dev, | 
|---|
| 21011 | enum nl80211_commands cmd, u8 count, | 
|---|
| 21012 | u64 color_bitmap, u8 link_id) | 
|---|
| 21013 | { | 
|---|
| 21014 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 21015 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 21016 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 21017 | struct sk_buff *msg; | 
|---|
| 21018 | void *hdr; | 
|---|
| 21019 |  | 
|---|
| 21020 | lockdep_assert_wiphy(wdev->wiphy); | 
|---|
| 21021 |  | 
|---|
| 21022 | trace_cfg80211_bss_color_notify(netdev: dev, cmd, count, color_bitmap); | 
|---|
| 21023 |  | 
|---|
| 21024 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 21025 | if (!msg) | 
|---|
| 21026 | return -ENOMEM; | 
|---|
| 21027 |  | 
|---|
| 21028 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd); | 
|---|
| 21029 | if (!hdr) | 
|---|
| 21030 | goto nla_put_failure; | 
|---|
| 21031 |  | 
|---|
| 21032 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex)) | 
|---|
| 21033 | goto nla_put_failure; | 
|---|
| 21034 |  | 
|---|
| 21035 | if (wdev->valid_links && | 
|---|
| 21036 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: link_id)) | 
|---|
| 21037 | goto nla_put_failure; | 
|---|
| 21038 |  | 
|---|
| 21039 | if (cmd == NL80211_CMD_COLOR_CHANGE_STARTED && | 
|---|
| 21040 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_COLOR_CHANGE_COUNT, value: count)) | 
|---|
| 21041 | goto nla_put_failure; | 
|---|
| 21042 |  | 
|---|
| 21043 | if (cmd == NL80211_CMD_OBSS_COLOR_COLLISION && | 
|---|
| 21044 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_OBSS_COLOR_BITMAP, | 
|---|
| 21045 | value: color_bitmap, padattr: NL80211_ATTR_PAD)) | 
|---|
| 21046 | goto nla_put_failure; | 
|---|
| 21047 |  | 
|---|
| 21048 | genlmsg_end(skb: msg, hdr); | 
|---|
| 21049 |  | 
|---|
| 21050 | return genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), | 
|---|
| 21051 | skb: msg, portid: 0, group: NL80211_MCGRP_MLME, GFP_KERNEL); | 
|---|
| 21052 |  | 
|---|
| 21053 | nla_put_failure: | 
|---|
| 21054 | nlmsg_free(skb: msg); | 
|---|
| 21055 | return -EINVAL; | 
|---|
| 21056 | } | 
|---|
| 21057 | EXPORT_SYMBOL(cfg80211_bss_color_notify); | 
|---|
| 21058 |  | 
|---|
| 21059 | void | 
|---|
| 21060 | nl80211_radar_notify(struct cfg80211_registered_device *rdev, | 
|---|
| 21061 | const struct cfg80211_chan_def *chandef, | 
|---|
| 21062 | enum nl80211_radar_event event, | 
|---|
| 21063 | struct net_device *netdev, gfp_t gfp) | 
|---|
| 21064 | { | 
|---|
| 21065 | struct sk_buff *msg; | 
|---|
| 21066 | void *hdr; | 
|---|
| 21067 |  | 
|---|
| 21068 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 21069 | if (!msg) | 
|---|
| 21070 | return; | 
|---|
| 21071 |  | 
|---|
| 21072 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_RADAR_DETECT); | 
|---|
| 21073 | if (!hdr) { | 
|---|
| 21074 | nlmsg_free(skb: msg); | 
|---|
| 21075 | return; | 
|---|
| 21076 | } | 
|---|
| 21077 |  | 
|---|
| 21078 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx)) | 
|---|
| 21079 | goto nla_put_failure; | 
|---|
| 21080 |  | 
|---|
| 21081 | /* NOP and radar events don't need a netdev parameter */ | 
|---|
| 21082 | if (netdev) { | 
|---|
| 21083 | struct wireless_dev *wdev = netdev->ieee80211_ptr; | 
|---|
| 21084 |  | 
|---|
| 21085 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || | 
|---|
| 21086 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), | 
|---|
| 21087 | padattr: NL80211_ATTR_PAD)) | 
|---|
| 21088 | goto nla_put_failure; | 
|---|
| 21089 | } | 
|---|
| 21090 |  | 
|---|
| 21091 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_RADAR_EVENT, value: event)) | 
|---|
| 21092 | goto nla_put_failure; | 
|---|
| 21093 |  | 
|---|
| 21094 | if (nl80211_send_chandef(msg, chandef)) | 
|---|
| 21095 | goto nla_put_failure; | 
|---|
| 21096 |  | 
|---|
| 21097 | genlmsg_end(skb: msg, hdr); | 
|---|
| 21098 |  | 
|---|
| 21099 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 21100 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 21101 | return; | 
|---|
| 21102 |  | 
|---|
| 21103 | nla_put_failure: | 
|---|
| 21104 | nlmsg_free(skb: msg); | 
|---|
| 21105 | } | 
|---|
| 21106 |  | 
|---|
| 21107 | void cfg80211_sta_opmode_change_notify(struct net_device *dev, const u8 *mac, | 
|---|
| 21108 | struct sta_opmode_info *sta_opmode, | 
|---|
| 21109 | gfp_t gfp) | 
|---|
| 21110 | { | 
|---|
| 21111 | struct sk_buff *msg; | 
|---|
| 21112 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 21113 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); | 
|---|
| 21114 | void *hdr; | 
|---|
| 21115 |  | 
|---|
| 21116 | if (WARN_ON(!mac)) | 
|---|
| 21117 | return; | 
|---|
| 21118 |  | 
|---|
| 21119 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 21120 | if (!msg) | 
|---|
| 21121 | return; | 
|---|
| 21122 |  | 
|---|
| 21123 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_STA_OPMODE_CHANGED); | 
|---|
| 21124 | if (!hdr) { | 
|---|
| 21125 | nlmsg_free(skb: msg); | 
|---|
| 21126 | return; | 
|---|
| 21127 | } | 
|---|
| 21128 |  | 
|---|
| 21129 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx)) | 
|---|
| 21130 | goto nla_put_failure; | 
|---|
| 21131 |  | 
|---|
| 21132 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex)) | 
|---|
| 21133 | goto nla_put_failure; | 
|---|
| 21134 |  | 
|---|
| 21135 | if (nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: mac)) | 
|---|
| 21136 | goto nla_put_failure; | 
|---|
| 21137 |  | 
|---|
| 21138 | if ((sta_opmode->changed & STA_OPMODE_SMPS_MODE_CHANGED) && | 
|---|
| 21139 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_SMPS_MODE, value: sta_opmode->smps_mode)) | 
|---|
| 21140 | goto nla_put_failure; | 
|---|
| 21141 |  | 
|---|
| 21142 | if ((sta_opmode->changed & STA_OPMODE_MAX_BW_CHANGED) && | 
|---|
| 21143 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CHANNEL_WIDTH, value: sta_opmode->bw)) | 
|---|
| 21144 | goto nla_put_failure; | 
|---|
| 21145 |  | 
|---|
| 21146 | if ((sta_opmode->changed & STA_OPMODE_N_SS_CHANGED) && | 
|---|
| 21147 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_NSS, value: sta_opmode->rx_nss)) | 
|---|
| 21148 | goto nla_put_failure; | 
|---|
| 21149 |  | 
|---|
| 21150 | genlmsg_end(skb: msg, hdr); | 
|---|
| 21151 |  | 
|---|
| 21152 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 21153 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 21154 |  | 
|---|
| 21155 | return; | 
|---|
| 21156 |  | 
|---|
| 21157 | nla_put_failure: | 
|---|
| 21158 | nlmsg_free(skb: msg); | 
|---|
| 21159 | } | 
|---|
| 21160 | EXPORT_SYMBOL(cfg80211_sta_opmode_change_notify); | 
|---|
| 21161 |  | 
|---|
| 21162 | void cfg80211_probe_status(struct net_device *dev, const u8 *addr, | 
|---|
| 21163 | u64 cookie, bool acked, s32 ack_signal, | 
|---|
| 21164 | bool is_valid_ack_signal, gfp_t gfp) | 
|---|
| 21165 | { | 
|---|
| 21166 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 21167 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); | 
|---|
| 21168 | struct sk_buff *msg; | 
|---|
| 21169 | void *hdr; | 
|---|
| 21170 |  | 
|---|
| 21171 | trace_cfg80211_probe_status(netdev: dev, addr, cookie, acked); | 
|---|
| 21172 |  | 
|---|
| 21173 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 21174 |  | 
|---|
| 21175 | if (!msg) | 
|---|
| 21176 | return; | 
|---|
| 21177 |  | 
|---|
| 21178 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_PROBE_CLIENT); | 
|---|
| 21179 | if (!hdr) { | 
|---|
| 21180 | nlmsg_free(skb: msg); | 
|---|
| 21181 | return; | 
|---|
| 21182 | } | 
|---|
| 21183 |  | 
|---|
| 21184 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 21185 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || | 
|---|
| 21186 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: addr) || | 
|---|
| 21187 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: cookie, | 
|---|
| 21188 | padattr: NL80211_ATTR_PAD) || | 
|---|
| 21189 | (acked && nla_put_flag(skb: msg, attrtype: NL80211_ATTR_ACK)) || | 
|---|
| 21190 | (is_valid_ack_signal && nla_put_s32(skb: msg, attrtype: NL80211_ATTR_ACK_SIGNAL, | 
|---|
| 21191 | value: ack_signal))) | 
|---|
| 21192 | goto nla_put_failure; | 
|---|
| 21193 |  | 
|---|
| 21194 | genlmsg_end(skb: msg, hdr); | 
|---|
| 21195 |  | 
|---|
| 21196 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 21197 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 21198 | return; | 
|---|
| 21199 |  | 
|---|
| 21200 | nla_put_failure: | 
|---|
| 21201 | nlmsg_free(skb: msg); | 
|---|
| 21202 | } | 
|---|
| 21203 | EXPORT_SYMBOL(cfg80211_probe_status); | 
|---|
| 21204 |  | 
|---|
| 21205 | void cfg80211_report_obss_beacon_khz(struct wiphy *wiphy, const u8 *frame, | 
|---|
| 21206 | size_t len, int freq, int sig_dbm) | 
|---|
| 21207 | { | 
|---|
| 21208 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 21209 | struct sk_buff *msg; | 
|---|
| 21210 | void *hdr; | 
|---|
| 21211 | struct cfg80211_beacon_registration *reg; | 
|---|
| 21212 |  | 
|---|
| 21213 | trace_cfg80211_report_obss_beacon(wiphy, frame, len, freq, sig_dbm); | 
|---|
| 21214 |  | 
|---|
| 21215 | spin_lock_bh(lock: &rdev->beacon_registrations_lock); | 
|---|
| 21216 | list_for_each_entry(reg, &rdev->beacon_registrations, list) { | 
|---|
| 21217 | msg = nlmsg_new(payload: len + 100, GFP_ATOMIC); | 
|---|
| 21218 | if (!msg) { | 
|---|
| 21219 | spin_unlock_bh(lock: &rdev->beacon_registrations_lock); | 
|---|
| 21220 | return; | 
|---|
| 21221 | } | 
|---|
| 21222 |  | 
|---|
| 21223 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_FRAME); | 
|---|
| 21224 | if (!hdr) | 
|---|
| 21225 | goto nla_put_failure; | 
|---|
| 21226 |  | 
|---|
| 21227 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 21228 | (freq && | 
|---|
| 21229 | (nla_put_u32(skb: msg, NL80211_ATTR_WIPHY_FREQ, | 
|---|
| 21230 | KHZ_TO_MHZ(freq)) || | 
|---|
| 21231 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY_FREQ_OFFSET, | 
|---|
| 21232 | value: freq % 1000))) || | 
|---|
| 21233 | (sig_dbm && | 
|---|
| 21234 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_RX_SIGNAL_DBM, value: sig_dbm)) || | 
|---|
| 21235 | nla_put(skb: msg, NL80211_ATTR_FRAME, attrlen: len, data: frame)) | 
|---|
| 21236 | goto nla_put_failure; | 
|---|
| 21237 |  | 
|---|
| 21238 | genlmsg_end(skb: msg, hdr); | 
|---|
| 21239 |  | 
|---|
| 21240 | genlmsg_unicast(net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: reg->nlportid); | 
|---|
| 21241 | } | 
|---|
| 21242 | spin_unlock_bh(lock: &rdev->beacon_registrations_lock); | 
|---|
| 21243 | return; | 
|---|
| 21244 |  | 
|---|
| 21245 | nla_put_failure: | 
|---|
| 21246 | spin_unlock_bh(lock: &rdev->beacon_registrations_lock); | 
|---|
| 21247 | nlmsg_free(skb: msg); | 
|---|
| 21248 | } | 
|---|
| 21249 | EXPORT_SYMBOL(cfg80211_report_obss_beacon_khz); | 
|---|
| 21250 |  | 
|---|
| 21251 | #ifdef CONFIG_PM | 
|---|
| 21252 | static int cfg80211_net_detect_results(struct sk_buff *msg, | 
|---|
| 21253 | struct cfg80211_wowlan_wakeup *wakeup) | 
|---|
| 21254 | { | 
|---|
| 21255 | struct cfg80211_wowlan_nd_info *nd = wakeup->net_detect; | 
|---|
| 21256 | struct nlattr *nl_results, *nl_match, *nl_freqs; | 
|---|
| 21257 | int i, j; | 
|---|
| 21258 |  | 
|---|
| 21259 | nl_results = nla_nest_start_noflag(skb: msg, | 
|---|
| 21260 | attrtype: NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS); | 
|---|
| 21261 | if (!nl_results) | 
|---|
| 21262 | return -EMSGSIZE; | 
|---|
| 21263 |  | 
|---|
| 21264 | for (i = 0; i < nd->n_matches; i++) { | 
|---|
| 21265 | struct cfg80211_wowlan_nd_match *match = nd->matches[i]; | 
|---|
| 21266 |  | 
|---|
| 21267 | nl_match = nla_nest_start_noflag(skb: msg, attrtype: i); | 
|---|
| 21268 | if (!nl_match) | 
|---|
| 21269 | break; | 
|---|
| 21270 |  | 
|---|
| 21271 | /* The SSID attribute is optional in nl80211, but for | 
|---|
| 21272 | * simplicity reasons it's always present in the | 
|---|
| 21273 | * cfg80211 structure.  If a driver can't pass the | 
|---|
| 21274 | * SSID, that needs to be changed.  A zero length SSID | 
|---|
| 21275 | * is still a valid SSID (wildcard), so it cannot be | 
|---|
| 21276 | * used for this purpose. | 
|---|
| 21277 | */ | 
|---|
| 21278 | if (nla_put(skb: msg, NL80211_ATTR_SSID, attrlen: match->ssid.ssid_len, | 
|---|
| 21279 | data: match->ssid.ssid)) { | 
|---|
| 21280 | nla_nest_cancel(skb: msg, start: nl_match); | 
|---|
| 21281 | goto out; | 
|---|
| 21282 | } | 
|---|
| 21283 |  | 
|---|
| 21284 | if (match->n_channels) { | 
|---|
| 21285 | nl_freqs = nla_nest_start_noflag(skb: msg, | 
|---|
| 21286 | attrtype: NL80211_ATTR_SCAN_FREQUENCIES); | 
|---|
| 21287 | if (!nl_freqs) { | 
|---|
| 21288 | nla_nest_cancel(skb: msg, start: nl_match); | 
|---|
| 21289 | goto out; | 
|---|
| 21290 | } | 
|---|
| 21291 |  | 
|---|
| 21292 | for (j = 0; j < match->n_channels; j++) { | 
|---|
| 21293 | if (nla_put_u32(skb: msg, attrtype: j, value: match->channels[j])) { | 
|---|
| 21294 | nla_nest_cancel(skb: msg, start: nl_freqs); | 
|---|
| 21295 | nla_nest_cancel(skb: msg, start: nl_match); | 
|---|
| 21296 | goto out; | 
|---|
| 21297 | } | 
|---|
| 21298 | } | 
|---|
| 21299 |  | 
|---|
| 21300 | nla_nest_end(skb: msg, start: nl_freqs); | 
|---|
| 21301 | } | 
|---|
| 21302 |  | 
|---|
| 21303 | nla_nest_end(skb: msg, start: nl_match); | 
|---|
| 21304 | } | 
|---|
| 21305 |  | 
|---|
| 21306 | out: | 
|---|
| 21307 | nla_nest_end(skb: msg, start: nl_results); | 
|---|
| 21308 | return 0; | 
|---|
| 21309 | } | 
|---|
| 21310 |  | 
|---|
| 21311 | void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev, | 
|---|
| 21312 | struct cfg80211_wowlan_wakeup *wakeup, | 
|---|
| 21313 | gfp_t gfp) | 
|---|
| 21314 | { | 
|---|
| 21315 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); | 
|---|
| 21316 | struct sk_buff *msg; | 
|---|
| 21317 | void *hdr; | 
|---|
| 21318 | int size = 200; | 
|---|
| 21319 |  | 
|---|
| 21320 | trace_cfg80211_report_wowlan_wakeup(wiphy: wdev->wiphy, wdev, wakeup); | 
|---|
| 21321 |  | 
|---|
| 21322 | if (wakeup) | 
|---|
| 21323 | size += wakeup->packet_present_len; | 
|---|
| 21324 |  | 
|---|
| 21325 | msg = nlmsg_new(payload: size, flags: gfp); | 
|---|
| 21326 | if (!msg) | 
|---|
| 21327 | return; | 
|---|
| 21328 |  | 
|---|
| 21329 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_SET_WOWLAN); | 
|---|
| 21330 | if (!hdr) | 
|---|
| 21331 | goto free_msg; | 
|---|
| 21332 |  | 
|---|
| 21333 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 21334 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), | 
|---|
| 21335 | padattr: NL80211_ATTR_PAD)) | 
|---|
| 21336 | goto free_msg; | 
|---|
| 21337 |  | 
|---|
| 21338 | if (wdev->netdev && nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, | 
|---|
| 21339 | value: wdev->netdev->ifindex)) | 
|---|
| 21340 | goto free_msg; | 
|---|
| 21341 |  | 
|---|
| 21342 | if (wakeup) { | 
|---|
| 21343 | struct nlattr *reasons; | 
|---|
| 21344 |  | 
|---|
| 21345 | reasons = nla_nest_start_noflag(skb: msg, | 
|---|
| 21346 | attrtype: NL80211_ATTR_WOWLAN_TRIGGERS); | 
|---|
| 21347 | if (!reasons) | 
|---|
| 21348 | goto free_msg; | 
|---|
| 21349 |  | 
|---|
| 21350 | if (wakeup->disconnect && | 
|---|
| 21351 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_DISCONNECT)) | 
|---|
| 21352 | goto free_msg; | 
|---|
| 21353 | if (wakeup->magic_pkt && | 
|---|
| 21354 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_MAGIC_PKT)) | 
|---|
| 21355 | goto free_msg; | 
|---|
| 21356 | if (wakeup->gtk_rekey_failure && | 
|---|
| 21357 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) | 
|---|
| 21358 | goto free_msg; | 
|---|
| 21359 | if (wakeup->eap_identity_req && | 
|---|
| 21360 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) | 
|---|
| 21361 | goto free_msg; | 
|---|
| 21362 | if (wakeup->four_way_handshake && | 
|---|
| 21363 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) | 
|---|
| 21364 | goto free_msg; | 
|---|
| 21365 | if (wakeup->rfkill_release && | 
|---|
| 21366 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_RFKILL_RELEASE)) | 
|---|
| 21367 | goto free_msg; | 
|---|
| 21368 |  | 
|---|
| 21369 | if (wakeup->pattern_idx >= 0 && | 
|---|
| 21370 | nla_put_u32(skb: msg, attrtype: NL80211_WOWLAN_TRIG_PKT_PATTERN, | 
|---|
| 21371 | value: wakeup->pattern_idx)) | 
|---|
| 21372 | goto free_msg; | 
|---|
| 21373 |  | 
|---|
| 21374 | if (wakeup->tcp_match && | 
|---|
| 21375 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH)) | 
|---|
| 21376 | goto free_msg; | 
|---|
| 21377 |  | 
|---|
| 21378 | if (wakeup->tcp_connlost && | 
|---|
| 21379 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST)) | 
|---|
| 21380 | goto free_msg; | 
|---|
| 21381 |  | 
|---|
| 21382 | if (wakeup->tcp_nomoretokens && | 
|---|
| 21383 | nla_put_flag(skb: msg, | 
|---|
| 21384 | attrtype: NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS)) | 
|---|
| 21385 | goto free_msg; | 
|---|
| 21386 |  | 
|---|
| 21387 | if (wakeup->unprot_deauth_disassoc && | 
|---|
| 21388 | nla_put_flag(skb: msg, | 
|---|
| 21389 | attrtype: NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC)) | 
|---|
| 21390 | goto free_msg; | 
|---|
| 21391 |  | 
|---|
| 21392 | if (wakeup->packet) { | 
|---|
| 21393 | u32 pkt_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211; | 
|---|
| 21394 | u32 len_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN; | 
|---|
| 21395 |  | 
|---|
| 21396 | if (!wakeup->packet_80211) { | 
|---|
| 21397 | pkt_attr = | 
|---|
| 21398 | NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023; | 
|---|
| 21399 | len_attr = | 
|---|
| 21400 | NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN; | 
|---|
| 21401 | } | 
|---|
| 21402 |  | 
|---|
| 21403 | if (wakeup->packet_len && | 
|---|
| 21404 | nla_put_u32(skb: msg, attrtype: len_attr, value: wakeup->packet_len)) | 
|---|
| 21405 | goto free_msg; | 
|---|
| 21406 |  | 
|---|
| 21407 | if (nla_put(skb: msg, attrtype: pkt_attr, attrlen: wakeup->packet_present_len, | 
|---|
| 21408 | data: wakeup->packet)) | 
|---|
| 21409 | goto free_msg; | 
|---|
| 21410 | } | 
|---|
| 21411 |  | 
|---|
| 21412 | if (wakeup->net_detect && | 
|---|
| 21413 | cfg80211_net_detect_results(msg, wakeup)) | 
|---|
| 21414 | goto free_msg; | 
|---|
| 21415 |  | 
|---|
| 21416 | nla_nest_end(skb: msg, start: reasons); | 
|---|
| 21417 | } | 
|---|
| 21418 |  | 
|---|
| 21419 | genlmsg_end(skb: msg, hdr); | 
|---|
| 21420 |  | 
|---|
| 21421 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 21422 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 21423 | return; | 
|---|
| 21424 |  | 
|---|
| 21425 | free_msg: | 
|---|
| 21426 | nlmsg_free(skb: msg); | 
|---|
| 21427 | } | 
|---|
| 21428 | EXPORT_SYMBOL(cfg80211_report_wowlan_wakeup); | 
|---|
| 21429 | #endif | 
|---|
| 21430 |  | 
|---|
| 21431 | void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer, | 
|---|
| 21432 | enum nl80211_tdls_operation oper, | 
|---|
| 21433 | u16 reason_code, gfp_t gfp) | 
|---|
| 21434 | { | 
|---|
| 21435 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 21436 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); | 
|---|
| 21437 | struct sk_buff *msg; | 
|---|
| 21438 | void *hdr; | 
|---|
| 21439 |  | 
|---|
| 21440 | trace_cfg80211_tdls_oper_request(wiphy: wdev->wiphy, netdev: dev, peer, oper, | 
|---|
| 21441 | reason_code); | 
|---|
| 21442 |  | 
|---|
| 21443 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 21444 | if (!msg) | 
|---|
| 21445 | return; | 
|---|
| 21446 |  | 
|---|
| 21447 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_TDLS_OPER); | 
|---|
| 21448 | if (!hdr) { | 
|---|
| 21449 | nlmsg_free(skb: msg); | 
|---|
| 21450 | return; | 
|---|
| 21451 | } | 
|---|
| 21452 |  | 
|---|
| 21453 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 21454 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || | 
|---|
| 21455 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_TDLS_OPERATION, value: oper) || | 
|---|
| 21456 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: peer) || | 
|---|
| 21457 | (reason_code > 0 && | 
|---|
| 21458 | nla_put_u16(skb: msg, NL80211_ATTR_REASON_CODE, value: reason_code))) | 
|---|
| 21459 | goto nla_put_failure; | 
|---|
| 21460 |  | 
|---|
| 21461 | genlmsg_end(skb: msg, hdr); | 
|---|
| 21462 |  | 
|---|
| 21463 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 21464 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 21465 | return; | 
|---|
| 21466 |  | 
|---|
| 21467 | nla_put_failure: | 
|---|
| 21468 | nlmsg_free(skb: msg); | 
|---|
| 21469 | } | 
|---|
| 21470 | EXPORT_SYMBOL(cfg80211_tdls_oper_request); | 
|---|
| 21471 |  | 
|---|
| 21472 | static int nl80211_netlink_notify(struct notifier_block * nb, | 
|---|
| 21473 | unsigned long state, | 
|---|
| 21474 | void *_notify) | 
|---|
| 21475 | { | 
|---|
| 21476 | struct netlink_notify *notify = _notify; | 
|---|
| 21477 | struct cfg80211_registered_device *rdev; | 
|---|
| 21478 | struct wireless_dev *wdev; | 
|---|
| 21479 | struct cfg80211_beacon_registration *reg, *tmp; | 
|---|
| 21480 |  | 
|---|
| 21481 | if (state != NETLINK_URELEASE || notify->protocol != NETLINK_GENERIC) | 
|---|
| 21482 | return NOTIFY_DONE; | 
|---|
| 21483 |  | 
|---|
| 21484 | rcu_read_lock(); | 
|---|
| 21485 |  | 
|---|
| 21486 | list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { | 
|---|
| 21487 | struct cfg80211_sched_scan_request *sched_scan_req; | 
|---|
| 21488 |  | 
|---|
| 21489 | list_for_each_entry_rcu(sched_scan_req, | 
|---|
| 21490 | &rdev->sched_scan_req_list, | 
|---|
| 21491 | list) { | 
|---|
| 21492 | if (sched_scan_req->owner_nlportid == notify->portid) { | 
|---|
| 21493 | sched_scan_req->nl_owner_dead = true; | 
|---|
| 21494 | wiphy_work_queue(wiphy: &rdev->wiphy, | 
|---|
| 21495 | work: &rdev->sched_scan_stop_wk); | 
|---|
| 21496 | } | 
|---|
| 21497 | } | 
|---|
| 21498 |  | 
|---|
| 21499 | list_for_each_entry_rcu(wdev, &rdev->wiphy.wdev_list, list) { | 
|---|
| 21500 | cfg80211_mlme_unregister_socket(wdev, nlpid: notify->portid); | 
|---|
| 21501 |  | 
|---|
| 21502 | if (wdev->owner_nlportid == notify->portid) { | 
|---|
| 21503 | wdev->nl_owner_dead = true; | 
|---|
| 21504 | schedule_work(work: &rdev->destroy_work); | 
|---|
| 21505 | } else if (wdev->conn_owner_nlportid == notify->portid) { | 
|---|
| 21506 | schedule_work(work: &wdev->disconnect_wk); | 
|---|
| 21507 | } | 
|---|
| 21508 |  | 
|---|
| 21509 | cfg80211_release_pmsr(wdev, portid: notify->portid); | 
|---|
| 21510 | } | 
|---|
| 21511 |  | 
|---|
| 21512 | spin_lock_bh(lock: &rdev->beacon_registrations_lock); | 
|---|
| 21513 | list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations, | 
|---|
| 21514 | list) { | 
|---|
| 21515 | if (reg->nlportid == notify->portid) { | 
|---|
| 21516 | list_del(entry: ®->list); | 
|---|
| 21517 | kfree(objp: reg); | 
|---|
| 21518 | break; | 
|---|
| 21519 | } | 
|---|
| 21520 | } | 
|---|
| 21521 | spin_unlock_bh(lock: &rdev->beacon_registrations_lock); | 
|---|
| 21522 | } | 
|---|
| 21523 |  | 
|---|
| 21524 | rcu_read_unlock(); | 
|---|
| 21525 |  | 
|---|
| 21526 | /* | 
|---|
| 21527 | * It is possible that the user space process that is controlling the | 
|---|
| 21528 | * indoor setting disappeared, so notify the regulatory core. | 
|---|
| 21529 | */ | 
|---|
| 21530 | regulatory_netlink_notify(portid: notify->portid); | 
|---|
| 21531 | return NOTIFY_OK; | 
|---|
| 21532 | } | 
|---|
| 21533 |  | 
|---|
| 21534 | static struct notifier_block nl80211_netlink_notifier = { | 
|---|
| 21535 | .notifier_call = nl80211_netlink_notify, | 
|---|
| 21536 | }; | 
|---|
| 21537 |  | 
|---|
| 21538 | void cfg80211_ft_event(struct net_device *netdev, | 
|---|
| 21539 | struct cfg80211_ft_event_params *ft_event) | 
|---|
| 21540 | { | 
|---|
| 21541 | struct wiphy *wiphy = netdev->ieee80211_ptr->wiphy; | 
|---|
| 21542 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 21543 | struct sk_buff *msg; | 
|---|
| 21544 | void *hdr; | 
|---|
| 21545 |  | 
|---|
| 21546 | trace_cfg80211_ft_event(wiphy, netdev, ft_event); | 
|---|
| 21547 |  | 
|---|
| 21548 | if (!ft_event->target_ap) | 
|---|
| 21549 | return; | 
|---|
| 21550 |  | 
|---|
| 21551 | msg = nlmsg_new(payload: 100 + ft_event->ies_len + ft_event->ric_ies_len, | 
|---|
| 21552 | GFP_KERNEL); | 
|---|
| 21553 | if (!msg) | 
|---|
| 21554 | return; | 
|---|
| 21555 |  | 
|---|
| 21556 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_FT_EVENT); | 
|---|
| 21557 | if (!hdr) | 
|---|
| 21558 | goto out; | 
|---|
| 21559 |  | 
|---|
| 21560 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 21561 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || | 
|---|
| 21562 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: ft_event->target_ap)) | 
|---|
| 21563 | goto out; | 
|---|
| 21564 |  | 
|---|
| 21565 | if (ft_event->ies && | 
|---|
| 21566 | nla_put(skb: msg, NL80211_ATTR_IE, attrlen: ft_event->ies_len, data: ft_event->ies)) | 
|---|
| 21567 | goto out; | 
|---|
| 21568 | if (ft_event->ric_ies && | 
|---|
| 21569 | nla_put(skb: msg, attrtype: NL80211_ATTR_IE_RIC, attrlen: ft_event->ric_ies_len, | 
|---|
| 21570 | data: ft_event->ric_ies)) | 
|---|
| 21571 | goto out; | 
|---|
| 21572 |  | 
|---|
| 21573 | genlmsg_end(skb: msg, hdr); | 
|---|
| 21574 |  | 
|---|
| 21575 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 21576 | group: NL80211_MCGRP_MLME, GFP_KERNEL); | 
|---|
| 21577 | return; | 
|---|
| 21578 | out: | 
|---|
| 21579 | nlmsg_free(skb: msg); | 
|---|
| 21580 | } | 
|---|
| 21581 | EXPORT_SYMBOL(cfg80211_ft_event); | 
|---|
| 21582 |  | 
|---|
| 21583 | void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp) | 
|---|
| 21584 | { | 
|---|
| 21585 | struct cfg80211_registered_device *rdev; | 
|---|
| 21586 | struct sk_buff *msg; | 
|---|
| 21587 | void *hdr; | 
|---|
| 21588 | u32 nlportid; | 
|---|
| 21589 |  | 
|---|
| 21590 | rdev = wiphy_to_rdev(wiphy: wdev->wiphy); | 
|---|
| 21591 | if (!rdev->crit_proto_nlportid) | 
|---|
| 21592 | return; | 
|---|
| 21593 |  | 
|---|
| 21594 | nlportid = rdev->crit_proto_nlportid; | 
|---|
| 21595 | rdev->crit_proto_nlportid = 0; | 
|---|
| 21596 |  | 
|---|
| 21597 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 21598 | if (!msg) | 
|---|
| 21599 | return; | 
|---|
| 21600 |  | 
|---|
| 21601 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_CRIT_PROTOCOL_STOP); | 
|---|
| 21602 | if (!hdr) | 
|---|
| 21603 | goto nla_put_failure; | 
|---|
| 21604 |  | 
|---|
| 21605 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 21606 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), | 
|---|
| 21607 | padattr: NL80211_ATTR_PAD)) | 
|---|
| 21608 | goto nla_put_failure; | 
|---|
| 21609 |  | 
|---|
| 21610 | genlmsg_end(skb: msg, hdr); | 
|---|
| 21611 |  | 
|---|
| 21612 | genlmsg_unicast(net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: nlportid); | 
|---|
| 21613 | return; | 
|---|
| 21614 |  | 
|---|
| 21615 | nla_put_failure: | 
|---|
| 21616 | nlmsg_free(skb: msg); | 
|---|
| 21617 | } | 
|---|
| 21618 | EXPORT_SYMBOL(cfg80211_crit_proto_stopped); | 
|---|
| 21619 |  | 
|---|
| 21620 | void nl80211_send_ap_stopped(struct wireless_dev *wdev, unsigned int link_id) | 
|---|
| 21621 | { | 
|---|
| 21622 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 21623 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 21624 | struct sk_buff *msg; | 
|---|
| 21625 | void *hdr; | 
|---|
| 21626 |  | 
|---|
| 21627 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 21628 | if (!msg) | 
|---|
| 21629 | return; | 
|---|
| 21630 |  | 
|---|
| 21631 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_STOP_AP); | 
|---|
| 21632 | if (!hdr) | 
|---|
| 21633 | goto out; | 
|---|
| 21634 |  | 
|---|
| 21635 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 21636 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: wdev->netdev->ifindex) || | 
|---|
| 21637 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), | 
|---|
| 21638 | padattr: NL80211_ATTR_PAD) || | 
|---|
| 21639 | (wdev->valid_links && | 
|---|
| 21640 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: link_id))) | 
|---|
| 21641 | goto out; | 
|---|
| 21642 |  | 
|---|
| 21643 | genlmsg_end(skb: msg, hdr); | 
|---|
| 21644 |  | 
|---|
| 21645 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy), skb: msg, portid: 0, | 
|---|
| 21646 | group: NL80211_MCGRP_MLME, GFP_KERNEL); | 
|---|
| 21647 | return; | 
|---|
| 21648 | out: | 
|---|
| 21649 | nlmsg_free(skb: msg); | 
|---|
| 21650 | } | 
|---|
| 21651 |  | 
|---|
| 21652 | int cfg80211_external_auth_request(struct net_device *dev, | 
|---|
| 21653 | struct cfg80211_external_auth_params *params, | 
|---|
| 21654 | gfp_t gfp) | 
|---|
| 21655 | { | 
|---|
| 21656 | struct wireless_dev *wdev = dev->ieee80211_ptr; | 
|---|
| 21657 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); | 
|---|
| 21658 | struct sk_buff *msg; | 
|---|
| 21659 | void *hdr; | 
|---|
| 21660 |  | 
|---|
| 21661 | if (!wdev->conn_owner_nlportid) | 
|---|
| 21662 | return -EINVAL; | 
|---|
| 21663 |  | 
|---|
| 21664 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 21665 | if (!msg) | 
|---|
| 21666 | return -ENOMEM; | 
|---|
| 21667 |  | 
|---|
| 21668 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_EXTERNAL_AUTH); | 
|---|
| 21669 | if (!hdr) | 
|---|
| 21670 | goto nla_put_failure; | 
|---|
| 21671 |  | 
|---|
| 21672 | /* Some historical mistakes in drivers <-> userspace interface (notably | 
|---|
| 21673 | * between drivers and wpa_supplicant) led to a big-endian conversion | 
|---|
| 21674 | * being needed on NL80211_ATTR_AKM_SUITES _only_ when its value is | 
|---|
| 21675 | * WLAN_AKM_SUITE_SAE. This is now fixed on userspace side, but for the | 
|---|
| 21676 | * benefit of older wpa_supplicant versions, send this particular value | 
|---|
| 21677 | * in big-endian. Note that newer wpa_supplicant will also detect this | 
|---|
| 21678 | * particular value in big endian still, so it all continues to work. | 
|---|
| 21679 | */ | 
|---|
| 21680 | if (params->key_mgmt_suite == WLAN_AKM_SUITE_SAE) { | 
|---|
| 21681 | if (nla_put_be32(skb: msg, NL80211_ATTR_AKM_SUITES, | 
|---|
| 21682 | cpu_to_be32(WLAN_AKM_SUITE_SAE))) | 
|---|
| 21683 | goto nla_put_failure; | 
|---|
| 21684 | } else { | 
|---|
| 21685 | if (nla_put_u32(skb: msg, NL80211_ATTR_AKM_SUITES, | 
|---|
| 21686 | value: params->key_mgmt_suite)) | 
|---|
| 21687 | goto nla_put_failure; | 
|---|
| 21688 | } | 
|---|
| 21689 |  | 
|---|
| 21690 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 21691 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || | 
|---|
| 21692 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_EXTERNAL_AUTH_ACTION, | 
|---|
| 21693 | value: params->action) || | 
|---|
| 21694 | nla_put(skb: msg, attrtype: NL80211_ATTR_BSSID, ETH_ALEN, data: params->bssid) || | 
|---|
| 21695 | nla_put(skb: msg, NL80211_ATTR_SSID, attrlen: params->ssid.ssid_len, | 
|---|
| 21696 | data: params->ssid.ssid) || | 
|---|
| 21697 | (!is_zero_ether_addr(addr: params->mld_addr) && | 
|---|
| 21698 | nla_put(skb: msg, attrtype: NL80211_ATTR_MLD_ADDR, ETH_ALEN, data: params->mld_addr))) | 
|---|
| 21699 | goto nla_put_failure; | 
|---|
| 21700 |  | 
|---|
| 21701 | genlmsg_end(skb: msg, hdr); | 
|---|
| 21702 | genlmsg_unicast(net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, | 
|---|
| 21703 | portid: wdev->conn_owner_nlportid); | 
|---|
| 21704 | return 0; | 
|---|
| 21705 |  | 
|---|
| 21706 | nla_put_failure: | 
|---|
| 21707 | nlmsg_free(skb: msg); | 
|---|
| 21708 | return -ENOBUFS; | 
|---|
| 21709 | } | 
|---|
| 21710 | EXPORT_SYMBOL(cfg80211_external_auth_request); | 
|---|
| 21711 |  | 
|---|
| 21712 | void cfg80211_update_owe_info_event(struct net_device *netdev, | 
|---|
| 21713 | struct cfg80211_update_owe_info *owe_info, | 
|---|
| 21714 | gfp_t gfp) | 
|---|
| 21715 | { | 
|---|
| 21716 | struct wiphy *wiphy = netdev->ieee80211_ptr->wiphy; | 
|---|
| 21717 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 21718 | struct sk_buff *msg; | 
|---|
| 21719 | void *hdr; | 
|---|
| 21720 |  | 
|---|
| 21721 | trace_cfg80211_update_owe_info_event(wiphy, netdev, owe_info); | 
|---|
| 21722 |  | 
|---|
| 21723 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 21724 | if (!msg) | 
|---|
| 21725 | return; | 
|---|
| 21726 |  | 
|---|
| 21727 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_UPDATE_OWE_INFO); | 
|---|
| 21728 | if (!hdr) | 
|---|
| 21729 | goto nla_put_failure; | 
|---|
| 21730 |  | 
|---|
| 21731 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 21732 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || | 
|---|
| 21733 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: owe_info->peer)) | 
|---|
| 21734 | goto nla_put_failure; | 
|---|
| 21735 |  | 
|---|
| 21736 | if (!owe_info->ie_len || | 
|---|
| 21737 | nla_put(skb: msg, NL80211_ATTR_IE, attrlen: owe_info->ie_len, data: owe_info->ie)) | 
|---|
| 21738 | goto nla_put_failure; | 
|---|
| 21739 |  | 
|---|
| 21740 | if (owe_info->assoc_link_id != -1) { | 
|---|
| 21741 | if (nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, | 
|---|
| 21742 | value: owe_info->assoc_link_id)) | 
|---|
| 21743 | goto nla_put_failure; | 
|---|
| 21744 |  | 
|---|
| 21745 | if (!is_zero_ether_addr(addr: owe_info->peer_mld_addr) && | 
|---|
| 21746 | nla_put(skb: msg, attrtype: NL80211_ATTR_MLD_ADDR, ETH_ALEN, | 
|---|
| 21747 | data: owe_info->peer_mld_addr)) | 
|---|
| 21748 | goto nla_put_failure; | 
|---|
| 21749 | } | 
|---|
| 21750 |  | 
|---|
| 21751 | genlmsg_end(skb: msg, hdr); | 
|---|
| 21752 |  | 
|---|
| 21753 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 21754 | group: NL80211_MCGRP_MLME, flags: gfp); | 
|---|
| 21755 | return; | 
|---|
| 21756 |  | 
|---|
| 21757 | nla_put_failure: | 
|---|
| 21758 | genlmsg_cancel(skb: msg, hdr); | 
|---|
| 21759 | nlmsg_free(skb: msg); | 
|---|
| 21760 | } | 
|---|
| 21761 | EXPORT_SYMBOL(cfg80211_update_owe_info_event); | 
|---|
| 21762 |  | 
|---|
| 21763 | void cfg80211_schedule_channels_check(struct wireless_dev *wdev) | 
|---|
| 21764 | { | 
|---|
| 21765 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 21766 |  | 
|---|
| 21767 | /* Schedule channels check if NO_IR or DFS relaxations are supported */ | 
|---|
| 21768 | if (wdev->iftype == NL80211_IFTYPE_STATION && | 
|---|
| 21769 | (wiphy_ext_feature_isset(wiphy, | 
|---|
| 21770 | ftidx: NL80211_EXT_FEATURE_DFS_CONCURRENT) || | 
|---|
| 21771 | (IS_ENABLED(CONFIG_CFG80211_REG_RELAX_NO_IR) && | 
|---|
| 21772 | wiphy->regulatory_flags & REGULATORY_ENABLE_RELAX_NO_IR))) | 
|---|
| 21773 | reg_check_channels(); | 
|---|
| 21774 | } | 
|---|
| 21775 | EXPORT_SYMBOL(cfg80211_schedule_channels_check); | 
|---|
| 21776 |  | 
|---|
| 21777 | void cfg80211_epcs_changed(struct net_device *netdev, bool enabled) | 
|---|
| 21778 | { | 
|---|
| 21779 | struct wireless_dev *wdev = netdev->ieee80211_ptr; | 
|---|
| 21780 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 21781 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 21782 | struct sk_buff *msg; | 
|---|
| 21783 | void *hdr; | 
|---|
| 21784 |  | 
|---|
| 21785 | trace_cfg80211_epcs_changed(wdev, enabled); | 
|---|
| 21786 |  | 
|---|
| 21787 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 
|---|
| 21788 | if (!msg) | 
|---|
| 21789 | return; | 
|---|
| 21790 |  | 
|---|
| 21791 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_EPCS_CFG); | 
|---|
| 21792 | if (!hdr) { | 
|---|
| 21793 | nlmsg_free(skb: msg); | 
|---|
| 21794 | return; | 
|---|
| 21795 | } | 
|---|
| 21796 |  | 
|---|
| 21797 | if (enabled && nla_put_flag(skb: msg, attrtype: NL80211_ATTR_EPCS)) | 
|---|
| 21798 | goto nla_put_failure; | 
|---|
| 21799 |  | 
|---|
| 21800 | genlmsg_end(skb: msg, hdr); | 
|---|
| 21801 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, | 
|---|
| 21802 | group: NL80211_MCGRP_MLME, GFP_KERNEL); | 
|---|
| 21803 | return; | 
|---|
| 21804 |  | 
|---|
| 21805 | nla_put_failure: | 
|---|
| 21806 | nlmsg_free(skb: msg); | 
|---|
| 21807 | } | 
|---|
| 21808 | EXPORT_SYMBOL(cfg80211_epcs_changed); | 
|---|
| 21809 |  | 
|---|
| 21810 | void cfg80211_next_nan_dw_notif(struct wireless_dev *wdev, | 
|---|
| 21811 | struct ieee80211_channel *chan, gfp_t gfp) | 
|---|
| 21812 | { | 
|---|
| 21813 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 21814 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 21815 | struct sk_buff *msg; | 
|---|
| 21816 | void *hdr; | 
|---|
| 21817 |  | 
|---|
| 21818 | trace_cfg80211_next_nan_dw_notif(wdev, chan); | 
|---|
| 21819 |  | 
|---|
| 21820 | if (!wdev->owner_nlportid) | 
|---|
| 21821 | return; | 
|---|
| 21822 |  | 
|---|
| 21823 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 21824 | if (!msg) | 
|---|
| 21825 | return; | 
|---|
| 21826 |  | 
|---|
| 21827 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, | 
|---|
| 21828 | cmd: NL80211_CMD_NAN_NEXT_DW_NOTIFICATION); | 
|---|
| 21829 | if (!hdr) | 
|---|
| 21830 | goto nla_put_failure; | 
|---|
| 21831 |  | 
|---|
| 21832 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 21833 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), | 
|---|
| 21834 | padattr: NL80211_ATTR_PAD) || | 
|---|
| 21835 | nla_put_u32(skb: msg, NL80211_ATTR_WIPHY_FREQ, value: chan->center_freq)) | 
|---|
| 21836 | goto nla_put_failure; | 
|---|
| 21837 |  | 
|---|
| 21838 | genlmsg_end(skb: msg, hdr); | 
|---|
| 21839 |  | 
|---|
| 21840 | genlmsg_unicast(net: wiphy_net(wiphy), skb: msg, portid: wdev->owner_nlportid); | 
|---|
| 21841 |  | 
|---|
| 21842 | return; | 
|---|
| 21843 |  | 
|---|
| 21844 | nla_put_failure: | 
|---|
| 21845 | nlmsg_free(skb: msg); | 
|---|
| 21846 | } | 
|---|
| 21847 | EXPORT_SYMBOL(cfg80211_next_nan_dw_notif); | 
|---|
| 21848 |  | 
|---|
| 21849 | void cfg80211_nan_cluster_joined(struct wireless_dev *wdev, | 
|---|
| 21850 | const u8 *cluster_id, bool new_cluster, | 
|---|
| 21851 | gfp_t gfp) | 
|---|
| 21852 | { | 
|---|
| 21853 | struct wiphy *wiphy = wdev->wiphy; | 
|---|
| 21854 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); | 
|---|
| 21855 | struct sk_buff *msg; | 
|---|
| 21856 | void *hdr; | 
|---|
| 21857 |  | 
|---|
| 21858 | trace_cfg80211_nan_cluster_joined(wdev, cluster_id, new_cluster); | 
|---|
| 21859 |  | 
|---|
| 21860 | memcpy(to: wdev->u.nan.cluster_id, from: cluster_id, ETH_ALEN); | 
|---|
| 21861 |  | 
|---|
| 21862 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); | 
|---|
| 21863 | if (!msg) | 
|---|
| 21864 | return; | 
|---|
| 21865 |  | 
|---|
| 21866 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_NAN_CLUSTER_JOINED); | 
|---|
| 21867 | if (!hdr) | 
|---|
| 21868 | goto nla_put_failure; | 
|---|
| 21869 |  | 
|---|
| 21870 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || | 
|---|
| 21871 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), | 
|---|
| 21872 | padattr: NL80211_ATTR_PAD) || | 
|---|
| 21873 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: cluster_id) || | 
|---|
| 21874 | (new_cluster && nla_put_flag(skb: msg, attrtype: NL80211_ATTR_NAN_NEW_CLUSTER))) | 
|---|
| 21875 | goto nla_put_failure; | 
|---|
| 21876 |  | 
|---|
| 21877 | genlmsg_end(skb: msg, hdr); | 
|---|
| 21878 |  | 
|---|
| 21879 | if (!wdev->owner_nlportid) | 
|---|
| 21880 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy), | 
|---|
| 21881 | skb: msg, portid: 0, group: NL80211_MCGRP_NAN, flags: gfp); | 
|---|
| 21882 | else | 
|---|
| 21883 | genlmsg_unicast(net: wiphy_net(wiphy), skb: msg, | 
|---|
| 21884 | portid: wdev->owner_nlportid); | 
|---|
| 21885 | return; | 
|---|
| 21886 |  | 
|---|
| 21887 | nla_put_failure: | 
|---|
| 21888 | nlmsg_free(skb: msg); | 
|---|
| 21889 | } | 
|---|
| 21890 | EXPORT_SYMBOL(cfg80211_nan_cluster_joined); | 
|---|
| 21891 |  | 
|---|
| 21892 | /* initialisation/exit functions */ | 
|---|
| 21893 |  | 
|---|
| 21894 | int __init nl80211_init(void) | 
|---|
| 21895 | { | 
|---|
| 21896 | int err; | 
|---|
| 21897 |  | 
|---|
| 21898 | err = genl_register_family(family: &nl80211_fam); | 
|---|
| 21899 | if (err) | 
|---|
| 21900 | return err; | 
|---|
| 21901 |  | 
|---|
| 21902 | err = netlink_register_notifier(nb: &nl80211_netlink_notifier); | 
|---|
| 21903 | if (err) | 
|---|
| 21904 | goto err_out; | 
|---|
| 21905 |  | 
|---|
| 21906 | return 0; | 
|---|
| 21907 | err_out: | 
|---|
| 21908 | genl_unregister_family(family: &nl80211_fam); | 
|---|
| 21909 | return err; | 
|---|
| 21910 | } | 
|---|
| 21911 |  | 
|---|
| 21912 | void nl80211_exit(void) | 
|---|
| 21913 | { | 
|---|
| 21914 | netlink_unregister_notifier(nb: &nl80211_netlink_notifier); | 
|---|
| 21915 | genl_unregister_family(family: &nl80211_fam); | 
|---|
| 21916 | } | 
|---|
| 21917 |  | 
|---|