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-2024 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 | unsigned int fixedlen, hdrlen; |
233 | bool s1g_bcn; |
234 | |
235 | if (len < offsetofend(typeof(*mgmt), frame_control)) |
236 | goto err; |
237 | |
238 | s1g_bcn = ieee80211_is_s1g_beacon(fc: mgmt->frame_control); |
239 | if (s1g_bcn) { |
240 | fixedlen = offsetof(struct ieee80211_ext, |
241 | u.s1g_beacon.variable); |
242 | hdrlen = offsetof(struct ieee80211_ext, u.s1g_beacon); |
243 | } else { |
244 | fixedlen = offsetof(struct ieee80211_mgmt, |
245 | u.beacon.variable); |
246 | hdrlen = offsetof(struct ieee80211_mgmt, u.beacon); |
247 | } |
248 | |
249 | if (len < fixedlen) |
250 | goto err; |
251 | |
252 | if (ieee80211_hdrlen(fc: mgmt->frame_control) != hdrlen) |
253 | goto err; |
254 | |
255 | data += fixedlen; |
256 | len -= fixedlen; |
257 | |
258 | for_each_element(elem, data, len) { |
259 | /* nothing */ |
260 | } |
261 | |
262 | if (for_each_element_completed(element: elem, data, datalen: len)) |
263 | return 0; |
264 | |
265 | err: |
266 | NL_SET_ERR_MSG_ATTR(extack, attr, "malformed beacon head" ); |
267 | return -EINVAL; |
268 | } |
269 | |
270 | static int validate_ie_attr(const struct nlattr *attr, |
271 | struct netlink_ext_ack *extack) |
272 | { |
273 | const u8 *data = nla_data(nla: attr); |
274 | unsigned int len = nla_len(nla: attr); |
275 | const struct element *elem; |
276 | |
277 | for_each_element(elem, data, len) { |
278 | /* nothing */ |
279 | } |
280 | |
281 | if (for_each_element_completed(element: elem, data, datalen: len)) |
282 | return 0; |
283 | |
284 | NL_SET_ERR_MSG_ATTR(extack, attr, "malformed information elements" ); |
285 | return -EINVAL; |
286 | } |
287 | |
288 | static int validate_he_capa(const struct nlattr *attr, |
289 | struct netlink_ext_ack *extack) |
290 | { |
291 | if (!ieee80211_he_capa_size_ok(data: nla_data(nla: attr), len: nla_len(nla: attr))) |
292 | return -EINVAL; |
293 | |
294 | return 0; |
295 | } |
296 | |
297 | /* policy for the attributes */ |
298 | static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR]; |
299 | |
300 | static const struct nla_policy |
301 | nl80211_ftm_responder_policy[NL80211_FTM_RESP_ATTR_MAX + 1] = { |
302 | [NL80211_FTM_RESP_ATTR_ENABLED] = { .type = NLA_FLAG, }, |
303 | [NL80211_FTM_RESP_ATTR_LCI] = { .type = NLA_BINARY, |
304 | .len = U8_MAX }, |
305 | [NL80211_FTM_RESP_ATTR_CIVICLOC] = { .type = NLA_BINARY, |
306 | .len = U8_MAX }, |
307 | }; |
308 | |
309 | static const struct nla_policy |
310 | nl80211_pmsr_ftm_req_attr_policy[NL80211_PMSR_FTM_REQ_ATTR_MAX + 1] = { |
311 | [NL80211_PMSR_FTM_REQ_ATTR_ASAP] = { .type = NLA_FLAG }, |
312 | [NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE] = { .type = NLA_U32 }, |
313 | [NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP] = |
314 | NLA_POLICY_MAX(NLA_U8, 15), |
315 | [NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD] = { .type = NLA_U16 }, |
316 | [NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION] = |
317 | NLA_POLICY_MAX(NLA_U8, 15), |
318 | [NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST] = |
319 | NLA_POLICY_MAX(NLA_U8, 31), |
320 | [NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES] = { .type = NLA_U8 }, |
321 | [NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI] = { .type = NLA_FLAG }, |
322 | [NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC] = { .type = NLA_FLAG }, |
323 | [NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED] = { .type = NLA_FLAG }, |
324 | [NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED] = { .type = NLA_FLAG }, |
325 | [NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK] = { .type = NLA_FLAG }, |
326 | [NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR] = { .type = NLA_U8 }, |
327 | }; |
328 | |
329 | static const struct nla_policy |
330 | nl80211_pmsr_req_data_policy[NL80211_PMSR_TYPE_MAX + 1] = { |
331 | [NL80211_PMSR_TYPE_FTM] = |
332 | NLA_POLICY_NESTED(nl80211_pmsr_ftm_req_attr_policy), |
333 | }; |
334 | |
335 | static const struct nla_policy |
336 | nl80211_pmsr_req_attr_policy[NL80211_PMSR_REQ_ATTR_MAX + 1] = { |
337 | [NL80211_PMSR_REQ_ATTR_DATA] = |
338 | NLA_POLICY_NESTED(nl80211_pmsr_req_data_policy), |
339 | [NL80211_PMSR_REQ_ATTR_GET_AP_TSF] = { .type = NLA_FLAG }, |
340 | }; |
341 | |
342 | static const struct nla_policy |
343 | nl80211_pmsr_peer_attr_policy[NL80211_PMSR_PEER_ATTR_MAX + 1] = { |
344 | [NL80211_PMSR_PEER_ATTR_ADDR] = NLA_POLICY_ETH_ADDR, |
345 | [NL80211_PMSR_PEER_ATTR_CHAN] = NLA_POLICY_NESTED(nl80211_policy), |
346 | [NL80211_PMSR_PEER_ATTR_REQ] = |
347 | NLA_POLICY_NESTED(nl80211_pmsr_req_attr_policy), |
348 | [NL80211_PMSR_PEER_ATTR_RESP] = { .type = NLA_REJECT }, |
349 | }; |
350 | |
351 | static const struct nla_policy |
352 | nl80211_pmsr_attr_policy[NL80211_PMSR_ATTR_MAX + 1] = { |
353 | [NL80211_PMSR_ATTR_MAX_PEERS] = { .type = NLA_REJECT }, |
354 | [NL80211_PMSR_ATTR_REPORT_AP_TSF] = { .type = NLA_REJECT }, |
355 | [NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR] = { .type = NLA_REJECT }, |
356 | [NL80211_PMSR_ATTR_TYPE_CAPA] = { .type = NLA_REJECT }, |
357 | [NL80211_PMSR_ATTR_PEERS] = |
358 | NLA_POLICY_NESTED_ARRAY(nl80211_pmsr_peer_attr_policy), |
359 | }; |
360 | |
361 | static const struct nla_policy |
362 | he_obss_pd_policy[NL80211_HE_OBSS_PD_ATTR_MAX + 1] = { |
363 | [NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET] = |
364 | NLA_POLICY_RANGE(NLA_U8, 1, 20), |
365 | [NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET] = |
366 | NLA_POLICY_RANGE(NLA_U8, 1, 20), |
367 | [NL80211_HE_OBSS_PD_ATTR_NON_SRG_MAX_OFFSET] = |
368 | NLA_POLICY_RANGE(NLA_U8, 1, 20), |
369 | [NL80211_HE_OBSS_PD_ATTR_BSS_COLOR_BITMAP] = |
370 | NLA_POLICY_EXACT_LEN(8), |
371 | [NL80211_HE_OBSS_PD_ATTR_PARTIAL_BSSID_BITMAP] = |
372 | NLA_POLICY_EXACT_LEN(8), |
373 | [NL80211_HE_OBSS_PD_ATTR_SR_CTRL] = { .type = NLA_U8 }, |
374 | }; |
375 | |
376 | static const struct nla_policy |
377 | he_bss_color_policy[NL80211_HE_BSS_COLOR_ATTR_MAX + 1] = { |
378 | [NL80211_HE_BSS_COLOR_ATTR_COLOR] = NLA_POLICY_RANGE(NLA_U8, 1, 63), |
379 | [NL80211_HE_BSS_COLOR_ATTR_DISABLED] = { .type = NLA_FLAG }, |
380 | [NL80211_HE_BSS_COLOR_ATTR_PARTIAL] = { .type = NLA_FLAG }, |
381 | }; |
382 | |
383 | static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = { |
384 | [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY, |
385 | .len = NL80211_MAX_SUPP_RATES }, |
386 | [NL80211_TXRATE_HT] = { .type = NLA_BINARY, |
387 | .len = NL80211_MAX_SUPP_HT_RATES }, |
388 | [NL80211_TXRATE_VHT] = NLA_POLICY_EXACT_LEN_WARN(sizeof(struct nl80211_txrate_vht)), |
389 | [NL80211_TXRATE_GI] = { .type = NLA_U8 }, |
390 | [NL80211_TXRATE_HE] = NLA_POLICY_EXACT_LEN(sizeof(struct nl80211_txrate_he)), |
391 | [NL80211_TXRATE_HE_GI] = NLA_POLICY_RANGE(NLA_U8, |
392 | NL80211_RATE_INFO_HE_GI_0_8, |
393 | NL80211_RATE_INFO_HE_GI_3_2), |
394 | [NL80211_TXRATE_HE_LTF] = NLA_POLICY_RANGE(NLA_U8, |
395 | NL80211_RATE_INFO_HE_1XLTF, |
396 | NL80211_RATE_INFO_HE_4XLTF), |
397 | }; |
398 | |
399 | static const struct nla_policy |
400 | nl80211_tid_config_attr_policy[NL80211_TID_CONFIG_ATTR_MAX + 1] = { |
401 | [NL80211_TID_CONFIG_ATTR_VIF_SUPP] = { .type = NLA_U64 }, |
402 | [NL80211_TID_CONFIG_ATTR_PEER_SUPP] = { .type = NLA_U64 }, |
403 | [NL80211_TID_CONFIG_ATTR_OVERRIDE] = { .type = NLA_FLAG }, |
404 | [NL80211_TID_CONFIG_ATTR_TIDS] = NLA_POLICY_RANGE(NLA_U16, 1, 0xff), |
405 | [NL80211_TID_CONFIG_ATTR_NOACK] = |
406 | NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE), |
407 | [NL80211_TID_CONFIG_ATTR_RETRY_SHORT] = NLA_POLICY_MIN(NLA_U8, 1), |
408 | [NL80211_TID_CONFIG_ATTR_RETRY_LONG] = NLA_POLICY_MIN(NLA_U8, 1), |
409 | [NL80211_TID_CONFIG_ATTR_AMPDU_CTRL] = |
410 | NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE), |
411 | [NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL] = |
412 | NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE), |
413 | [NL80211_TID_CONFIG_ATTR_AMSDU_CTRL] = |
414 | NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE), |
415 | [NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE] = |
416 | NLA_POLICY_MAX(NLA_U8, NL80211_TX_RATE_FIXED), |
417 | [NL80211_TID_CONFIG_ATTR_TX_RATE] = |
418 | NLA_POLICY_NESTED(nl80211_txattr_policy), |
419 | }; |
420 | |
421 | static const struct nla_policy |
422 | nl80211_fils_discovery_policy[NL80211_FILS_DISCOVERY_ATTR_MAX + 1] = { |
423 | [NL80211_FILS_DISCOVERY_ATTR_INT_MIN] = NLA_POLICY_MAX(NLA_U32, 10000), |
424 | [NL80211_FILS_DISCOVERY_ATTR_INT_MAX] = NLA_POLICY_MAX(NLA_U32, 10000), |
425 | [NL80211_FILS_DISCOVERY_ATTR_TMPL] = |
426 | NLA_POLICY_RANGE(NLA_BINARY, |
427 | NL80211_FILS_DISCOVERY_TMPL_MIN_LEN, |
428 | IEEE80211_MAX_DATA_LEN), |
429 | }; |
430 | |
431 | static const struct nla_policy |
432 | nl80211_unsol_bcast_probe_resp_policy[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_MAX + 1] = { |
433 | [NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT] = NLA_POLICY_MAX(NLA_U32, 20), |
434 | [NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL] = { .type = NLA_BINARY, |
435 | .len = IEEE80211_MAX_DATA_LEN } |
436 | }; |
437 | |
438 | static const struct nla_policy |
439 | sar_specs_policy[NL80211_SAR_ATTR_SPECS_MAX + 1] = { |
440 | [NL80211_SAR_ATTR_SPECS_POWER] = { .type = NLA_S32 }, |
441 | [NL80211_SAR_ATTR_SPECS_RANGE_INDEX] = {.type = NLA_U32 }, |
442 | }; |
443 | |
444 | static const struct nla_policy |
445 | sar_policy[NL80211_SAR_ATTR_MAX + 1] = { |
446 | [NL80211_SAR_ATTR_TYPE] = NLA_POLICY_MAX(NLA_U32, NUM_NL80211_SAR_TYPE), |
447 | [NL80211_SAR_ATTR_SPECS] = NLA_POLICY_NESTED_ARRAY(sar_specs_policy), |
448 | }; |
449 | |
450 | static const struct nla_policy |
451 | nl80211_mbssid_config_policy[NL80211_MBSSID_CONFIG_ATTR_MAX + 1] = { |
452 | [NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES] = NLA_POLICY_MIN(NLA_U8, 2), |
453 | [NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY] = |
454 | NLA_POLICY_MIN(NLA_U8, 1), |
455 | [NL80211_MBSSID_CONFIG_ATTR_INDEX] = { .type = NLA_U8 }, |
456 | [NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX] = { .type = NLA_U32 }, |
457 | [NL80211_MBSSID_CONFIG_ATTR_EMA] = { .type = NLA_FLAG }, |
458 | }; |
459 | |
460 | static const struct nla_policy |
461 | nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] = { |
462 | [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 }, |
463 | [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 }, |
464 | }; |
465 | |
466 | static const struct netlink_range_validation nl80211_punct_bitmap_range = { |
467 | .min = 0, |
468 | .max = 0xffff, |
469 | }; |
470 | |
471 | static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = { |
472 | [0] = { .strict_start_type = NL80211_ATTR_HE_OBSS_PD }, |
473 | [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, |
474 | [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, |
475 | .len = 20-1 }, |
476 | [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED }, |
477 | |
478 | [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 }, |
479 | [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 }, |
480 | [NL80211_ATTR_WIPHY_EDMG_CHANNELS] = NLA_POLICY_RANGE(NLA_U8, |
481 | NL80211_EDMG_CHANNELS_MIN, |
482 | NL80211_EDMG_CHANNELS_MAX), |
483 | [NL80211_ATTR_WIPHY_EDMG_BW_CONFIG] = NLA_POLICY_RANGE(NLA_U8, |
484 | NL80211_EDMG_BW_CONFIG_MIN, |
485 | NL80211_EDMG_BW_CONFIG_MAX), |
486 | |
487 | [NL80211_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 }, |
488 | [NL80211_ATTR_CENTER_FREQ1] = { .type = NLA_U32 }, |
489 | [NL80211_ATTR_CENTER_FREQ1_OFFSET] = NLA_POLICY_RANGE(NLA_U32, 0, 999), |
490 | [NL80211_ATTR_CENTER_FREQ2] = { .type = NLA_U32 }, |
491 | |
492 | [NL80211_ATTR_WIPHY_RETRY_SHORT] = NLA_POLICY_MIN(NLA_U8, 1), |
493 | [NL80211_ATTR_WIPHY_RETRY_LONG] = NLA_POLICY_MIN(NLA_U8, 1), |
494 | [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, |
495 | [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 }, |
496 | [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 }, |
497 | [NL80211_ATTR_WIPHY_DYN_ACK] = { .type = NLA_FLAG }, |
498 | |
499 | [NL80211_ATTR_IFTYPE] = NLA_POLICY_MAX(NLA_U32, NL80211_IFTYPE_MAX), |
500 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, |
501 | [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 }, |
502 | |
503 | [NL80211_ATTR_MAC] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), |
504 | [NL80211_ATTR_PREV_BSSID] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), |
505 | |
506 | [NL80211_ATTR_KEY] = { .type = NLA_NESTED, }, |
507 | [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY, |
508 | .len = WLAN_MAX_KEY_LEN }, |
509 | [NL80211_ATTR_KEY_IDX] = NLA_POLICY_MAX(NLA_U8, 7), |
510 | [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 }, |
511 | [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG }, |
512 | [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 }, |
513 | [NL80211_ATTR_KEY_TYPE] = |
514 | NLA_POLICY_MAX(NLA_U32, NUM_NL80211_KEYTYPES), |
515 | |
516 | [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 }, |
517 | [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 }, |
518 | [NL80211_ATTR_BEACON_HEAD] = |
519 | NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_beacon_head, |
520 | IEEE80211_MAX_DATA_LEN), |
521 | [NL80211_ATTR_BEACON_TAIL] = |
522 | NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_ie_attr, |
523 | IEEE80211_MAX_DATA_LEN), |
524 | [NL80211_ATTR_STA_AID] = |
525 | NLA_POLICY_RANGE(NLA_U16, 1, IEEE80211_MAX_AID), |
526 | [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED }, |
527 | [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 }, |
528 | [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY, |
529 | .len = NL80211_MAX_SUPP_RATES }, |
530 | [NL80211_ATTR_STA_PLINK_ACTION] = |
531 | NLA_POLICY_MAX(NLA_U8, NUM_NL80211_PLINK_ACTIONS - 1), |
532 | [NL80211_ATTR_STA_TX_POWER_SETTING] = |
533 | NLA_POLICY_RANGE(NLA_U8, |
534 | NL80211_TX_POWER_AUTOMATIC, |
535 | NL80211_TX_POWER_FIXED), |
536 | [NL80211_ATTR_STA_TX_POWER] = { .type = NLA_S16 }, |
537 | [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 }, |
538 | [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ }, |
539 | [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY, |
540 | .len = IEEE80211_MAX_MESH_ID_LEN }, |
541 | [NL80211_ATTR_MPATH_NEXT_HOP] = NLA_POLICY_ETH_ADDR_COMPAT, |
542 | |
543 | /* allow 3 for NUL-termination, we used to declare this NLA_STRING */ |
544 | [NL80211_ATTR_REG_ALPHA2] = NLA_POLICY_RANGE(NLA_BINARY, 2, 3), |
545 | [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED }, |
546 | |
547 | [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, |
548 | [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, |
549 | [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, |
550 | [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY, |
551 | .len = NL80211_MAX_SUPP_RATES }, |
552 | [NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 }, |
553 | |
554 | [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED }, |
555 | [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG }, |
556 | |
557 | [NL80211_ATTR_HT_CAPABILITY] = NLA_POLICY_EXACT_LEN_WARN(NL80211_HT_CAPABILITY_LEN), |
558 | |
559 | [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 }, |
560 | [NL80211_ATTR_IE] = NLA_POLICY_VALIDATE_FN(NLA_BINARY, |
561 | validate_ie_attr, |
562 | IEEE80211_MAX_DATA_LEN), |
563 | [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED }, |
564 | [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED }, |
565 | |
566 | [NL80211_ATTR_SSID] = { .type = NLA_BINARY, |
567 | .len = IEEE80211_MAX_SSID_LEN }, |
568 | [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 }, |
569 | [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 }, |
570 | [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG }, |
571 | [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG }, |
572 | [NL80211_ATTR_USE_MFP] = NLA_POLICY_RANGE(NLA_U32, |
573 | NL80211_MFP_NO, |
574 | NL80211_MFP_OPTIONAL), |
575 | [NL80211_ATTR_STA_FLAGS2] = |
576 | NLA_POLICY_EXACT_LEN_WARN(sizeof(struct nl80211_sta_flag_update)), |
577 | [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG }, |
578 | [NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 }, |
579 | [NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG }, |
580 | [NL80211_ATTR_CONTROL_PORT_OVER_NL80211] = { .type = NLA_FLAG }, |
581 | [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG }, |
582 | [NL80211_ATTR_STATUS_CODE] = { .type = NLA_U16 }, |
583 | [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, |
584 | [NL80211_ATTR_WPA_VERSIONS] = |
585 | NLA_POLICY_RANGE(NLA_U32, 0, |
586 | NL80211_WPA_VERSION_1 | |
587 | NL80211_WPA_VERSION_2 | |
588 | NL80211_WPA_VERSION_3), |
589 | [NL80211_ATTR_PID] = { .type = NLA_U32 }, |
590 | [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, |
591 | [NL80211_ATTR_PMKID] = NLA_POLICY_EXACT_LEN_WARN(WLAN_PMKID_LEN), |
592 | [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, |
593 | [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, |
594 | [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED }, |
595 | [NL80211_ATTR_FRAME] = { .type = NLA_BINARY, |
596 | .len = IEEE80211_MAX_DATA_LEN }, |
597 | [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, |
598 | [NL80211_ATTR_PS_STATE] = NLA_POLICY_RANGE(NLA_U32, |
599 | NL80211_PS_DISABLED, |
600 | NL80211_PS_ENABLED), |
601 | [NL80211_ATTR_CQM] = { .type = NLA_NESTED, }, |
602 | [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG }, |
603 | [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 }, |
604 | [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 }, |
605 | [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 }, |
606 | [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 }, |
607 | [NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 }, |
608 | [NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 }, |
609 | [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 }, |
610 | [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG }, |
611 | [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, |
612 | [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED }, |
613 | [NL80211_ATTR_STA_PLINK_STATE] = |
614 | NLA_POLICY_MAX(NLA_U8, NUM_NL80211_PLINK_STATES - 1), |
615 | [NL80211_ATTR_MEASUREMENT_DURATION] = { .type = NLA_U16 }, |
616 | [NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY] = { .type = NLA_FLAG }, |
617 | [NL80211_ATTR_MESH_PEER_AID] = |
618 | NLA_POLICY_RANGE(NLA_U16, 1, IEEE80211_MAX_AID), |
619 | [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 }, |
620 | [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED }, |
621 | [NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED }, |
622 | [NL80211_ATTR_HIDDEN_SSID] = |
623 | NLA_POLICY_RANGE(NLA_U32, |
624 | NL80211_HIDDEN_SSID_NOT_IN_USE, |
625 | NL80211_HIDDEN_SSID_ZERO_CONTENTS), |
626 | [NL80211_ATTR_IE_PROBE_RESP] = |
627 | NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_ie_attr, |
628 | IEEE80211_MAX_DATA_LEN), |
629 | [NL80211_ATTR_IE_ASSOC_RESP] = |
630 | NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_ie_attr, |
631 | IEEE80211_MAX_DATA_LEN), |
632 | [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG }, |
633 | [NL80211_ATTR_STA_WME] = NLA_POLICY_NESTED(nl80211_sta_wme_policy), |
634 | [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED }, |
635 | [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG }, |
636 | [NL80211_ATTR_TDLS_ACTION] = { .type = NLA_U8 }, |
637 | [NL80211_ATTR_TDLS_DIALOG_TOKEN] = { .type = NLA_U8 }, |
638 | [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 }, |
639 | [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG }, |
640 | [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG }, |
641 | [NL80211_ATTR_TDLS_INITIATOR] = { .type = NLA_FLAG }, |
642 | [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG }, |
643 | [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY, |
644 | .len = IEEE80211_MAX_DATA_LEN }, |
645 | [NL80211_ATTR_DFS_REGION] = { .type = NLA_U8 }, |
646 | [NL80211_ATTR_DISABLE_HT] = { .type = NLA_FLAG }, |
647 | [NL80211_ATTR_HT_CAPABILITY_MASK] = { |
648 | .len = NL80211_HT_CAPABILITY_LEN |
649 | }, |
650 | [NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 }, |
651 | [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 }, |
652 | [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 }, |
653 | [NL80211_ATTR_WDEV] = { .type = NLA_U64 }, |
654 | [NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 }, |
655 | |
656 | /* need to include at least Auth Transaction and Status Code */ |
657 | [NL80211_ATTR_AUTH_DATA] = NLA_POLICY_MIN_LEN(4), |
658 | |
659 | [NL80211_ATTR_VHT_CAPABILITY] = NLA_POLICY_EXACT_LEN_WARN(NL80211_VHT_CAPABILITY_LEN), |
660 | [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 }, |
661 | [NL80211_ATTR_P2P_CTWINDOW] = NLA_POLICY_MAX(NLA_U8, 127), |
662 | [NL80211_ATTR_P2P_OPPPS] = NLA_POLICY_MAX(NLA_U8, 1), |
663 | [NL80211_ATTR_LOCAL_MESH_POWER_MODE] = |
664 | NLA_POLICY_RANGE(NLA_U32, |
665 | NL80211_MESH_POWER_UNKNOWN + 1, |
666 | NL80211_MESH_POWER_MAX), |
667 | [NL80211_ATTR_ACL_POLICY] = {. type = NLA_U32 }, |
668 | [NL80211_ATTR_MAC_ADDRS] = { .type = NLA_NESTED }, |
669 | [NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 }, |
670 | [NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, }, |
671 | [NL80211_ATTR_SPLIT_WIPHY_DUMP] = { .type = NLA_FLAG, }, |
672 | [NL80211_ATTR_DISABLE_VHT] = { .type = NLA_FLAG }, |
673 | [NL80211_ATTR_VHT_CAPABILITY_MASK] = { |
674 | .len = NL80211_VHT_CAPABILITY_LEN, |
675 | }, |
676 | [NL80211_ATTR_MDID] = { .type = NLA_U16 }, |
677 | [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY, |
678 | .len = IEEE80211_MAX_DATA_LEN }, |
679 | [NL80211_ATTR_CRIT_PROT_ID] = { .type = NLA_U16 }, |
680 | [NL80211_ATTR_MAX_CRIT_PROT_DURATION] = |
681 | NLA_POLICY_MAX(NLA_U16, NL80211_CRIT_PROTO_MAX_DURATION), |
682 | [NL80211_ATTR_PEER_AID] = |
683 | NLA_POLICY_RANGE(NLA_U16, 1, IEEE80211_MAX_AID), |
684 | [NL80211_ATTR_CH_SWITCH_COUNT] = { .type = NLA_U32 }, |
685 | [NL80211_ATTR_CH_SWITCH_BLOCK_TX] = { .type = NLA_FLAG }, |
686 | [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED }, |
687 | [NL80211_ATTR_CNTDWN_OFFS_BEACON] = { .type = NLA_BINARY }, |
688 | [NL80211_ATTR_CNTDWN_OFFS_PRESP] = { .type = NLA_BINARY }, |
689 | [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = NLA_POLICY_MIN_LEN(2), |
690 | /* |
691 | * The value of the Length field of the Supported Operating |
692 | * Classes element is between 2 and 253. |
693 | */ |
694 | [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = |
695 | NLA_POLICY_RANGE(NLA_BINARY, 2, 253), |
696 | [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG }, |
697 | [NL80211_ATTR_OPMODE_NOTIF] = { .type = NLA_U8 }, |
698 | [NL80211_ATTR_VENDOR_ID] = { .type = NLA_U32 }, |
699 | [NL80211_ATTR_VENDOR_SUBCMD] = { .type = NLA_U32 }, |
700 | [NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY }, |
701 | [NL80211_ATTR_QOS_MAP] = NLA_POLICY_RANGE(NLA_BINARY, |
702 | IEEE80211_QOS_MAP_LEN_MIN, |
703 | IEEE80211_QOS_MAP_LEN_MAX), |
704 | [NL80211_ATTR_MAC_HINT] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), |
705 | [NL80211_ATTR_WIPHY_FREQ_HINT] = { .type = NLA_U32 }, |
706 | [NL80211_ATTR_TDLS_PEER_CAPABILITY] = { .type = NLA_U32 }, |
707 | [NL80211_ATTR_SOCKET_OWNER] = { .type = NLA_FLAG }, |
708 | [NL80211_ATTR_CSA_C_OFFSETS_TX] = { .type = NLA_BINARY }, |
709 | [NL80211_ATTR_USE_RRM] = { .type = NLA_FLAG }, |
710 | [NL80211_ATTR_TSID] = NLA_POLICY_MAX(NLA_U8, IEEE80211_NUM_TIDS - 1), |
711 | [NL80211_ATTR_USER_PRIO] = |
712 | NLA_POLICY_MAX(NLA_U8, IEEE80211_NUM_UPS - 1), |
713 | [NL80211_ATTR_ADMITTED_TIME] = { .type = NLA_U16 }, |
714 | [NL80211_ATTR_SMPS_MODE] = { .type = NLA_U8 }, |
715 | [NL80211_ATTR_OPER_CLASS] = { .type = NLA_U8 }, |
716 | [NL80211_ATTR_MAC_MASK] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), |
717 | [NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG }, |
718 | [NL80211_ATTR_NETNS_FD] = { .type = NLA_U32 }, |
719 | [NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 }, |
720 | [NL80211_ATTR_REG_INDOOR] = { .type = NLA_FLAG }, |
721 | [NL80211_ATTR_PBSS] = { .type = NLA_FLAG }, |
722 | [NL80211_ATTR_BSS_SELECT] = { .type = NLA_NESTED }, |
723 | [NL80211_ATTR_STA_SUPPORT_P2P_PS] = |
724 | NLA_POLICY_MAX(NLA_U8, NUM_NL80211_P2P_PS_STATUS - 1), |
725 | [NL80211_ATTR_MU_MIMO_GROUP_DATA] = { |
726 | .len = VHT_MUMIMO_GROUPS_DATA_LEN |
727 | }, |
728 | [NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), |
729 | [NL80211_ATTR_NAN_MASTER_PREF] = NLA_POLICY_MIN(NLA_U8, 1), |
730 | [NL80211_ATTR_BANDS] = { .type = NLA_U32 }, |
731 | [NL80211_ATTR_NAN_FUNC] = { .type = NLA_NESTED }, |
732 | [NL80211_ATTR_FILS_KEK] = { .type = NLA_BINARY, |
733 | .len = FILS_MAX_KEK_LEN }, |
734 | [NL80211_ATTR_FILS_NONCES] = NLA_POLICY_EXACT_LEN_WARN(2 * FILS_NONCE_LEN), |
735 | [NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED] = { .type = NLA_FLAG, }, |
736 | [NL80211_ATTR_BSSID] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), |
737 | [NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI] = { .type = NLA_S8 }, |
738 | [NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST] = { |
739 | .len = sizeof(struct nl80211_bss_select_rssi_adjust) |
740 | }, |
741 | [NL80211_ATTR_TIMEOUT_REASON] = { .type = NLA_U32 }, |
742 | [NL80211_ATTR_FILS_ERP_USERNAME] = { .type = NLA_BINARY, |
743 | .len = FILS_ERP_MAX_USERNAME_LEN }, |
744 | [NL80211_ATTR_FILS_ERP_REALM] = { .type = NLA_BINARY, |
745 | .len = FILS_ERP_MAX_REALM_LEN }, |
746 | [NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] = { .type = NLA_U16 }, |
747 | [NL80211_ATTR_FILS_ERP_RRK] = { .type = NLA_BINARY, |
748 | .len = FILS_ERP_MAX_RRK_LEN }, |
749 | [NL80211_ATTR_FILS_CACHE_ID] = NLA_POLICY_EXACT_LEN_WARN(2), |
750 | [NL80211_ATTR_PMK] = { .type = NLA_BINARY, .len = PMK_MAX_LEN }, |
751 | [NL80211_ATTR_PMKR0_NAME] = NLA_POLICY_EXACT_LEN(WLAN_PMK_NAME_LEN), |
752 | [NL80211_ATTR_SCHED_SCAN_MULTI] = { .type = NLA_FLAG }, |
753 | [NL80211_ATTR_EXTERNAL_AUTH_SUPPORT] = { .type = NLA_FLAG }, |
754 | |
755 | [NL80211_ATTR_TXQ_LIMIT] = { .type = NLA_U32 }, |
756 | [NL80211_ATTR_TXQ_MEMORY_LIMIT] = { .type = NLA_U32 }, |
757 | [NL80211_ATTR_TXQ_QUANTUM] = { .type = NLA_U32 }, |
758 | [NL80211_ATTR_HE_CAPABILITY] = |
759 | NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_he_capa, |
760 | NL80211_HE_MAX_CAPABILITY_LEN), |
761 | [NL80211_ATTR_FTM_RESPONDER] = |
762 | NLA_POLICY_NESTED(nl80211_ftm_responder_policy), |
763 | [NL80211_ATTR_TIMEOUT] = NLA_POLICY_MIN(NLA_U32, 1), |
764 | [NL80211_ATTR_PEER_MEASUREMENTS] = |
765 | NLA_POLICY_NESTED(nl80211_pmsr_attr_policy), |
766 | [NL80211_ATTR_AIRTIME_WEIGHT] = NLA_POLICY_MIN(NLA_U16, 1), |
767 | [NL80211_ATTR_SAE_PASSWORD] = { .type = NLA_BINARY, |
768 | .len = SAE_PASSWORD_MAX_LEN }, |
769 | [NL80211_ATTR_TWT_RESPONDER] = { .type = NLA_FLAG }, |
770 | [NL80211_ATTR_HE_OBSS_PD] = NLA_POLICY_NESTED(he_obss_pd_policy), |
771 | [NL80211_ATTR_VLAN_ID] = NLA_POLICY_RANGE(NLA_U16, 1, VLAN_N_VID - 2), |
772 | [NL80211_ATTR_HE_BSS_COLOR] = NLA_POLICY_NESTED(he_bss_color_policy), |
773 | [NL80211_ATTR_TID_CONFIG] = |
774 | NLA_POLICY_NESTED_ARRAY(nl80211_tid_config_attr_policy), |
775 | [NL80211_ATTR_CONTROL_PORT_NO_PREAUTH] = { .type = NLA_FLAG }, |
776 | [NL80211_ATTR_PMK_LIFETIME] = NLA_POLICY_MIN(NLA_U32, 1), |
777 | [NL80211_ATTR_PMK_REAUTH_THRESHOLD] = NLA_POLICY_RANGE(NLA_U8, 1, 100), |
778 | [NL80211_ATTR_RECEIVE_MULTICAST] = { .type = NLA_FLAG }, |
779 | [NL80211_ATTR_WIPHY_FREQ_OFFSET] = NLA_POLICY_RANGE(NLA_U32, 0, 999), |
780 | [NL80211_ATTR_SCAN_FREQ_KHZ] = { .type = NLA_NESTED }, |
781 | [NL80211_ATTR_HE_6GHZ_CAPABILITY] = |
782 | NLA_POLICY_EXACT_LEN(sizeof(struct ieee80211_he_6ghz_capa)), |
783 | [NL80211_ATTR_FILS_DISCOVERY] = |
784 | NLA_POLICY_NESTED(nl80211_fils_discovery_policy), |
785 | [NL80211_ATTR_UNSOL_BCAST_PROBE_RESP] = |
786 | NLA_POLICY_NESTED(nl80211_unsol_bcast_probe_resp_policy), |
787 | [NL80211_ATTR_S1G_CAPABILITY] = |
788 | NLA_POLICY_EXACT_LEN(IEEE80211_S1G_CAPABILITY_LEN), |
789 | [NL80211_ATTR_S1G_CAPABILITY_MASK] = |
790 | NLA_POLICY_EXACT_LEN(IEEE80211_S1G_CAPABILITY_LEN), |
791 | [NL80211_ATTR_SAE_PWE] = |
792 | NLA_POLICY_RANGE(NLA_U8, NL80211_SAE_PWE_HUNT_AND_PECK, |
793 | NL80211_SAE_PWE_BOTH), |
794 | [NL80211_ATTR_RECONNECT_REQUESTED] = { .type = NLA_REJECT }, |
795 | [NL80211_ATTR_SAR_SPEC] = NLA_POLICY_NESTED(sar_policy), |
796 | [NL80211_ATTR_DISABLE_HE] = { .type = NLA_FLAG }, |
797 | [NL80211_ATTR_OBSS_COLOR_BITMAP] = { .type = NLA_U64 }, |
798 | [NL80211_ATTR_COLOR_CHANGE_COUNT] = { .type = NLA_U8 }, |
799 | [NL80211_ATTR_COLOR_CHANGE_COLOR] = { .type = NLA_U8 }, |
800 | [NL80211_ATTR_COLOR_CHANGE_ELEMS] = NLA_POLICY_NESTED(nl80211_policy), |
801 | [NL80211_ATTR_MBSSID_CONFIG] = |
802 | NLA_POLICY_NESTED(nl80211_mbssid_config_policy), |
803 | [NL80211_ATTR_MBSSID_ELEMS] = { .type = NLA_NESTED }, |
804 | [NL80211_ATTR_RADAR_BACKGROUND] = { .type = NLA_FLAG }, |
805 | [NL80211_ATTR_AP_SETTINGS_FLAGS] = { .type = NLA_U32 }, |
806 | [NL80211_ATTR_EHT_CAPABILITY] = |
807 | NLA_POLICY_RANGE(NLA_BINARY, |
808 | NL80211_EHT_MIN_CAPABILITY_LEN, |
809 | NL80211_EHT_MAX_CAPABILITY_LEN), |
810 | [NL80211_ATTR_DISABLE_EHT] = { .type = NLA_FLAG }, |
811 | [NL80211_ATTR_MLO_LINKS] = |
812 | NLA_POLICY_NESTED_ARRAY(nl80211_policy), |
813 | [NL80211_ATTR_MLO_LINK_ID] = |
814 | NLA_POLICY_RANGE(NLA_U8, 0, IEEE80211_MLD_MAX_NUM_LINKS), |
815 | [NL80211_ATTR_MLD_ADDR] = NLA_POLICY_EXACT_LEN(ETH_ALEN), |
816 | [NL80211_ATTR_MLO_SUPPORT] = { .type = NLA_FLAG }, |
817 | [NL80211_ATTR_MAX_NUM_AKM_SUITES] = { .type = NLA_REJECT }, |
818 | [NL80211_ATTR_PUNCT_BITMAP] = |
819 | NLA_POLICY_FULL_RANGE(NLA_U32, &nl80211_punct_bitmap_range), |
820 | |
821 | [NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS] = { .type = NLA_U16 }, |
822 | [NL80211_ATTR_HW_TIMESTAMP_ENABLED] = { .type = NLA_FLAG }, |
823 | [NL80211_ATTR_EMA_RNR_ELEMS] = { .type = NLA_NESTED }, |
824 | [NL80211_ATTR_MLO_LINK_DISABLED] = { .type = NLA_FLAG }, |
825 | [NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA] = { .type = NLA_FLAG }, |
826 | [NL80211_ATTR_MLO_TTLM_DLINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8), |
827 | [NL80211_ATTR_MLO_TTLM_ULINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8), |
828 | [NL80211_ATTR_ASSOC_SPP_AMSDU] = { .type = NLA_FLAG }, |
829 | }; |
830 | |
831 | /* policy for the key attributes */ |
832 | static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = { |
833 | [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN }, |
834 | [NL80211_KEY_IDX] = { .type = NLA_U8 }, |
835 | [NL80211_KEY_CIPHER] = { .type = NLA_U32 }, |
836 | [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 }, |
837 | [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG }, |
838 | [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG }, |
839 | [NL80211_KEY_TYPE] = NLA_POLICY_MAX(NLA_U32, NUM_NL80211_KEYTYPES - 1), |
840 | [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, |
841 | [NL80211_KEY_MODE] = NLA_POLICY_RANGE(NLA_U8, 0, NL80211_KEY_SET_TX), |
842 | }; |
843 | |
844 | /* policy for the key default flags */ |
845 | static const struct nla_policy |
846 | nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = { |
847 | [NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG }, |
848 | [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG }, |
849 | }; |
850 | |
851 | #ifdef CONFIG_PM |
852 | /* policy for WoWLAN attributes */ |
853 | static const struct nla_policy |
854 | nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = { |
855 | [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG }, |
856 | [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG }, |
857 | [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG }, |
858 | [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED }, |
859 | [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG }, |
860 | [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG }, |
861 | [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG }, |
862 | [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG }, |
863 | [NL80211_WOWLAN_TRIG_TCP_CONNECTION] = { .type = NLA_NESTED }, |
864 | [NL80211_WOWLAN_TRIG_NET_DETECT] = { .type = NLA_NESTED }, |
865 | }; |
866 | |
867 | static const struct nla_policy |
868 | nl80211_wowlan_tcp_policy[NUM_NL80211_WOWLAN_TCP] = { |
869 | [NL80211_WOWLAN_TCP_SRC_IPV4] = { .type = NLA_U32 }, |
870 | [NL80211_WOWLAN_TCP_DST_IPV4] = { .type = NLA_U32 }, |
871 | [NL80211_WOWLAN_TCP_DST_MAC] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), |
872 | [NL80211_WOWLAN_TCP_SRC_PORT] = { .type = NLA_U16 }, |
873 | [NL80211_WOWLAN_TCP_DST_PORT] = { .type = NLA_U16 }, |
874 | [NL80211_WOWLAN_TCP_DATA_PAYLOAD] = NLA_POLICY_MIN_LEN(1), |
875 | [NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ] = { |
876 | .len = sizeof(struct nl80211_wowlan_tcp_data_seq) |
877 | }, |
878 | [NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN] = { |
879 | .len = sizeof(struct nl80211_wowlan_tcp_data_token) |
880 | }, |
881 | [NL80211_WOWLAN_TCP_DATA_INTERVAL] = { .type = NLA_U32 }, |
882 | [NL80211_WOWLAN_TCP_WAKE_PAYLOAD] = NLA_POLICY_MIN_LEN(1), |
883 | [NL80211_WOWLAN_TCP_WAKE_MASK] = NLA_POLICY_MIN_LEN(1), |
884 | }; |
885 | #endif /* CONFIG_PM */ |
886 | |
887 | /* policy for coalesce rule attributes */ |
888 | static const struct nla_policy |
889 | nl80211_coalesce_policy[NUM_NL80211_ATTR_COALESCE_RULE] = { |
890 | [NL80211_ATTR_COALESCE_RULE_DELAY] = { .type = NLA_U32 }, |
891 | [NL80211_ATTR_COALESCE_RULE_CONDITION] = |
892 | NLA_POLICY_RANGE(NLA_U32, |
893 | NL80211_COALESCE_CONDITION_MATCH, |
894 | NL80211_COALESCE_CONDITION_NO_MATCH), |
895 | [NL80211_ATTR_COALESCE_RULE_PKT_PATTERN] = { .type = NLA_NESTED }, |
896 | }; |
897 | |
898 | /* policy for GTK rekey offload attributes */ |
899 | static const struct nla_policy |
900 | nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = { |
901 | [NL80211_REKEY_DATA_KEK] = { |
902 | .type = NLA_BINARY, |
903 | .len = NL80211_KEK_EXT_LEN |
904 | }, |
905 | [NL80211_REKEY_DATA_KCK] = { |
906 | .type = NLA_BINARY, |
907 | .len = NL80211_KCK_EXT_LEN_32 |
908 | }, |
909 | [NL80211_REKEY_DATA_REPLAY_CTR] = NLA_POLICY_EXACT_LEN(NL80211_REPLAY_CTR_LEN), |
910 | [NL80211_REKEY_DATA_AKM] = { .type = NLA_U32 }, |
911 | }; |
912 | |
913 | static const struct nla_policy |
914 | nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = { |
915 | [NL80211_SCHED_SCAN_MATCH_ATTR_SSID] = { .type = NLA_BINARY, |
916 | .len = IEEE80211_MAX_SSID_LEN }, |
917 | [NL80211_SCHED_SCAN_MATCH_ATTR_BSSID] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), |
918 | [NL80211_SCHED_SCAN_MATCH_ATTR_RSSI] = { .type = NLA_U32 }, |
919 | }; |
920 | |
921 | static const struct nla_policy |
922 | nl80211_plan_policy[NL80211_SCHED_SCAN_PLAN_MAX + 1] = { |
923 | [NL80211_SCHED_SCAN_PLAN_INTERVAL] = { .type = NLA_U32 }, |
924 | [NL80211_SCHED_SCAN_PLAN_ITERATIONS] = { .type = NLA_U32 }, |
925 | }; |
926 | |
927 | static const struct nla_policy |
928 | nl80211_bss_select_policy[NL80211_BSS_SELECT_ATTR_MAX + 1] = { |
929 | [NL80211_BSS_SELECT_ATTR_RSSI] = { .type = NLA_FLAG }, |
930 | [NL80211_BSS_SELECT_ATTR_BAND_PREF] = { .type = NLA_U32 }, |
931 | [NL80211_BSS_SELECT_ATTR_RSSI_ADJUST] = { |
932 | .len = sizeof(struct nl80211_bss_select_rssi_adjust) |
933 | }, |
934 | }; |
935 | |
936 | /* policy for NAN function attributes */ |
937 | static const struct nla_policy |
938 | nl80211_nan_func_policy[NL80211_NAN_FUNC_ATTR_MAX + 1] = { |
939 | [NL80211_NAN_FUNC_TYPE] = |
940 | NLA_POLICY_MAX(NLA_U8, NL80211_NAN_FUNC_MAX_TYPE), |
941 | [NL80211_NAN_FUNC_SERVICE_ID] = { |
942 | .len = NL80211_NAN_FUNC_SERVICE_ID_LEN }, |
943 | [NL80211_NAN_FUNC_PUBLISH_TYPE] = { .type = NLA_U8 }, |
944 | [NL80211_NAN_FUNC_PUBLISH_BCAST] = { .type = NLA_FLAG }, |
945 | [NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE] = { .type = NLA_FLAG }, |
946 | [NL80211_NAN_FUNC_FOLLOW_UP_ID] = { .type = NLA_U8 }, |
947 | [NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID] = { .type = NLA_U8 }, |
948 | [NL80211_NAN_FUNC_FOLLOW_UP_DEST] = NLA_POLICY_EXACT_LEN_WARN(ETH_ALEN), |
949 | [NL80211_NAN_FUNC_CLOSE_RANGE] = { .type = NLA_FLAG }, |
950 | [NL80211_NAN_FUNC_TTL] = { .type = NLA_U32 }, |
951 | [NL80211_NAN_FUNC_SERVICE_INFO] = { .type = NLA_BINARY, |
952 | .len = NL80211_NAN_FUNC_SERVICE_SPEC_INFO_MAX_LEN }, |
953 | [NL80211_NAN_FUNC_SRF] = { .type = NLA_NESTED }, |
954 | [NL80211_NAN_FUNC_RX_MATCH_FILTER] = { .type = NLA_NESTED }, |
955 | [NL80211_NAN_FUNC_TX_MATCH_FILTER] = { .type = NLA_NESTED }, |
956 | [NL80211_NAN_FUNC_INSTANCE_ID] = { .type = NLA_U8 }, |
957 | [NL80211_NAN_FUNC_TERM_REASON] = { .type = NLA_U8 }, |
958 | }; |
959 | |
960 | /* policy for Service Response Filter attributes */ |
961 | static const struct nla_policy |
962 | nl80211_nan_srf_policy[NL80211_NAN_SRF_ATTR_MAX + 1] = { |
963 | [NL80211_NAN_SRF_INCLUDE] = { .type = NLA_FLAG }, |
964 | [NL80211_NAN_SRF_BF] = { .type = NLA_BINARY, |
965 | .len = NL80211_NAN_FUNC_SRF_MAX_LEN }, |
966 | [NL80211_NAN_SRF_BF_IDX] = { .type = NLA_U8 }, |
967 | [NL80211_NAN_SRF_MAC_ADDRS] = { .type = NLA_NESTED }, |
968 | }; |
969 | |
970 | /* policy for packet pattern attributes */ |
971 | static const struct nla_policy |
972 | nl80211_packet_pattern_policy[MAX_NL80211_PKTPAT + 1] = { |
973 | [NL80211_PKTPAT_MASK] = { .type = NLA_BINARY, }, |
974 | [NL80211_PKTPAT_PATTERN] = { .type = NLA_BINARY, }, |
975 | [NL80211_PKTPAT_OFFSET] = { .type = NLA_U32 }, |
976 | }; |
977 | |
978 | static int nl80211_prepare_wdev_dump(struct netlink_callback *cb, |
979 | struct cfg80211_registered_device **rdev, |
980 | struct wireless_dev **wdev, |
981 | struct nlattr **attrbuf) |
982 | { |
983 | int err; |
984 | |
985 | if (!cb->args[0]) { |
986 | struct nlattr **attrbuf_free = NULL; |
987 | |
988 | if (!attrbuf) { |
989 | attrbuf = kcalloc(n: NUM_NL80211_ATTR, size: sizeof(*attrbuf), |
990 | GFP_KERNEL); |
991 | if (!attrbuf) |
992 | return -ENOMEM; |
993 | attrbuf_free = attrbuf; |
994 | } |
995 | |
996 | err = nlmsg_parse_deprecated(nlh: cb->nlh, |
997 | GENL_HDRLEN + nl80211_fam.hdrsize, |
998 | tb: attrbuf, maxtype: nl80211_fam.maxattr, |
999 | policy: nl80211_policy, NULL); |
1000 | if (err) { |
1001 | kfree(objp: attrbuf_free); |
1002 | return err; |
1003 | } |
1004 | |
1005 | rtnl_lock(); |
1006 | *wdev = __cfg80211_wdev_from_attrs(NULL, netns: sock_net(sk: cb->skb->sk), |
1007 | attrs: attrbuf); |
1008 | kfree(objp: attrbuf_free); |
1009 | if (IS_ERR(ptr: *wdev)) { |
1010 | rtnl_unlock(); |
1011 | return PTR_ERR(ptr: *wdev); |
1012 | } |
1013 | *rdev = wiphy_to_rdev(wiphy: (*wdev)->wiphy); |
1014 | mutex_lock(&(*rdev)->wiphy.mtx); |
1015 | rtnl_unlock(); |
1016 | /* 0 is the first index - add 1 to parse only once */ |
1017 | cb->args[0] = (*rdev)->wiphy_idx + 1; |
1018 | cb->args[1] = (*wdev)->identifier; |
1019 | } else { |
1020 | /* subtract the 1 again here */ |
1021 | struct wiphy *wiphy; |
1022 | struct wireless_dev *tmp; |
1023 | |
1024 | rtnl_lock(); |
1025 | wiphy = wiphy_idx_to_wiphy(wiphy_idx: cb->args[0] - 1); |
1026 | if (!wiphy) { |
1027 | rtnl_unlock(); |
1028 | return -ENODEV; |
1029 | } |
1030 | *rdev = wiphy_to_rdev(wiphy); |
1031 | *wdev = NULL; |
1032 | |
1033 | list_for_each_entry(tmp, &(*rdev)->wiphy.wdev_list, list) { |
1034 | if (tmp->identifier == cb->args[1]) { |
1035 | *wdev = tmp; |
1036 | break; |
1037 | } |
1038 | } |
1039 | |
1040 | if (!*wdev) { |
1041 | rtnl_unlock(); |
1042 | return -ENODEV; |
1043 | } |
1044 | mutex_lock(&(*rdev)->wiphy.mtx); |
1045 | rtnl_unlock(); |
1046 | } |
1047 | |
1048 | return 0; |
1049 | } |
1050 | |
1051 | /* message building helper */ |
1052 | void *nl80211hdr_put(struct sk_buff *skb, u32 portid, u32 seq, |
1053 | int flags, u8 cmd) |
1054 | { |
1055 | /* since there is no private header just add the generic one */ |
1056 | return genlmsg_put(skb, portid, seq, family: &nl80211_fam, flags, cmd); |
1057 | } |
1058 | |
1059 | static int nl80211_msg_put_wmm_rules(struct sk_buff *msg, |
1060 | const struct ieee80211_reg_rule *rule) |
1061 | { |
1062 | int j; |
1063 | struct nlattr *nl_wmm_rules = |
1064 | nla_nest_start_noflag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_WMM); |
1065 | |
1066 | if (!nl_wmm_rules) |
1067 | goto nla_put_failure; |
1068 | |
1069 | for (j = 0; j < IEEE80211_NUM_ACS; j++) { |
1070 | struct nlattr *nl_wmm_rule = nla_nest_start_noflag(skb: msg, attrtype: j); |
1071 | |
1072 | if (!nl_wmm_rule) |
1073 | goto nla_put_failure; |
1074 | |
1075 | if (nla_put_u16(skb: msg, attrtype: NL80211_WMMR_CW_MIN, |
1076 | value: rule->wmm_rule.client[j].cw_min) || |
1077 | nla_put_u16(skb: msg, attrtype: NL80211_WMMR_CW_MAX, |
1078 | value: rule->wmm_rule.client[j].cw_max) || |
1079 | nla_put_u8(skb: msg, attrtype: NL80211_WMMR_AIFSN, |
1080 | value: rule->wmm_rule.client[j].aifsn) || |
1081 | nla_put_u16(skb: msg, attrtype: NL80211_WMMR_TXOP, |
1082 | value: rule->wmm_rule.client[j].cot)) |
1083 | goto nla_put_failure; |
1084 | |
1085 | nla_nest_end(skb: msg, start: nl_wmm_rule); |
1086 | } |
1087 | nla_nest_end(skb: msg, start: nl_wmm_rules); |
1088 | |
1089 | return 0; |
1090 | |
1091 | nla_put_failure: |
1092 | return -ENOBUFS; |
1093 | } |
1094 | |
1095 | static int nl80211_msg_put_channel(struct sk_buff *msg, struct wiphy *wiphy, |
1096 | struct ieee80211_channel *chan, |
1097 | bool large) |
1098 | { |
1099 | /* Some channels must be completely excluded from the |
1100 | * list to protect old user-space tools from breaking |
1101 | */ |
1102 | if (!large && chan->flags & |
1103 | (IEEE80211_CHAN_NO_10MHZ | IEEE80211_CHAN_NO_20MHZ)) |
1104 | return 0; |
1105 | if (!large && chan->freq_offset) |
1106 | return 0; |
1107 | |
1108 | if (nla_put_u32(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_FREQ, |
1109 | value: chan->center_freq)) |
1110 | goto nla_put_failure; |
1111 | |
1112 | if (nla_put_u32(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_OFFSET, value: chan->freq_offset)) |
1113 | goto nla_put_failure; |
1114 | |
1115 | if ((chan->flags & IEEE80211_CHAN_PSD) && |
1116 | nla_put_s8(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_PSD, value: chan->psd)) |
1117 | goto nla_put_failure; |
1118 | |
1119 | if ((chan->flags & IEEE80211_CHAN_DISABLED) && |
1120 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_DISABLED)) |
1121 | goto nla_put_failure; |
1122 | if (chan->flags & IEEE80211_CHAN_NO_IR) { |
1123 | if (nla_put_flag(skb: msg, NL80211_FREQUENCY_ATTR_NO_IR)) |
1124 | goto nla_put_failure; |
1125 | if (nla_put_flag(skb: msg, attrtype: __NL80211_FREQUENCY_ATTR_NO_IBSS)) |
1126 | goto nla_put_failure; |
1127 | } |
1128 | if (chan->flags & IEEE80211_CHAN_RADAR) { |
1129 | if (nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_RADAR)) |
1130 | goto nla_put_failure; |
1131 | if (large) { |
1132 | u32 time; |
1133 | |
1134 | time = elapsed_jiffies_msecs(start: chan->dfs_state_entered); |
1135 | |
1136 | if (nla_put_u32(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_DFS_STATE, |
1137 | value: chan->dfs_state)) |
1138 | goto nla_put_failure; |
1139 | if (nla_put_u32(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_DFS_TIME, |
1140 | value: time)) |
1141 | goto nla_put_failure; |
1142 | if (nla_put_u32(skb: msg, |
1143 | attrtype: NL80211_FREQUENCY_ATTR_DFS_CAC_TIME, |
1144 | value: chan->dfs_cac_ms)) |
1145 | goto nla_put_failure; |
1146 | } |
1147 | } |
1148 | |
1149 | if (large) { |
1150 | if ((chan->flags & IEEE80211_CHAN_NO_HT40MINUS) && |
1151 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_HT40_MINUS)) |
1152 | goto nla_put_failure; |
1153 | if ((chan->flags & IEEE80211_CHAN_NO_HT40PLUS) && |
1154 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_HT40_PLUS)) |
1155 | goto nla_put_failure; |
1156 | if ((chan->flags & IEEE80211_CHAN_NO_80MHZ) && |
1157 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_80MHZ)) |
1158 | goto nla_put_failure; |
1159 | if ((chan->flags & IEEE80211_CHAN_NO_160MHZ) && |
1160 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_160MHZ)) |
1161 | goto nla_put_failure; |
1162 | if ((chan->flags & IEEE80211_CHAN_INDOOR_ONLY) && |
1163 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_INDOOR_ONLY)) |
1164 | goto nla_put_failure; |
1165 | if ((chan->flags & IEEE80211_CHAN_IR_CONCURRENT) && |
1166 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_IR_CONCURRENT)) |
1167 | goto nla_put_failure; |
1168 | if ((chan->flags & IEEE80211_CHAN_NO_20MHZ) && |
1169 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_20MHZ)) |
1170 | goto nla_put_failure; |
1171 | if ((chan->flags & IEEE80211_CHAN_NO_10MHZ) && |
1172 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_10MHZ)) |
1173 | goto nla_put_failure; |
1174 | if ((chan->flags & IEEE80211_CHAN_NO_HE) && |
1175 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_HE)) |
1176 | goto nla_put_failure; |
1177 | if ((chan->flags & IEEE80211_CHAN_1MHZ) && |
1178 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_1MHZ)) |
1179 | goto nla_put_failure; |
1180 | if ((chan->flags & IEEE80211_CHAN_2MHZ) && |
1181 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_2MHZ)) |
1182 | goto nla_put_failure; |
1183 | if ((chan->flags & IEEE80211_CHAN_4MHZ) && |
1184 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_4MHZ)) |
1185 | goto nla_put_failure; |
1186 | if ((chan->flags & IEEE80211_CHAN_8MHZ) && |
1187 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_8MHZ)) |
1188 | goto nla_put_failure; |
1189 | if ((chan->flags & IEEE80211_CHAN_16MHZ) && |
1190 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_16MHZ)) |
1191 | goto nla_put_failure; |
1192 | if ((chan->flags & IEEE80211_CHAN_NO_320MHZ) && |
1193 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_320MHZ)) |
1194 | goto nla_put_failure; |
1195 | if ((chan->flags & IEEE80211_CHAN_NO_EHT) && |
1196 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_EHT)) |
1197 | goto nla_put_failure; |
1198 | if ((chan->flags & IEEE80211_CHAN_DFS_CONCURRENT) && |
1199 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_DFS_CONCURRENT)) |
1200 | goto nla_put_failure; |
1201 | if ((chan->flags & IEEE80211_CHAN_NO_6GHZ_VLP_CLIENT) && |
1202 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_6GHZ_VLP_CLIENT)) |
1203 | goto nla_put_failure; |
1204 | if ((chan->flags & IEEE80211_CHAN_NO_6GHZ_AFC_CLIENT) && |
1205 | nla_put_flag(skb: msg, attrtype: NL80211_FREQUENCY_ATTR_NO_6GHZ_AFC_CLIENT)) |
1206 | goto nla_put_failure; |
1207 | } |
1208 | |
1209 | if (nla_put_u32(skb: msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, |
1210 | DBM_TO_MBM(chan->max_power))) |
1211 | goto nla_put_failure; |
1212 | |
1213 | if (large) { |
1214 | const struct ieee80211_reg_rule *rule = |
1215 | freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq)); |
1216 | |
1217 | if (!IS_ERR_OR_NULL(ptr: rule) && rule->has_wmm) { |
1218 | if (nl80211_msg_put_wmm_rules(msg, rule)) |
1219 | goto nla_put_failure; |
1220 | } |
1221 | } |
1222 | |
1223 | return 0; |
1224 | |
1225 | nla_put_failure: |
1226 | return -ENOBUFS; |
1227 | } |
1228 | |
1229 | static bool nl80211_put_txq_stats(struct sk_buff *msg, |
1230 | struct cfg80211_txq_stats *txqstats, |
1231 | int attrtype) |
1232 | { |
1233 | struct nlattr *txqattr; |
1234 | |
1235 | #define PUT_TXQVAL_U32(attr, memb) do { \ |
1236 | if (txqstats->filled & BIT(NL80211_TXQ_STATS_ ## attr) && \ |
1237 | nla_put_u32(msg, NL80211_TXQ_STATS_ ## attr, txqstats->memb)) \ |
1238 | return false; \ |
1239 | } while (0) |
1240 | |
1241 | txqattr = nla_nest_start_noflag(skb: msg, attrtype); |
1242 | if (!txqattr) |
1243 | return false; |
1244 | |
1245 | PUT_TXQVAL_U32(BACKLOG_BYTES, backlog_bytes); |
1246 | PUT_TXQVAL_U32(BACKLOG_PACKETS, backlog_packets); |
1247 | PUT_TXQVAL_U32(FLOWS, flows); |
1248 | PUT_TXQVAL_U32(DROPS, drops); |
1249 | PUT_TXQVAL_U32(ECN_MARKS, ecn_marks); |
1250 | PUT_TXQVAL_U32(OVERLIMIT, overlimit); |
1251 | PUT_TXQVAL_U32(OVERMEMORY, overmemory); |
1252 | PUT_TXQVAL_U32(COLLISIONS, collisions); |
1253 | PUT_TXQVAL_U32(TX_BYTES, tx_bytes); |
1254 | PUT_TXQVAL_U32(TX_PACKETS, tx_packets); |
1255 | PUT_TXQVAL_U32(MAX_FLOWS, max_flows); |
1256 | nla_nest_end(skb: msg, start: txqattr); |
1257 | |
1258 | #undef PUT_TXQVAL_U32 |
1259 | return true; |
1260 | } |
1261 | |
1262 | /* netlink command implementations */ |
1263 | |
1264 | /** |
1265 | * nl80211_link_id - return link ID |
1266 | * @attrs: attributes to look at |
1267 | * |
1268 | * Returns: the link ID or 0 if not given |
1269 | * |
1270 | * Note this function doesn't do any validation of the link |
1271 | * ID validity wrt. links that were actually added, so it must |
1272 | * be called only from ops with %NL80211_FLAG_MLO_VALID_LINK_ID |
1273 | * or if additional validation is done. |
1274 | */ |
1275 | static unsigned int nl80211_link_id(struct nlattr **attrs) |
1276 | { |
1277 | struct nlattr *linkid = attrs[NL80211_ATTR_MLO_LINK_ID]; |
1278 | |
1279 | if (!linkid) |
1280 | return 0; |
1281 | |
1282 | return nla_get_u8(nla: linkid); |
1283 | } |
1284 | |
1285 | static int nl80211_link_id_or_invalid(struct nlattr **attrs) |
1286 | { |
1287 | struct nlattr *linkid = attrs[NL80211_ATTR_MLO_LINK_ID]; |
1288 | |
1289 | if (!linkid) |
1290 | return -1; |
1291 | |
1292 | return nla_get_u8(nla: linkid); |
1293 | } |
1294 | |
1295 | struct key_parse { |
1296 | struct key_params p; |
1297 | int idx; |
1298 | int type; |
1299 | bool def, defmgmt, defbeacon; |
1300 | bool def_uni, def_multi; |
1301 | }; |
1302 | |
1303 | static int nl80211_parse_key_new(struct genl_info *info, struct nlattr *key, |
1304 | struct key_parse *k) |
1305 | { |
1306 | struct nlattr *tb[NL80211_KEY_MAX + 1]; |
1307 | int err = nla_parse_nested_deprecated(tb, maxtype: NL80211_KEY_MAX, nla: key, |
1308 | policy: nl80211_key_policy, |
1309 | extack: info->extack); |
1310 | if (err) |
1311 | return err; |
1312 | |
1313 | k->def = !!tb[NL80211_KEY_DEFAULT]; |
1314 | k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT]; |
1315 | k->defbeacon = !!tb[NL80211_KEY_DEFAULT_BEACON]; |
1316 | |
1317 | if (k->def) { |
1318 | k->def_uni = true; |
1319 | k->def_multi = true; |
1320 | } |
1321 | if (k->defmgmt || k->defbeacon) |
1322 | k->def_multi = true; |
1323 | |
1324 | if (tb[NL80211_KEY_IDX]) |
1325 | k->idx = nla_get_u8(nla: tb[NL80211_KEY_IDX]); |
1326 | |
1327 | if (tb[NL80211_KEY_DATA]) { |
1328 | k->p.key = nla_data(nla: tb[NL80211_KEY_DATA]); |
1329 | k->p.key_len = nla_len(nla: tb[NL80211_KEY_DATA]); |
1330 | } |
1331 | |
1332 | if (tb[NL80211_KEY_SEQ]) { |
1333 | k->p.seq = nla_data(nla: tb[NL80211_KEY_SEQ]); |
1334 | k->p.seq_len = nla_len(nla: tb[NL80211_KEY_SEQ]); |
1335 | } |
1336 | |
1337 | if (tb[NL80211_KEY_CIPHER]) |
1338 | k->p.cipher = nla_get_u32(nla: tb[NL80211_KEY_CIPHER]); |
1339 | |
1340 | if (tb[NL80211_KEY_TYPE]) |
1341 | k->type = nla_get_u32(nla: tb[NL80211_KEY_TYPE]); |
1342 | |
1343 | if (tb[NL80211_KEY_DEFAULT_TYPES]) { |
1344 | struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES]; |
1345 | |
1346 | err = nla_parse_nested_deprecated(tb: kdt, |
1347 | maxtype: NUM_NL80211_KEY_DEFAULT_TYPES - 1, |
1348 | nla: tb[NL80211_KEY_DEFAULT_TYPES], |
1349 | policy: nl80211_key_default_policy, |
1350 | extack: info->extack); |
1351 | if (err) |
1352 | return err; |
1353 | |
1354 | k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST]; |
1355 | k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST]; |
1356 | } |
1357 | |
1358 | if (tb[NL80211_KEY_MODE]) |
1359 | k->p.mode = nla_get_u8(nla: tb[NL80211_KEY_MODE]); |
1360 | |
1361 | return 0; |
1362 | } |
1363 | |
1364 | static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k) |
1365 | { |
1366 | if (info->attrs[NL80211_ATTR_KEY_DATA]) { |
1367 | k->p.key = nla_data(nla: info->attrs[NL80211_ATTR_KEY_DATA]); |
1368 | k->p.key_len = nla_len(nla: info->attrs[NL80211_ATTR_KEY_DATA]); |
1369 | } |
1370 | |
1371 | if (info->attrs[NL80211_ATTR_KEY_SEQ]) { |
1372 | k->p.seq = nla_data(nla: info->attrs[NL80211_ATTR_KEY_SEQ]); |
1373 | k->p.seq_len = nla_len(nla: info->attrs[NL80211_ATTR_KEY_SEQ]); |
1374 | } |
1375 | |
1376 | if (info->attrs[NL80211_ATTR_KEY_IDX]) |
1377 | k->idx = nla_get_u8(nla: info->attrs[NL80211_ATTR_KEY_IDX]); |
1378 | |
1379 | if (info->attrs[NL80211_ATTR_KEY_CIPHER]) |
1380 | k->p.cipher = nla_get_u32(nla: info->attrs[NL80211_ATTR_KEY_CIPHER]); |
1381 | |
1382 | k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT]; |
1383 | k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]; |
1384 | |
1385 | if (k->def) { |
1386 | k->def_uni = true; |
1387 | k->def_multi = true; |
1388 | } |
1389 | if (k->defmgmt) |
1390 | k->def_multi = true; |
1391 | |
1392 | if (info->attrs[NL80211_ATTR_KEY_TYPE]) |
1393 | k->type = nla_get_u32(nla: info->attrs[NL80211_ATTR_KEY_TYPE]); |
1394 | |
1395 | if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) { |
1396 | struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES]; |
1397 | int err = nla_parse_nested_deprecated(tb: kdt, |
1398 | maxtype: NUM_NL80211_KEY_DEFAULT_TYPES - 1, |
1399 | nla: info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES], |
1400 | policy: nl80211_key_default_policy, |
1401 | extack: info->extack); |
1402 | if (err) |
1403 | return err; |
1404 | |
1405 | k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST]; |
1406 | k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST]; |
1407 | } |
1408 | |
1409 | return 0; |
1410 | } |
1411 | |
1412 | static int nl80211_parse_key(struct genl_info *info, struct key_parse *k) |
1413 | { |
1414 | int err; |
1415 | |
1416 | memset(k, 0, sizeof(*k)); |
1417 | k->idx = -1; |
1418 | k->type = -1; |
1419 | |
1420 | if (info->attrs[NL80211_ATTR_KEY]) |
1421 | err = nl80211_parse_key_new(info, key: info->attrs[NL80211_ATTR_KEY], k); |
1422 | else |
1423 | err = nl80211_parse_key_old(info, k); |
1424 | |
1425 | if (err) |
1426 | return err; |
1427 | |
1428 | if ((k->def ? 1 : 0) + (k->defmgmt ? 1 : 0) + |
1429 | (k->defbeacon ? 1 : 0) > 1) { |
1430 | GENL_SET_ERR_MSG(info, |
1431 | "key with multiple default flags is invalid" ); |
1432 | return -EINVAL; |
1433 | } |
1434 | |
1435 | if (k->defmgmt || k->defbeacon) { |
1436 | if (k->def_uni || !k->def_multi) { |
1437 | GENL_SET_ERR_MSG(info, |
1438 | "defmgmt/defbeacon key must be mcast" ); |
1439 | return -EINVAL; |
1440 | } |
1441 | } |
1442 | |
1443 | if (k->idx != -1) { |
1444 | if (k->defmgmt) { |
1445 | if (k->idx < 4 || k->idx > 5) { |
1446 | GENL_SET_ERR_MSG(info, |
1447 | "defmgmt key idx not 4 or 5" ); |
1448 | return -EINVAL; |
1449 | } |
1450 | } else if (k->defbeacon) { |
1451 | if (k->idx < 6 || k->idx > 7) { |
1452 | GENL_SET_ERR_MSG(info, |
1453 | "defbeacon key idx not 6 or 7" ); |
1454 | return -EINVAL; |
1455 | } |
1456 | } else if (k->def) { |
1457 | if (k->idx < 0 || k->idx > 3) { |
1458 | GENL_SET_ERR_MSG(info, "def key idx not 0-3" ); |
1459 | return -EINVAL; |
1460 | } |
1461 | } else { |
1462 | if (k->idx < 0 || k->idx > 7) { |
1463 | GENL_SET_ERR_MSG(info, "key idx not 0-7" ); |
1464 | return -EINVAL; |
1465 | } |
1466 | } |
1467 | } |
1468 | |
1469 | return 0; |
1470 | } |
1471 | |
1472 | static struct cfg80211_cached_keys * |
1473 | nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, |
1474 | struct genl_info *info, bool *no_ht) |
1475 | { |
1476 | struct nlattr *keys = info->attrs[NL80211_ATTR_KEYS]; |
1477 | struct key_parse parse; |
1478 | struct nlattr *key; |
1479 | struct cfg80211_cached_keys *result; |
1480 | int rem, err, def = 0; |
1481 | bool have_key = false; |
1482 | |
1483 | nla_for_each_nested(key, keys, rem) { |
1484 | have_key = true; |
1485 | break; |
1486 | } |
1487 | |
1488 | if (!have_key) |
1489 | return NULL; |
1490 | |
1491 | result = kzalloc(size: sizeof(*result), GFP_KERNEL); |
1492 | if (!result) |
1493 | return ERR_PTR(error: -ENOMEM); |
1494 | |
1495 | result->def = -1; |
1496 | |
1497 | nla_for_each_nested(key, keys, rem) { |
1498 | memset(&parse, 0, sizeof(parse)); |
1499 | parse.idx = -1; |
1500 | |
1501 | err = nl80211_parse_key_new(info, key, k: &parse); |
1502 | if (err) |
1503 | goto error; |
1504 | err = -EINVAL; |
1505 | if (!parse.p.key) |
1506 | goto error; |
1507 | if (parse.idx < 0 || parse.idx > 3) { |
1508 | GENL_SET_ERR_MSG(info, "key index out of range [0-3]" ); |
1509 | goto error; |
1510 | } |
1511 | if (parse.def) { |
1512 | if (def) { |
1513 | GENL_SET_ERR_MSG(info, |
1514 | "only one key can be default" ); |
1515 | goto error; |
1516 | } |
1517 | def = 1; |
1518 | result->def = parse.idx; |
1519 | if (!parse.def_uni || !parse.def_multi) |
1520 | goto error; |
1521 | } else if (parse.defmgmt) |
1522 | goto error; |
1523 | err = cfg80211_validate_key_settings(rdev, params: &parse.p, |
1524 | key_idx: parse.idx, pairwise: false, NULL); |
1525 | if (err) |
1526 | goto error; |
1527 | if (parse.p.cipher != WLAN_CIPHER_SUITE_WEP40 && |
1528 | parse.p.cipher != WLAN_CIPHER_SUITE_WEP104) { |
1529 | GENL_SET_ERR_MSG(info, "connect key must be WEP" ); |
1530 | err = -EINVAL; |
1531 | goto error; |
1532 | } |
1533 | result->params[parse.idx].cipher = parse.p.cipher; |
1534 | result->params[parse.idx].key_len = parse.p.key_len; |
1535 | result->params[parse.idx].key = result->data[parse.idx]; |
1536 | memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len); |
1537 | |
1538 | /* must be WEP key if we got here */ |
1539 | if (no_ht) |
1540 | *no_ht = true; |
1541 | } |
1542 | |
1543 | if (result->def < 0) { |
1544 | err = -EINVAL; |
1545 | GENL_SET_ERR_MSG(info, "need a default/TX key" ); |
1546 | goto error; |
1547 | } |
1548 | |
1549 | return result; |
1550 | error: |
1551 | kfree(objp: result); |
1552 | return ERR_PTR(error: err); |
1553 | } |
1554 | |
1555 | static int nl80211_key_allowed(struct wireless_dev *wdev) |
1556 | { |
1557 | lockdep_assert_wiphy(wdev->wiphy); |
1558 | |
1559 | switch (wdev->iftype) { |
1560 | case NL80211_IFTYPE_AP: |
1561 | case NL80211_IFTYPE_AP_VLAN: |
1562 | case NL80211_IFTYPE_P2P_GO: |
1563 | case NL80211_IFTYPE_MESH_POINT: |
1564 | break; |
1565 | case NL80211_IFTYPE_ADHOC: |
1566 | if (wdev->u.ibss.current_bss) |
1567 | return 0; |
1568 | return -ENOLINK; |
1569 | case NL80211_IFTYPE_STATION: |
1570 | case NL80211_IFTYPE_P2P_CLIENT: |
1571 | if (wdev->connected) |
1572 | return 0; |
1573 | return -ENOLINK; |
1574 | case NL80211_IFTYPE_NAN: |
1575 | if (wiphy_ext_feature_isset(wiphy: wdev->wiphy, |
1576 | ftidx: NL80211_EXT_FEATURE_SECURE_NAN)) |
1577 | return 0; |
1578 | return -EINVAL; |
1579 | case NL80211_IFTYPE_UNSPECIFIED: |
1580 | case NL80211_IFTYPE_OCB: |
1581 | case NL80211_IFTYPE_MONITOR: |
1582 | case NL80211_IFTYPE_P2P_DEVICE: |
1583 | case NL80211_IFTYPE_WDS: |
1584 | case NUM_NL80211_IFTYPES: |
1585 | return -EINVAL; |
1586 | } |
1587 | |
1588 | return 0; |
1589 | } |
1590 | |
1591 | static struct ieee80211_channel *nl80211_get_valid_chan(struct wiphy *wiphy, |
1592 | u32 freq) |
1593 | { |
1594 | struct ieee80211_channel *chan; |
1595 | |
1596 | chan = ieee80211_get_channel_khz(wiphy, freq); |
1597 | if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) |
1598 | return NULL; |
1599 | return chan; |
1600 | } |
1601 | |
1602 | static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes) |
1603 | { |
1604 | struct nlattr *nl_modes = nla_nest_start_noflag(skb: msg, attrtype: attr); |
1605 | int i; |
1606 | |
1607 | if (!nl_modes) |
1608 | goto nla_put_failure; |
1609 | |
1610 | i = 0; |
1611 | while (ifmodes) { |
1612 | if ((ifmodes & 1) && nla_put_flag(skb: msg, attrtype: i)) |
1613 | goto nla_put_failure; |
1614 | ifmodes >>= 1; |
1615 | i++; |
1616 | } |
1617 | |
1618 | nla_nest_end(skb: msg, start: nl_modes); |
1619 | return 0; |
1620 | |
1621 | nla_put_failure: |
1622 | return -ENOBUFS; |
1623 | } |
1624 | |
1625 | static int nl80211_put_iface_combinations(struct wiphy *wiphy, |
1626 | struct sk_buff *msg, |
1627 | bool large) |
1628 | { |
1629 | struct nlattr *nl_combis; |
1630 | int i, j; |
1631 | |
1632 | nl_combis = nla_nest_start_noflag(skb: msg, |
1633 | attrtype: NL80211_ATTR_INTERFACE_COMBINATIONS); |
1634 | if (!nl_combis) |
1635 | goto nla_put_failure; |
1636 | |
1637 | for (i = 0; i < wiphy->n_iface_combinations; i++) { |
1638 | const struct ieee80211_iface_combination *c; |
1639 | struct nlattr *nl_combi, *nl_limits; |
1640 | |
1641 | c = &wiphy->iface_combinations[i]; |
1642 | |
1643 | nl_combi = nla_nest_start_noflag(skb: msg, attrtype: i + 1); |
1644 | if (!nl_combi) |
1645 | goto nla_put_failure; |
1646 | |
1647 | nl_limits = nla_nest_start_noflag(skb: msg, |
1648 | attrtype: NL80211_IFACE_COMB_LIMITS); |
1649 | if (!nl_limits) |
1650 | goto nla_put_failure; |
1651 | |
1652 | for (j = 0; j < c->n_limits; j++) { |
1653 | struct nlattr *nl_limit; |
1654 | |
1655 | nl_limit = nla_nest_start_noflag(skb: msg, attrtype: j + 1); |
1656 | if (!nl_limit) |
1657 | goto nla_put_failure; |
1658 | if (nla_put_u32(skb: msg, attrtype: NL80211_IFACE_LIMIT_MAX, |
1659 | value: c->limits[j].max)) |
1660 | goto nla_put_failure; |
1661 | if (nl80211_put_iftypes(msg, attr: NL80211_IFACE_LIMIT_TYPES, |
1662 | ifmodes: c->limits[j].types)) |
1663 | goto nla_put_failure; |
1664 | nla_nest_end(skb: msg, start: nl_limit); |
1665 | } |
1666 | |
1667 | nla_nest_end(skb: msg, start: nl_limits); |
1668 | |
1669 | if (c->beacon_int_infra_match && |
1670 | nla_put_flag(skb: msg, attrtype: NL80211_IFACE_COMB_STA_AP_BI_MATCH)) |
1671 | goto nla_put_failure; |
1672 | if (nla_put_u32(skb: msg, attrtype: NL80211_IFACE_COMB_NUM_CHANNELS, |
1673 | value: c->num_different_channels) || |
1674 | nla_put_u32(skb: msg, attrtype: NL80211_IFACE_COMB_MAXNUM, |
1675 | value: c->max_interfaces)) |
1676 | goto nla_put_failure; |
1677 | if (large && |
1678 | (nla_put_u32(skb: msg, attrtype: NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, |
1679 | value: c->radar_detect_widths) || |
1680 | nla_put_u32(skb: msg, attrtype: NL80211_IFACE_COMB_RADAR_DETECT_REGIONS, |
1681 | value: c->radar_detect_regions))) |
1682 | goto nla_put_failure; |
1683 | if (c->beacon_int_min_gcd && |
1684 | nla_put_u32(skb: msg, attrtype: NL80211_IFACE_COMB_BI_MIN_GCD, |
1685 | value: c->beacon_int_min_gcd)) |
1686 | goto nla_put_failure; |
1687 | |
1688 | nla_nest_end(skb: msg, start: nl_combi); |
1689 | } |
1690 | |
1691 | nla_nest_end(skb: msg, start: nl_combis); |
1692 | |
1693 | return 0; |
1694 | nla_put_failure: |
1695 | return -ENOBUFS; |
1696 | } |
1697 | |
1698 | #ifdef CONFIG_PM |
1699 | static int nl80211_send_wowlan_tcp_caps(struct cfg80211_registered_device *rdev, |
1700 | struct sk_buff *msg) |
1701 | { |
1702 | const struct wiphy_wowlan_tcp_support *tcp = rdev->wiphy.wowlan->tcp; |
1703 | struct nlattr *nl_tcp; |
1704 | |
1705 | if (!tcp) |
1706 | return 0; |
1707 | |
1708 | nl_tcp = nla_nest_start_noflag(skb: msg, |
1709 | attrtype: NL80211_WOWLAN_TRIG_TCP_CONNECTION); |
1710 | if (!nl_tcp) |
1711 | return -ENOBUFS; |
1712 | |
1713 | if (nla_put_u32(skb: msg, attrtype: NL80211_WOWLAN_TCP_DATA_PAYLOAD, |
1714 | value: tcp->data_payload_max)) |
1715 | return -ENOBUFS; |
1716 | |
1717 | if (nla_put_u32(skb: msg, attrtype: NL80211_WOWLAN_TCP_DATA_PAYLOAD, |
1718 | value: tcp->data_payload_max)) |
1719 | return -ENOBUFS; |
1720 | |
1721 | if (tcp->seq && nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ)) |
1722 | return -ENOBUFS; |
1723 | |
1724 | if (tcp->tok && nla_put(skb: msg, attrtype: NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, |
1725 | attrlen: sizeof(*tcp->tok), data: tcp->tok)) |
1726 | return -ENOBUFS; |
1727 | |
1728 | if (nla_put_u32(skb: msg, attrtype: NL80211_WOWLAN_TCP_DATA_INTERVAL, |
1729 | value: tcp->data_interval_max)) |
1730 | return -ENOBUFS; |
1731 | |
1732 | if (nla_put_u32(skb: msg, attrtype: NL80211_WOWLAN_TCP_WAKE_PAYLOAD, |
1733 | value: tcp->wake_payload_max)) |
1734 | return -ENOBUFS; |
1735 | |
1736 | nla_nest_end(skb: msg, start: nl_tcp); |
1737 | return 0; |
1738 | } |
1739 | |
1740 | static int nl80211_send_wowlan(struct sk_buff *msg, |
1741 | struct cfg80211_registered_device *rdev, |
1742 | bool large) |
1743 | { |
1744 | struct nlattr *nl_wowlan; |
1745 | |
1746 | if (!rdev->wiphy.wowlan) |
1747 | return 0; |
1748 | |
1749 | nl_wowlan = nla_nest_start_noflag(skb: msg, |
1750 | attrtype: NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED); |
1751 | if (!nl_wowlan) |
1752 | return -ENOBUFS; |
1753 | |
1754 | if (((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_ANY) && |
1755 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_ANY)) || |
1756 | ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_DISCONNECT) && |
1757 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_DISCONNECT)) || |
1758 | ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT) && |
1759 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_MAGIC_PKT)) || |
1760 | ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) && |
1761 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) || |
1762 | ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && |
1763 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) || |
1764 | ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) && |
1765 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) || |
1766 | ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) && |
1767 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) || |
1768 | ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE) && |
1769 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) |
1770 | return -ENOBUFS; |
1771 | |
1772 | if (rdev->wiphy.wowlan->n_patterns) { |
1773 | struct nl80211_pattern_support pat = { |
1774 | .max_patterns = rdev->wiphy.wowlan->n_patterns, |
1775 | .min_pattern_len = rdev->wiphy.wowlan->pattern_min_len, |
1776 | .max_pattern_len = rdev->wiphy.wowlan->pattern_max_len, |
1777 | .max_pkt_offset = rdev->wiphy.wowlan->max_pkt_offset, |
1778 | }; |
1779 | |
1780 | if (nla_put(skb: msg, attrtype: NL80211_WOWLAN_TRIG_PKT_PATTERN, |
1781 | attrlen: sizeof(pat), data: &pat)) |
1782 | return -ENOBUFS; |
1783 | } |
1784 | |
1785 | if ((rdev->wiphy.wowlan->flags & WIPHY_WOWLAN_NET_DETECT) && |
1786 | nla_put_u32(skb: msg, attrtype: NL80211_WOWLAN_TRIG_NET_DETECT, |
1787 | value: rdev->wiphy.wowlan->max_nd_match_sets)) |
1788 | return -ENOBUFS; |
1789 | |
1790 | if (large && nl80211_send_wowlan_tcp_caps(rdev, msg)) |
1791 | return -ENOBUFS; |
1792 | |
1793 | nla_nest_end(skb: msg, start: nl_wowlan); |
1794 | |
1795 | return 0; |
1796 | } |
1797 | #endif |
1798 | |
1799 | static int nl80211_send_coalesce(struct sk_buff *msg, |
1800 | struct cfg80211_registered_device *rdev) |
1801 | { |
1802 | struct nl80211_coalesce_rule_support rule; |
1803 | |
1804 | if (!rdev->wiphy.coalesce) |
1805 | return 0; |
1806 | |
1807 | rule.max_rules = rdev->wiphy.coalesce->n_rules; |
1808 | rule.max_delay = rdev->wiphy.coalesce->max_delay; |
1809 | rule.pat.max_patterns = rdev->wiphy.coalesce->n_patterns; |
1810 | rule.pat.min_pattern_len = rdev->wiphy.coalesce->pattern_min_len; |
1811 | rule.pat.max_pattern_len = rdev->wiphy.coalesce->pattern_max_len; |
1812 | rule.pat.max_pkt_offset = rdev->wiphy.coalesce->max_pkt_offset; |
1813 | |
1814 | if (nla_put(skb: msg, attrtype: NL80211_ATTR_COALESCE_RULE, attrlen: sizeof(rule), data: &rule)) |
1815 | return -ENOBUFS; |
1816 | |
1817 | return 0; |
1818 | } |
1819 | |
1820 | static int |
1821 | nl80211_send_iftype_data(struct sk_buff *msg, |
1822 | const struct ieee80211_supported_band *sband, |
1823 | const struct ieee80211_sband_iftype_data *iftdata) |
1824 | { |
1825 | const struct ieee80211_sta_he_cap *he_cap = &iftdata->he_cap; |
1826 | const struct ieee80211_sta_eht_cap *eht_cap = &iftdata->eht_cap; |
1827 | |
1828 | if (nl80211_put_iftypes(msg, attr: NL80211_BAND_IFTYPE_ATTR_IFTYPES, |
1829 | ifmodes: iftdata->types_mask)) |
1830 | return -ENOBUFS; |
1831 | |
1832 | if (he_cap->has_he) { |
1833 | if (nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC, |
1834 | attrlen: sizeof(he_cap->he_cap_elem.mac_cap_info), |
1835 | data: he_cap->he_cap_elem.mac_cap_info) || |
1836 | nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY, |
1837 | attrlen: sizeof(he_cap->he_cap_elem.phy_cap_info), |
1838 | data: he_cap->he_cap_elem.phy_cap_info) || |
1839 | nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET, |
1840 | attrlen: sizeof(he_cap->he_mcs_nss_supp), |
1841 | data: &he_cap->he_mcs_nss_supp) || |
1842 | nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE, |
1843 | attrlen: sizeof(he_cap->ppe_thres), data: he_cap->ppe_thres)) |
1844 | return -ENOBUFS; |
1845 | } |
1846 | |
1847 | if (eht_cap->has_eht && he_cap->has_he) { |
1848 | u8 mcs_nss_size, ppe_thresh_size; |
1849 | u16 ppe_thres_hdr; |
1850 | bool is_ap; |
1851 | |
1852 | is_ap = iftdata->types_mask & BIT(NL80211_IFTYPE_AP) || |
1853 | iftdata->types_mask & BIT(NL80211_IFTYPE_P2P_GO); |
1854 | |
1855 | mcs_nss_size = |
1856 | ieee80211_eht_mcs_nss_size(he_cap: &he_cap->he_cap_elem, |
1857 | eht_cap: &eht_cap->eht_cap_elem, |
1858 | from_ap: is_ap); |
1859 | |
1860 | ppe_thres_hdr = get_unaligned_le16(p: &eht_cap->eht_ppe_thres[0]); |
1861 | ppe_thresh_size = |
1862 | ieee80211_eht_ppe_size(ppe_thres_hdr, |
1863 | phy_cap_info: eht_cap->eht_cap_elem.phy_cap_info); |
1864 | |
1865 | if (nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC, |
1866 | attrlen: sizeof(eht_cap->eht_cap_elem.mac_cap_info), |
1867 | data: eht_cap->eht_cap_elem.mac_cap_info) || |
1868 | nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY, |
1869 | attrlen: sizeof(eht_cap->eht_cap_elem.phy_cap_info), |
1870 | data: eht_cap->eht_cap_elem.phy_cap_info) || |
1871 | nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET, |
1872 | attrlen: mcs_nss_size, data: &eht_cap->eht_mcs_nss_supp) || |
1873 | nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE, |
1874 | attrlen: ppe_thresh_size, data: eht_cap->eht_ppe_thres)) |
1875 | return -ENOBUFS; |
1876 | } |
1877 | |
1878 | if (sband->band == NL80211_BAND_6GHZ && |
1879 | nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA, |
1880 | attrlen: sizeof(iftdata->he_6ghz_capa), |
1881 | data: &iftdata->he_6ghz_capa)) |
1882 | return -ENOBUFS; |
1883 | |
1884 | if (iftdata->vendor_elems.data && iftdata->vendor_elems.len && |
1885 | nla_put(skb: msg, attrtype: NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS, |
1886 | attrlen: iftdata->vendor_elems.len, data: iftdata->vendor_elems.data)) |
1887 | return -ENOBUFS; |
1888 | |
1889 | return 0; |
1890 | } |
1891 | |
1892 | static int nl80211_send_band_rateinfo(struct sk_buff *msg, |
1893 | struct ieee80211_supported_band *sband, |
1894 | bool large) |
1895 | { |
1896 | struct nlattr *nl_rates, *nl_rate; |
1897 | struct ieee80211_rate *rate; |
1898 | int i; |
1899 | |
1900 | /* add HT info */ |
1901 | if (sband->ht_cap.ht_supported && |
1902 | (nla_put(skb: msg, attrtype: NL80211_BAND_ATTR_HT_MCS_SET, |
1903 | attrlen: sizeof(sband->ht_cap.mcs), |
1904 | data: &sband->ht_cap.mcs) || |
1905 | nla_put_u16(skb: msg, NL80211_BAND_ATTR_HT_CAPA, |
1906 | value: sband->ht_cap.cap) || |
1907 | nla_put_u8(skb: msg, attrtype: NL80211_BAND_ATTR_HT_AMPDU_FACTOR, |
1908 | value: sband->ht_cap.ampdu_factor) || |
1909 | nla_put_u8(skb: msg, attrtype: NL80211_BAND_ATTR_HT_AMPDU_DENSITY, |
1910 | value: sband->ht_cap.ampdu_density))) |
1911 | return -ENOBUFS; |
1912 | |
1913 | /* add VHT info */ |
1914 | if (sband->vht_cap.vht_supported && |
1915 | (nla_put(skb: msg, attrtype: NL80211_BAND_ATTR_VHT_MCS_SET, |
1916 | attrlen: sizeof(sband->vht_cap.vht_mcs), |
1917 | data: &sband->vht_cap.vht_mcs) || |
1918 | nla_put_u32(skb: msg, attrtype: NL80211_BAND_ATTR_VHT_CAPA, |
1919 | value: sband->vht_cap.cap))) |
1920 | return -ENOBUFS; |
1921 | |
1922 | if (large && sband->n_iftype_data) { |
1923 | struct nlattr *nl_iftype_data = |
1924 | nla_nest_start_noflag(skb: msg, |
1925 | attrtype: NL80211_BAND_ATTR_IFTYPE_DATA); |
1926 | const struct ieee80211_sband_iftype_data *iftd; |
1927 | int err; |
1928 | |
1929 | if (!nl_iftype_data) |
1930 | return -ENOBUFS; |
1931 | |
1932 | for_each_sband_iftype_data(sband, i, iftd) { |
1933 | struct nlattr *iftdata; |
1934 | |
1935 | iftdata = nla_nest_start_noflag(skb: msg, attrtype: i + 1); |
1936 | if (!iftdata) |
1937 | return -ENOBUFS; |
1938 | |
1939 | err = nl80211_send_iftype_data(msg, sband, iftdata: iftd); |
1940 | if (err) |
1941 | return err; |
1942 | |
1943 | nla_nest_end(skb: msg, start: iftdata); |
1944 | } |
1945 | |
1946 | nla_nest_end(skb: msg, start: nl_iftype_data); |
1947 | } |
1948 | |
1949 | /* add EDMG info */ |
1950 | if (large && sband->edmg_cap.channels && |
1951 | (nla_put_u8(skb: msg, attrtype: NL80211_BAND_ATTR_EDMG_CHANNELS, |
1952 | value: sband->edmg_cap.channels) || |
1953 | nla_put_u8(skb: msg, attrtype: NL80211_BAND_ATTR_EDMG_BW_CONFIG, |
1954 | value: sband->edmg_cap.bw_config))) |
1955 | |
1956 | return -ENOBUFS; |
1957 | |
1958 | /* add bitrates */ |
1959 | nl_rates = nla_nest_start_noflag(skb: msg, attrtype: NL80211_BAND_ATTR_RATES); |
1960 | if (!nl_rates) |
1961 | return -ENOBUFS; |
1962 | |
1963 | for (i = 0; i < sband->n_bitrates; i++) { |
1964 | nl_rate = nla_nest_start_noflag(skb: msg, attrtype: i); |
1965 | if (!nl_rate) |
1966 | return -ENOBUFS; |
1967 | |
1968 | rate = &sband->bitrates[i]; |
1969 | if (nla_put_u32(skb: msg, attrtype: NL80211_BITRATE_ATTR_RATE, |
1970 | value: rate->bitrate)) |
1971 | return -ENOBUFS; |
1972 | if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) && |
1973 | nla_put_flag(skb: msg, |
1974 | attrtype: NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE)) |
1975 | return -ENOBUFS; |
1976 | |
1977 | nla_nest_end(skb: msg, start: nl_rate); |
1978 | } |
1979 | |
1980 | nla_nest_end(skb: msg, start: nl_rates); |
1981 | |
1982 | /* S1G capabilities */ |
1983 | if (sband->band == NL80211_BAND_S1GHZ && sband->s1g_cap.s1g && |
1984 | (nla_put(skb: msg, attrtype: NL80211_BAND_ATTR_S1G_CAPA, |
1985 | attrlen: sizeof(sband->s1g_cap.cap), |
1986 | data: sband->s1g_cap.cap) || |
1987 | nla_put(skb: msg, attrtype: NL80211_BAND_ATTR_S1G_MCS_NSS_SET, |
1988 | attrlen: sizeof(sband->s1g_cap.nss_mcs), |
1989 | data: sband->s1g_cap.nss_mcs))) |
1990 | return -ENOBUFS; |
1991 | |
1992 | return 0; |
1993 | } |
1994 | |
1995 | static int |
1996 | nl80211_send_mgmt_stypes(struct sk_buff *msg, |
1997 | const struct ieee80211_txrx_stypes *mgmt_stypes) |
1998 | { |
1999 | u16 stypes; |
2000 | struct nlattr *nl_ftypes, *nl_ifs; |
2001 | enum nl80211_iftype ift; |
2002 | int i; |
2003 | |
2004 | if (!mgmt_stypes) |
2005 | return 0; |
2006 | |
2007 | nl_ifs = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_TX_FRAME_TYPES); |
2008 | if (!nl_ifs) |
2009 | return -ENOBUFS; |
2010 | |
2011 | for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) { |
2012 | nl_ftypes = nla_nest_start_noflag(skb: msg, attrtype: ift); |
2013 | if (!nl_ftypes) |
2014 | return -ENOBUFS; |
2015 | i = 0; |
2016 | stypes = mgmt_stypes[ift].tx; |
2017 | while (stypes) { |
2018 | if ((stypes & 1) && |
2019 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_FRAME_TYPE, |
2020 | value: (i << 4) | IEEE80211_FTYPE_MGMT)) |
2021 | return -ENOBUFS; |
2022 | stypes >>= 1; |
2023 | i++; |
2024 | } |
2025 | nla_nest_end(skb: msg, start: nl_ftypes); |
2026 | } |
2027 | |
2028 | nla_nest_end(skb: msg, start: nl_ifs); |
2029 | |
2030 | nl_ifs = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_RX_FRAME_TYPES); |
2031 | if (!nl_ifs) |
2032 | return -ENOBUFS; |
2033 | |
2034 | for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) { |
2035 | nl_ftypes = nla_nest_start_noflag(skb: msg, attrtype: ift); |
2036 | if (!nl_ftypes) |
2037 | return -ENOBUFS; |
2038 | i = 0; |
2039 | stypes = mgmt_stypes[ift].rx; |
2040 | while (stypes) { |
2041 | if ((stypes & 1) && |
2042 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_FRAME_TYPE, |
2043 | value: (i << 4) | IEEE80211_FTYPE_MGMT)) |
2044 | return -ENOBUFS; |
2045 | stypes >>= 1; |
2046 | i++; |
2047 | } |
2048 | nla_nest_end(skb: msg, start: nl_ftypes); |
2049 | } |
2050 | nla_nest_end(skb: msg, start: nl_ifs); |
2051 | |
2052 | return 0; |
2053 | } |
2054 | |
2055 | #define CMD(op, n) \ |
2056 | do { \ |
2057 | if (rdev->ops->op) { \ |
2058 | i++; \ |
2059 | if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \ |
2060 | goto nla_put_failure; \ |
2061 | } \ |
2062 | } while (0) |
2063 | |
2064 | static int nl80211_add_commands_unsplit(struct cfg80211_registered_device *rdev, |
2065 | struct sk_buff *msg) |
2066 | { |
2067 | int i = 0; |
2068 | |
2069 | /* |
2070 | * do *NOT* add anything into this function, new things need to be |
2071 | * advertised only to new versions of userspace that can deal with |
2072 | * the split (and they can't possibly care about new features... |
2073 | */ |
2074 | CMD(add_virtual_intf, NEW_INTERFACE); |
2075 | CMD(change_virtual_intf, SET_INTERFACE); |
2076 | CMD(add_key, NEW_KEY); |
2077 | CMD(start_ap, START_AP); |
2078 | CMD(add_station, NEW_STATION); |
2079 | CMD(add_mpath, NEW_MPATH); |
2080 | CMD(update_mesh_config, SET_MESH_CONFIG); |
2081 | CMD(change_bss, SET_BSS); |
2082 | CMD(auth, AUTHENTICATE); |
2083 | CMD(assoc, ASSOCIATE); |
2084 | CMD(deauth, DEAUTHENTICATE); |
2085 | CMD(disassoc, DISASSOCIATE); |
2086 | CMD(join_ibss, JOIN_IBSS); |
2087 | CMD(join_mesh, JOIN_MESH); |
2088 | CMD(set_pmksa, SET_PMKSA); |
2089 | CMD(del_pmksa, DEL_PMKSA); |
2090 | CMD(flush_pmksa, FLUSH_PMKSA); |
2091 | if (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) |
2092 | CMD(remain_on_channel, REMAIN_ON_CHANNEL); |
2093 | CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); |
2094 | CMD(mgmt_tx, FRAME); |
2095 | CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL); |
2096 | if (rdev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { |
2097 | i++; |
2098 | if (nla_put_u32(skb: msg, attrtype: i, value: NL80211_CMD_SET_WIPHY_NETNS)) |
2099 | goto nla_put_failure; |
2100 | } |
2101 | if (rdev->ops->set_monitor_channel || rdev->ops->start_ap || |
2102 | rdev->ops->join_mesh) { |
2103 | i++; |
2104 | if (nla_put_u32(skb: msg, attrtype: i, value: NL80211_CMD_SET_CHANNEL)) |
2105 | goto nla_put_failure; |
2106 | } |
2107 | if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) { |
2108 | CMD(tdls_mgmt, TDLS_MGMT); |
2109 | CMD(tdls_oper, TDLS_OPER); |
2110 | } |
2111 | if (rdev->wiphy.max_sched_scan_reqs) |
2112 | CMD(sched_scan_start, START_SCHED_SCAN); |
2113 | CMD(probe_client, PROBE_CLIENT); |
2114 | CMD(set_noack_map, SET_NOACK_MAP); |
2115 | if (rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) { |
2116 | i++; |
2117 | if (nla_put_u32(skb: msg, attrtype: i, value: NL80211_CMD_REGISTER_BEACONS)) |
2118 | goto nla_put_failure; |
2119 | } |
2120 | CMD(start_p2p_device, START_P2P_DEVICE); |
2121 | CMD(set_mcast_rate, SET_MCAST_RATE); |
2122 | #ifdef CONFIG_NL80211_TESTMODE |
2123 | CMD(testmode_cmd, TESTMODE); |
2124 | #endif |
2125 | |
2126 | if (rdev->ops->connect || rdev->ops->auth) { |
2127 | i++; |
2128 | if (nla_put_u32(skb: msg, attrtype: i, NL80211_CMD_CONNECT)) |
2129 | goto nla_put_failure; |
2130 | } |
2131 | |
2132 | if (rdev->ops->disconnect || rdev->ops->deauth) { |
2133 | i++; |
2134 | if (nla_put_u32(skb: msg, attrtype: i, value: NL80211_CMD_DISCONNECT)) |
2135 | goto nla_put_failure; |
2136 | } |
2137 | |
2138 | return i; |
2139 | nla_put_failure: |
2140 | return -ENOBUFS; |
2141 | } |
2142 | |
2143 | static int |
2144 | nl80211_send_pmsr_ftm_capa(const struct cfg80211_pmsr_capabilities *cap, |
2145 | struct sk_buff *msg) |
2146 | { |
2147 | struct nlattr *ftm; |
2148 | |
2149 | if (!cap->ftm.supported) |
2150 | return 0; |
2151 | |
2152 | ftm = nla_nest_start_noflag(skb: msg, attrtype: NL80211_PMSR_TYPE_FTM); |
2153 | if (!ftm) |
2154 | return -ENOBUFS; |
2155 | |
2156 | if (cap->ftm.asap && nla_put_flag(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_ASAP)) |
2157 | return -ENOBUFS; |
2158 | if (cap->ftm.non_asap && |
2159 | nla_put_flag(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP)) |
2160 | return -ENOBUFS; |
2161 | if (cap->ftm.request_lci && |
2162 | nla_put_flag(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI)) |
2163 | return -ENOBUFS; |
2164 | if (cap->ftm.request_civicloc && |
2165 | nla_put_flag(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC)) |
2166 | return -ENOBUFS; |
2167 | if (nla_put_u32(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES, |
2168 | value: cap->ftm.preambles)) |
2169 | return -ENOBUFS; |
2170 | if (nla_put_u32(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS, |
2171 | value: cap->ftm.bandwidths)) |
2172 | return -ENOBUFS; |
2173 | if (cap->ftm.max_bursts_exponent >= 0 && |
2174 | nla_put_u32(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT, |
2175 | value: cap->ftm.max_bursts_exponent)) |
2176 | return -ENOBUFS; |
2177 | if (cap->ftm.max_ftms_per_burst && |
2178 | nla_put_u32(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST, |
2179 | value: cap->ftm.max_ftms_per_burst)) |
2180 | return -ENOBUFS; |
2181 | if (cap->ftm.trigger_based && |
2182 | nla_put_flag(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_TRIGGER_BASED)) |
2183 | return -ENOBUFS; |
2184 | if (cap->ftm.non_trigger_based && |
2185 | nla_put_flag(skb: msg, attrtype: NL80211_PMSR_FTM_CAPA_ATTR_NON_TRIGGER_BASED)) |
2186 | return -ENOBUFS; |
2187 | |
2188 | nla_nest_end(skb: msg, start: ftm); |
2189 | return 0; |
2190 | } |
2191 | |
2192 | static int nl80211_send_pmsr_capa(struct cfg80211_registered_device *rdev, |
2193 | struct sk_buff *msg) |
2194 | { |
2195 | const struct cfg80211_pmsr_capabilities *cap = rdev->wiphy.pmsr_capa; |
2196 | struct nlattr *pmsr, *caps; |
2197 | |
2198 | if (!cap) |
2199 | return 0; |
2200 | |
2201 | /* |
2202 | * we don't need to clean up anything here since the caller |
2203 | * will genlmsg_cancel() if we fail |
2204 | */ |
2205 | |
2206 | pmsr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_PEER_MEASUREMENTS); |
2207 | if (!pmsr) |
2208 | return -ENOBUFS; |
2209 | |
2210 | if (nla_put_u32(skb: msg, attrtype: NL80211_PMSR_ATTR_MAX_PEERS, value: cap->max_peers)) |
2211 | return -ENOBUFS; |
2212 | |
2213 | if (cap->report_ap_tsf && |
2214 | nla_put_flag(skb: msg, attrtype: NL80211_PMSR_ATTR_REPORT_AP_TSF)) |
2215 | return -ENOBUFS; |
2216 | |
2217 | if (cap->randomize_mac_addr && |
2218 | nla_put_flag(skb: msg, attrtype: NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR)) |
2219 | return -ENOBUFS; |
2220 | |
2221 | caps = nla_nest_start_noflag(skb: msg, attrtype: NL80211_PMSR_ATTR_TYPE_CAPA); |
2222 | if (!caps) |
2223 | return -ENOBUFS; |
2224 | |
2225 | if (nl80211_send_pmsr_ftm_capa(cap, msg)) |
2226 | return -ENOBUFS; |
2227 | |
2228 | nla_nest_end(skb: msg, start: caps); |
2229 | nla_nest_end(skb: msg, start: pmsr); |
2230 | |
2231 | return 0; |
2232 | } |
2233 | |
2234 | static int |
2235 | nl80211_put_iftype_akm_suites(struct cfg80211_registered_device *rdev, |
2236 | struct sk_buff *msg) |
2237 | { |
2238 | int i; |
2239 | struct nlattr *nested, *nested_akms; |
2240 | const struct wiphy_iftype_akm_suites *iftype_akms; |
2241 | |
2242 | if (!rdev->wiphy.num_iftype_akm_suites || |
2243 | !rdev->wiphy.iftype_akm_suites) |
2244 | return 0; |
2245 | |
2246 | nested = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_IFTYPE_AKM_SUITES); |
2247 | if (!nested) |
2248 | return -ENOBUFS; |
2249 | |
2250 | for (i = 0; i < rdev->wiphy.num_iftype_akm_suites; i++) { |
2251 | nested_akms = nla_nest_start(skb: msg, attrtype: i + 1); |
2252 | if (!nested_akms) |
2253 | return -ENOBUFS; |
2254 | |
2255 | iftype_akms = &rdev->wiphy.iftype_akm_suites[i]; |
2256 | |
2257 | if (nl80211_put_iftypes(msg, attr: NL80211_IFTYPE_AKM_ATTR_IFTYPES, |
2258 | ifmodes: iftype_akms->iftypes_mask)) |
2259 | return -ENOBUFS; |
2260 | |
2261 | if (nla_put(skb: msg, attrtype: NL80211_IFTYPE_AKM_ATTR_SUITES, |
2262 | attrlen: sizeof(u32) * iftype_akms->n_akm_suites, |
2263 | data: iftype_akms->akm_suites)) { |
2264 | return -ENOBUFS; |
2265 | } |
2266 | nla_nest_end(skb: msg, start: nested_akms); |
2267 | } |
2268 | |
2269 | nla_nest_end(skb: msg, start: nested); |
2270 | |
2271 | return 0; |
2272 | } |
2273 | |
2274 | static int |
2275 | nl80211_put_tid_config_support(struct cfg80211_registered_device *rdev, |
2276 | struct sk_buff *msg) |
2277 | { |
2278 | struct nlattr *supp; |
2279 | |
2280 | if (!rdev->wiphy.tid_config_support.vif && |
2281 | !rdev->wiphy.tid_config_support.peer) |
2282 | return 0; |
2283 | |
2284 | supp = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_TID_CONFIG); |
2285 | if (!supp) |
2286 | return -ENOSPC; |
2287 | |
2288 | if (rdev->wiphy.tid_config_support.vif && |
2289 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_TID_CONFIG_ATTR_VIF_SUPP, |
2290 | value: rdev->wiphy.tid_config_support.vif, |
2291 | padattr: NL80211_TID_CONFIG_ATTR_PAD)) |
2292 | goto fail; |
2293 | |
2294 | if (rdev->wiphy.tid_config_support.peer && |
2295 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_TID_CONFIG_ATTR_PEER_SUPP, |
2296 | value: rdev->wiphy.tid_config_support.peer, |
2297 | padattr: NL80211_TID_CONFIG_ATTR_PAD)) |
2298 | goto fail; |
2299 | |
2300 | /* for now we just use the same value ... makes more sense */ |
2301 | if (nla_put_u8(skb: msg, attrtype: NL80211_TID_CONFIG_ATTR_RETRY_SHORT, |
2302 | value: rdev->wiphy.tid_config_support.max_retry)) |
2303 | goto fail; |
2304 | if (nla_put_u8(skb: msg, attrtype: NL80211_TID_CONFIG_ATTR_RETRY_LONG, |
2305 | value: rdev->wiphy.tid_config_support.max_retry)) |
2306 | goto fail; |
2307 | |
2308 | nla_nest_end(skb: msg, start: supp); |
2309 | |
2310 | return 0; |
2311 | fail: |
2312 | nla_nest_cancel(skb: msg, start: supp); |
2313 | return -ENOBUFS; |
2314 | } |
2315 | |
2316 | static int |
2317 | nl80211_put_sar_specs(struct cfg80211_registered_device *rdev, |
2318 | struct sk_buff *msg) |
2319 | { |
2320 | struct nlattr *sar_capa, *specs, *sub_freq_range; |
2321 | u8 num_freq_ranges; |
2322 | int i; |
2323 | |
2324 | if (!rdev->wiphy.sar_capa) |
2325 | return 0; |
2326 | |
2327 | num_freq_ranges = rdev->wiphy.sar_capa->num_freq_ranges; |
2328 | |
2329 | sar_capa = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_SAR_SPEC); |
2330 | if (!sar_capa) |
2331 | return -ENOSPC; |
2332 | |
2333 | if (nla_put_u32(skb: msg, attrtype: NL80211_SAR_ATTR_TYPE, value: rdev->wiphy.sar_capa->type)) |
2334 | goto fail; |
2335 | |
2336 | specs = nla_nest_start(skb: msg, attrtype: NL80211_SAR_ATTR_SPECS); |
2337 | if (!specs) |
2338 | goto fail; |
2339 | |
2340 | /* report supported freq_ranges */ |
2341 | for (i = 0; i < num_freq_ranges; i++) { |
2342 | sub_freq_range = nla_nest_start(skb: msg, attrtype: i + 1); |
2343 | if (!sub_freq_range) |
2344 | goto fail; |
2345 | |
2346 | if (nla_put_u32(skb: msg, attrtype: NL80211_SAR_ATTR_SPECS_START_FREQ, |
2347 | value: rdev->wiphy.sar_capa->freq_ranges[i].start_freq)) |
2348 | goto fail; |
2349 | |
2350 | if (nla_put_u32(skb: msg, attrtype: NL80211_SAR_ATTR_SPECS_END_FREQ, |
2351 | value: rdev->wiphy.sar_capa->freq_ranges[i].end_freq)) |
2352 | goto fail; |
2353 | |
2354 | nla_nest_end(skb: msg, start: sub_freq_range); |
2355 | } |
2356 | |
2357 | nla_nest_end(skb: msg, start: specs); |
2358 | nla_nest_end(skb: msg, start: sar_capa); |
2359 | |
2360 | return 0; |
2361 | fail: |
2362 | nla_nest_cancel(skb: msg, start: sar_capa); |
2363 | return -ENOBUFS; |
2364 | } |
2365 | |
2366 | static int nl80211_put_mbssid_support(struct wiphy *wiphy, struct sk_buff *msg) |
2367 | { |
2368 | struct nlattr *config; |
2369 | |
2370 | if (!wiphy->mbssid_max_interfaces) |
2371 | return 0; |
2372 | |
2373 | config = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_MBSSID_CONFIG); |
2374 | if (!config) |
2375 | return -ENOBUFS; |
2376 | |
2377 | if (nla_put_u8(skb: msg, attrtype: NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES, |
2378 | value: wiphy->mbssid_max_interfaces)) |
2379 | goto fail; |
2380 | |
2381 | if (wiphy->ema_max_profile_periodicity && |
2382 | nla_put_u8(skb: msg, |
2383 | attrtype: NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY, |
2384 | value: wiphy->ema_max_profile_periodicity)) |
2385 | goto fail; |
2386 | |
2387 | nla_nest_end(skb: msg, start: config); |
2388 | return 0; |
2389 | |
2390 | fail: |
2391 | nla_nest_cancel(skb: msg, start: config); |
2392 | return -ENOBUFS; |
2393 | } |
2394 | |
2395 | struct nl80211_dump_wiphy_state { |
2396 | s64 filter_wiphy; |
2397 | long start; |
2398 | long split_start, band_start, chan_start, capa_start; |
2399 | bool split; |
2400 | }; |
2401 | |
2402 | static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, |
2403 | enum nl80211_commands cmd, |
2404 | struct sk_buff *msg, u32 portid, u32 seq, |
2405 | int flags, struct nl80211_dump_wiphy_state *state) |
2406 | { |
2407 | void *hdr; |
2408 | struct nlattr *nl_bands, *nl_band; |
2409 | struct nlattr *nl_freqs, *nl_freq; |
2410 | struct nlattr *nl_cmds; |
2411 | enum nl80211_band band; |
2412 | struct ieee80211_channel *chan; |
2413 | int i; |
2414 | const struct ieee80211_txrx_stypes *mgmt_stypes = |
2415 | rdev->wiphy.mgmt_stypes; |
2416 | u32 features; |
2417 | |
2418 | hdr = nl80211hdr_put(skb: msg, portid, seq, flags, cmd); |
2419 | if (!hdr) |
2420 | return -ENOBUFS; |
2421 | |
2422 | if (WARN_ON(!state)) |
2423 | return -EINVAL; |
2424 | |
2425 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
2426 | nla_put_string(skb: msg, attrtype: NL80211_ATTR_WIPHY_NAME, |
2427 | str: wiphy_name(wiphy: &rdev->wiphy)) || |
2428 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_GENERATION, |
2429 | value: cfg80211_rdev_list_generation)) |
2430 | goto nla_put_failure; |
2431 | |
2432 | if (cmd != NL80211_CMD_NEW_WIPHY) |
2433 | goto finish; |
2434 | |
2435 | switch (state->split_start) { |
2436 | case 0: |
2437 | if (nla_put_u8(skb: msg, attrtype: NL80211_ATTR_WIPHY_RETRY_SHORT, |
2438 | value: rdev->wiphy.retry_short) || |
2439 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_WIPHY_RETRY_LONG, |
2440 | value: rdev->wiphy.retry_long) || |
2441 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY_FRAG_THRESHOLD, |
2442 | value: rdev->wiphy.frag_threshold) || |
2443 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY_RTS_THRESHOLD, |
2444 | value: rdev->wiphy.rts_threshold) || |
2445 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_WIPHY_COVERAGE_CLASS, |
2446 | value: rdev->wiphy.coverage_class) || |
2447 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MAX_NUM_SCAN_SSIDS, |
2448 | value: rdev->wiphy.max_scan_ssids) || |
2449 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS, |
2450 | value: rdev->wiphy.max_sched_scan_ssids) || |
2451 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_MAX_SCAN_IE_LEN, |
2452 | value: rdev->wiphy.max_scan_ie_len) || |
2453 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN, |
2454 | value: rdev->wiphy.max_sched_scan_ie_len) || |
2455 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MAX_MATCH_SETS, |
2456 | value: rdev->wiphy.max_match_sets)) |
2457 | goto nla_put_failure; |
2458 | |
2459 | if ((rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) && |
2460 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_SUPPORT_IBSS_RSN)) |
2461 | goto nla_put_failure; |
2462 | if ((rdev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) && |
2463 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_SUPPORT_MESH_AUTH)) |
2464 | goto nla_put_failure; |
2465 | if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) && |
2466 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_SUPPORT_AP_UAPSD)) |
2467 | goto nla_put_failure; |
2468 | if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) && |
2469 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_ROAM_SUPPORT)) |
2470 | goto nla_put_failure; |
2471 | if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) && |
2472 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_TDLS_SUPPORT)) |
2473 | goto nla_put_failure; |
2474 | if ((rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) && |
2475 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_TDLS_EXTERNAL_SETUP)) |
2476 | goto nla_put_failure; |
2477 | state->split_start++; |
2478 | if (state->split) |
2479 | break; |
2480 | fallthrough; |
2481 | case 1: |
2482 | if (nla_put(skb: msg, attrtype: NL80211_ATTR_CIPHER_SUITES, |
2483 | attrlen: sizeof(u32) * rdev->wiphy.n_cipher_suites, |
2484 | data: rdev->wiphy.cipher_suites)) |
2485 | goto nla_put_failure; |
2486 | |
2487 | if (nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MAX_NUM_PMKIDS, |
2488 | value: rdev->wiphy.max_num_pmkids)) |
2489 | goto nla_put_failure; |
2490 | |
2491 | if ((rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) && |
2492 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_CONTROL_PORT_ETHERTYPE)) |
2493 | goto nla_put_failure; |
2494 | |
2495 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, |
2496 | value: rdev->wiphy.available_antennas_tx) || |
2497 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, |
2498 | value: rdev->wiphy.available_antennas_rx)) |
2499 | goto nla_put_failure; |
2500 | |
2501 | if ((rdev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) && |
2502 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_PROBE_RESP_OFFLOAD, |
2503 | value: rdev->wiphy.probe_resp_offload)) |
2504 | goto nla_put_failure; |
2505 | |
2506 | if ((rdev->wiphy.available_antennas_tx || |
2507 | rdev->wiphy.available_antennas_rx) && |
2508 | rdev->ops->get_antenna) { |
2509 | u32 tx_ant = 0, rx_ant = 0; |
2510 | int res; |
2511 | |
2512 | res = rdev_get_antenna(rdev, tx_ant: &tx_ant, rx_ant: &rx_ant); |
2513 | if (!res) { |
2514 | if (nla_put_u32(skb: msg, |
2515 | attrtype: NL80211_ATTR_WIPHY_ANTENNA_TX, |
2516 | value: tx_ant) || |
2517 | nla_put_u32(skb: msg, |
2518 | attrtype: NL80211_ATTR_WIPHY_ANTENNA_RX, |
2519 | value: rx_ant)) |
2520 | goto nla_put_failure; |
2521 | } |
2522 | } |
2523 | |
2524 | state->split_start++; |
2525 | if (state->split) |
2526 | break; |
2527 | fallthrough; |
2528 | case 2: |
2529 | if (nl80211_put_iftypes(msg, attr: NL80211_ATTR_SUPPORTED_IFTYPES, |
2530 | ifmodes: rdev->wiphy.interface_modes)) |
2531 | goto nla_put_failure; |
2532 | state->split_start++; |
2533 | if (state->split) |
2534 | break; |
2535 | fallthrough; |
2536 | case 3: |
2537 | nl_bands = nla_nest_start_noflag(skb: msg, |
2538 | attrtype: NL80211_ATTR_WIPHY_BANDS); |
2539 | if (!nl_bands) |
2540 | goto nla_put_failure; |
2541 | |
2542 | for (band = state->band_start; |
2543 | band < (state->split ? |
2544 | NUM_NL80211_BANDS : |
2545 | NL80211_BAND_60GHZ + 1); |
2546 | band++) { |
2547 | struct ieee80211_supported_band *sband; |
2548 | |
2549 | /* omit higher bands for ancient software */ |
2550 | if (band > NL80211_BAND_5GHZ && !state->split) |
2551 | break; |
2552 | |
2553 | sband = rdev->wiphy.bands[band]; |
2554 | |
2555 | if (!sband) |
2556 | continue; |
2557 | |
2558 | nl_band = nla_nest_start_noflag(skb: msg, attrtype: band); |
2559 | if (!nl_band) |
2560 | goto nla_put_failure; |
2561 | |
2562 | switch (state->chan_start) { |
2563 | case 0: |
2564 | if (nl80211_send_band_rateinfo(msg, sband, |
2565 | large: state->split)) |
2566 | goto nla_put_failure; |
2567 | state->chan_start++; |
2568 | if (state->split) |
2569 | break; |
2570 | fallthrough; |
2571 | default: |
2572 | /* add frequencies */ |
2573 | nl_freqs = nla_nest_start_noflag(skb: msg, |
2574 | attrtype: NL80211_BAND_ATTR_FREQS); |
2575 | if (!nl_freqs) |
2576 | goto nla_put_failure; |
2577 | |
2578 | for (i = state->chan_start - 1; |
2579 | i < sband->n_channels; |
2580 | i++) { |
2581 | nl_freq = nla_nest_start_noflag(skb: msg, |
2582 | attrtype: i); |
2583 | if (!nl_freq) |
2584 | goto nla_put_failure; |
2585 | |
2586 | chan = &sband->channels[i]; |
2587 | |
2588 | if (nl80211_msg_put_channel( |
2589 | msg, wiphy: &rdev->wiphy, chan, |
2590 | large: state->split)) |
2591 | goto nla_put_failure; |
2592 | |
2593 | nla_nest_end(skb: msg, start: nl_freq); |
2594 | if (state->split) |
2595 | break; |
2596 | } |
2597 | if (i < sband->n_channels) |
2598 | state->chan_start = i + 2; |
2599 | else |
2600 | state->chan_start = 0; |
2601 | nla_nest_end(skb: msg, start: nl_freqs); |
2602 | } |
2603 | |
2604 | nla_nest_end(skb: msg, start: nl_band); |
2605 | |
2606 | if (state->split) { |
2607 | /* start again here */ |
2608 | if (state->chan_start) |
2609 | band--; |
2610 | break; |
2611 | } |
2612 | } |
2613 | nla_nest_end(skb: msg, start: nl_bands); |
2614 | |
2615 | if (band < NUM_NL80211_BANDS) |
2616 | state->band_start = band + 1; |
2617 | else |
2618 | state->band_start = 0; |
2619 | |
2620 | /* if bands & channels are done, continue outside */ |
2621 | if (state->band_start == 0 && state->chan_start == 0) |
2622 | state->split_start++; |
2623 | if (state->split) |
2624 | break; |
2625 | fallthrough; |
2626 | case 4: |
2627 | nl_cmds = nla_nest_start_noflag(skb: msg, |
2628 | attrtype: NL80211_ATTR_SUPPORTED_COMMANDS); |
2629 | if (!nl_cmds) |
2630 | goto nla_put_failure; |
2631 | |
2632 | i = nl80211_add_commands_unsplit(rdev, msg); |
2633 | if (i < 0) |
2634 | goto nla_put_failure; |
2635 | if (state->split) { |
2636 | CMD(crit_proto_start, CRIT_PROTOCOL_START); |
2637 | CMD(crit_proto_stop, CRIT_PROTOCOL_STOP); |
2638 | if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH) |
2639 | CMD(channel_switch, CHANNEL_SWITCH); |
2640 | CMD(set_qos_map, SET_QOS_MAP); |
2641 | if (rdev->wiphy.features & |
2642 | NL80211_FEATURE_SUPPORTS_WMM_ADMISSION) |
2643 | CMD(add_tx_ts, ADD_TX_TS); |
2644 | CMD(set_multicast_to_unicast, SET_MULTICAST_TO_UNICAST); |
2645 | CMD(update_connect_params, UPDATE_CONNECT_PARAMS); |
2646 | CMD(update_ft_ies, UPDATE_FT_IES); |
2647 | if (rdev->wiphy.sar_capa) |
2648 | CMD(set_sar_specs, SET_SAR_SPECS); |
2649 | } |
2650 | #undef CMD |
2651 | |
2652 | nla_nest_end(skb: msg, start: nl_cmds); |
2653 | state->split_start++; |
2654 | if (state->split) |
2655 | break; |
2656 | fallthrough; |
2657 | case 5: |
2658 | if (rdev->ops->remain_on_channel && |
2659 | (rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) && |
2660 | nla_put_u32(skb: msg, |
2661 | attrtype: NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION, |
2662 | value: rdev->wiphy.max_remain_on_channel_duration)) |
2663 | goto nla_put_failure; |
2664 | |
2665 | if ((rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) && |
2666 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_OFFCHANNEL_TX_OK)) |
2667 | goto nla_put_failure; |
2668 | |
2669 | state->split_start++; |
2670 | if (state->split) |
2671 | break; |
2672 | fallthrough; |
2673 | case 6: |
2674 | #ifdef CONFIG_PM |
2675 | if (nl80211_send_wowlan(msg, rdev, large: state->split)) |
2676 | goto nla_put_failure; |
2677 | state->split_start++; |
2678 | if (state->split) |
2679 | break; |
2680 | #else |
2681 | state->split_start++; |
2682 | #endif |
2683 | fallthrough; |
2684 | case 7: |
2685 | if (nl80211_put_iftypes(msg, attr: NL80211_ATTR_SOFTWARE_IFTYPES, |
2686 | ifmodes: rdev->wiphy.software_iftypes)) |
2687 | goto nla_put_failure; |
2688 | |
2689 | if (nl80211_put_iface_combinations(wiphy: &rdev->wiphy, msg, |
2690 | large: state->split)) |
2691 | goto nla_put_failure; |
2692 | |
2693 | state->split_start++; |
2694 | if (state->split) |
2695 | break; |
2696 | fallthrough; |
2697 | case 8: |
2698 | if ((rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) && |
2699 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_DEVICE_AP_SME, |
2700 | value: rdev->wiphy.ap_sme_capa)) |
2701 | goto nla_put_failure; |
2702 | |
2703 | features = rdev->wiphy.features; |
2704 | /* |
2705 | * We can only add the per-channel limit information if the |
2706 | * dump is split, otherwise it makes it too big. Therefore |
2707 | * only advertise it in that case. |
2708 | */ |
2709 | if (state->split) |
2710 | features |= NL80211_FEATURE_ADVERTISE_CHAN_LIMITS; |
2711 | if (nla_put_u32(skb: msg, NL80211_ATTR_FEATURE_FLAGS, value: features)) |
2712 | goto nla_put_failure; |
2713 | |
2714 | if (rdev->wiphy.ht_capa_mod_mask && |
2715 | nla_put(skb: msg, attrtype: NL80211_ATTR_HT_CAPABILITY_MASK, |
2716 | attrlen: sizeof(*rdev->wiphy.ht_capa_mod_mask), |
2717 | data: rdev->wiphy.ht_capa_mod_mask)) |
2718 | goto nla_put_failure; |
2719 | |
2720 | if (rdev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME && |
2721 | rdev->wiphy.max_acl_mac_addrs && |
2722 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_MAC_ACL_MAX, |
2723 | value: rdev->wiphy.max_acl_mac_addrs)) |
2724 | goto nla_put_failure; |
2725 | |
2726 | /* |
2727 | * Any information below this point is only available to |
2728 | * applications that can deal with it being split. This |
2729 | * helps ensure that newly added capabilities don't break |
2730 | * older tools by overrunning their buffers. |
2731 | * |
2732 | * We still increment split_start so that in the split |
2733 | * case we'll continue with more data in the next round, |
2734 | * but break unconditionally so unsplit data stops here. |
2735 | */ |
2736 | if (state->split) |
2737 | state->split_start++; |
2738 | else |
2739 | state->split_start = 0; |
2740 | break; |
2741 | case 9: |
2742 | if (nl80211_send_mgmt_stypes(msg, mgmt_stypes)) |
2743 | goto nla_put_failure; |
2744 | |
2745 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_MAX_NUM_SCHED_SCAN_PLANS, |
2746 | value: rdev->wiphy.max_sched_scan_plans) || |
2747 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_MAX_SCAN_PLAN_INTERVAL, |
2748 | value: rdev->wiphy.max_sched_scan_plan_interval) || |
2749 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_MAX_SCAN_PLAN_ITERATIONS, |
2750 | value: rdev->wiphy.max_sched_scan_plan_iterations)) |
2751 | goto nla_put_failure; |
2752 | |
2753 | if (rdev->wiphy.extended_capabilities && |
2754 | (nla_put(skb: msg, attrtype: NL80211_ATTR_EXT_CAPA, |
2755 | attrlen: rdev->wiphy.extended_capabilities_len, |
2756 | data: rdev->wiphy.extended_capabilities) || |
2757 | nla_put(skb: msg, attrtype: NL80211_ATTR_EXT_CAPA_MASK, |
2758 | attrlen: rdev->wiphy.extended_capabilities_len, |
2759 | data: rdev->wiphy.extended_capabilities_mask))) |
2760 | goto nla_put_failure; |
2761 | |
2762 | if (rdev->wiphy.vht_capa_mod_mask && |
2763 | nla_put(skb: msg, attrtype: NL80211_ATTR_VHT_CAPABILITY_MASK, |
2764 | attrlen: sizeof(*rdev->wiphy.vht_capa_mod_mask), |
2765 | data: rdev->wiphy.vht_capa_mod_mask)) |
2766 | goto nla_put_failure; |
2767 | |
2768 | if (nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, |
2769 | data: rdev->wiphy.perm_addr)) |
2770 | goto nla_put_failure; |
2771 | |
2772 | if (!is_zero_ether_addr(addr: rdev->wiphy.addr_mask) && |
2773 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC_MASK, ETH_ALEN, |
2774 | data: rdev->wiphy.addr_mask)) |
2775 | goto nla_put_failure; |
2776 | |
2777 | if (rdev->wiphy.n_addresses > 1) { |
2778 | void *attr; |
2779 | |
2780 | attr = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_MAC_ADDRS); |
2781 | if (!attr) |
2782 | goto nla_put_failure; |
2783 | |
2784 | for (i = 0; i < rdev->wiphy.n_addresses; i++) |
2785 | if (nla_put(skb: msg, attrtype: i + 1, ETH_ALEN, |
2786 | data: rdev->wiphy.addresses[i].addr)) |
2787 | goto nla_put_failure; |
2788 | |
2789 | nla_nest_end(skb: msg, start: attr); |
2790 | } |
2791 | |
2792 | state->split_start++; |
2793 | break; |
2794 | case 10: |
2795 | if (nl80211_send_coalesce(msg, rdev)) |
2796 | goto nla_put_failure; |
2797 | |
2798 | if ((rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ) && |
2799 | (nla_put_flag(skb: msg, attrtype: NL80211_ATTR_SUPPORT_5_MHZ) || |
2800 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_SUPPORT_10_MHZ))) |
2801 | goto nla_put_failure; |
2802 | |
2803 | if (rdev->wiphy.max_ap_assoc_sta && |
2804 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_MAX_AP_ASSOC_STA, |
2805 | value: rdev->wiphy.max_ap_assoc_sta)) |
2806 | goto nla_put_failure; |
2807 | |
2808 | state->split_start++; |
2809 | break; |
2810 | case 11: |
2811 | if (rdev->wiphy.n_vendor_commands) { |
2812 | const struct nl80211_vendor_cmd_info *info; |
2813 | struct nlattr *nested; |
2814 | |
2815 | nested = nla_nest_start_noflag(skb: msg, |
2816 | attrtype: NL80211_ATTR_VENDOR_DATA); |
2817 | if (!nested) |
2818 | goto nla_put_failure; |
2819 | |
2820 | for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) { |
2821 | info = &rdev->wiphy.vendor_commands[i].info; |
2822 | if (nla_put(skb: msg, attrtype: i + 1, attrlen: sizeof(*info), data: info)) |
2823 | goto nla_put_failure; |
2824 | } |
2825 | nla_nest_end(skb: msg, start: nested); |
2826 | } |
2827 | |
2828 | if (rdev->wiphy.n_vendor_events) { |
2829 | const struct nl80211_vendor_cmd_info *info; |
2830 | struct nlattr *nested; |
2831 | |
2832 | nested = nla_nest_start_noflag(skb: msg, |
2833 | attrtype: NL80211_ATTR_VENDOR_EVENTS); |
2834 | if (!nested) |
2835 | goto nla_put_failure; |
2836 | |
2837 | for (i = 0; i < rdev->wiphy.n_vendor_events; i++) { |
2838 | info = &rdev->wiphy.vendor_events[i]; |
2839 | if (nla_put(skb: msg, attrtype: i + 1, attrlen: sizeof(*info), data: info)) |
2840 | goto nla_put_failure; |
2841 | } |
2842 | nla_nest_end(skb: msg, start: nested); |
2843 | } |
2844 | state->split_start++; |
2845 | break; |
2846 | case 12: |
2847 | if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH && |
2848 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MAX_CSA_COUNTERS, |
2849 | value: rdev->wiphy.max_num_csa_counters)) |
2850 | goto nla_put_failure; |
2851 | |
2852 | if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED && |
2853 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_WIPHY_SELF_MANAGED_REG)) |
2854 | goto nla_put_failure; |
2855 | |
2856 | if (rdev->wiphy.max_sched_scan_reqs && |
2857 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_SCHED_SCAN_MAX_REQS, |
2858 | value: rdev->wiphy.max_sched_scan_reqs)) |
2859 | goto nla_put_failure; |
2860 | |
2861 | if (nla_put(skb: msg, attrtype: NL80211_ATTR_EXT_FEATURES, |
2862 | attrlen: sizeof(rdev->wiphy.ext_features), |
2863 | data: rdev->wiphy.ext_features)) |
2864 | goto nla_put_failure; |
2865 | |
2866 | if (rdev->wiphy.bss_select_support) { |
2867 | struct nlattr *nested; |
2868 | u32 bss_select_support = rdev->wiphy.bss_select_support; |
2869 | |
2870 | nested = nla_nest_start_noflag(skb: msg, |
2871 | attrtype: NL80211_ATTR_BSS_SELECT); |
2872 | if (!nested) |
2873 | goto nla_put_failure; |
2874 | |
2875 | i = 0; |
2876 | while (bss_select_support) { |
2877 | if ((bss_select_support & 1) && |
2878 | nla_put_flag(skb: msg, attrtype: i)) |
2879 | goto nla_put_failure; |
2880 | i++; |
2881 | bss_select_support >>= 1; |
2882 | } |
2883 | nla_nest_end(skb: msg, start: nested); |
2884 | } |
2885 | |
2886 | state->split_start++; |
2887 | break; |
2888 | case 13: |
2889 | if (rdev->wiphy.num_iftype_ext_capab && |
2890 | rdev->wiphy.iftype_ext_capab) { |
2891 | struct nlattr *nested_ext_capab, *nested; |
2892 | |
2893 | nested = nla_nest_start_noflag(skb: msg, |
2894 | attrtype: NL80211_ATTR_IFTYPE_EXT_CAPA); |
2895 | if (!nested) |
2896 | goto nla_put_failure; |
2897 | |
2898 | for (i = state->capa_start; |
2899 | i < rdev->wiphy.num_iftype_ext_capab; i++) { |
2900 | const struct wiphy_iftype_ext_capab *capab; |
2901 | |
2902 | capab = &rdev->wiphy.iftype_ext_capab[i]; |
2903 | |
2904 | nested_ext_capab = nla_nest_start_noflag(skb: msg, |
2905 | attrtype: i); |
2906 | if (!nested_ext_capab || |
2907 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFTYPE, |
2908 | value: capab->iftype) || |
2909 | nla_put(skb: msg, attrtype: NL80211_ATTR_EXT_CAPA, |
2910 | attrlen: capab->extended_capabilities_len, |
2911 | data: capab->extended_capabilities) || |
2912 | nla_put(skb: msg, attrtype: NL80211_ATTR_EXT_CAPA_MASK, |
2913 | attrlen: capab->extended_capabilities_len, |
2914 | data: capab->extended_capabilities_mask)) |
2915 | goto nla_put_failure; |
2916 | |
2917 | if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_MLO && |
2918 | (nla_put_u16(skb: msg, |
2919 | attrtype: NL80211_ATTR_EML_CAPABILITY, |
2920 | value: capab->eml_capabilities) || |
2921 | nla_put_u16(skb: msg, |
2922 | attrtype: NL80211_ATTR_MLD_CAPA_AND_OPS, |
2923 | value: capab->mld_capa_and_ops))) |
2924 | goto nla_put_failure; |
2925 | |
2926 | nla_nest_end(skb: msg, start: nested_ext_capab); |
2927 | if (state->split) |
2928 | break; |
2929 | } |
2930 | nla_nest_end(skb: msg, start: nested); |
2931 | if (i < rdev->wiphy.num_iftype_ext_capab) { |
2932 | state->capa_start = i + 1; |
2933 | break; |
2934 | } |
2935 | } |
2936 | |
2937 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_BANDS, |
2938 | value: rdev->wiphy.nan_supported_bands)) |
2939 | goto nla_put_failure; |
2940 | |
2941 | if (wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
2942 | ftidx: NL80211_EXT_FEATURE_TXQS)) { |
2943 | struct cfg80211_txq_stats txqstats = {}; |
2944 | int res; |
2945 | |
2946 | res = rdev_get_txq_stats(rdev, NULL, txqstats: &txqstats); |
2947 | if (!res && |
2948 | !nl80211_put_txq_stats(msg, txqstats: &txqstats, |
2949 | attrtype: NL80211_ATTR_TXQ_STATS)) |
2950 | goto nla_put_failure; |
2951 | |
2952 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_TXQ_LIMIT, |
2953 | value: rdev->wiphy.txq_limit)) |
2954 | goto nla_put_failure; |
2955 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_TXQ_MEMORY_LIMIT, |
2956 | value: rdev->wiphy.txq_memory_limit)) |
2957 | goto nla_put_failure; |
2958 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_TXQ_QUANTUM, |
2959 | value: rdev->wiphy.txq_quantum)) |
2960 | goto nla_put_failure; |
2961 | } |
2962 | |
2963 | state->split_start++; |
2964 | break; |
2965 | case 14: |
2966 | if (nl80211_send_pmsr_capa(rdev, msg)) |
2967 | goto nla_put_failure; |
2968 | |
2969 | state->split_start++; |
2970 | break; |
2971 | case 15: |
2972 | if (rdev->wiphy.akm_suites && |
2973 | nla_put(skb: msg, NL80211_ATTR_AKM_SUITES, |
2974 | attrlen: sizeof(u32) * rdev->wiphy.n_akm_suites, |
2975 | data: rdev->wiphy.akm_suites)) |
2976 | goto nla_put_failure; |
2977 | |
2978 | if (nl80211_put_iftype_akm_suites(rdev, msg)) |
2979 | goto nla_put_failure; |
2980 | |
2981 | if (nl80211_put_tid_config_support(rdev, msg)) |
2982 | goto nla_put_failure; |
2983 | state->split_start++; |
2984 | break; |
2985 | case 16: |
2986 | if (nl80211_put_sar_specs(rdev, msg)) |
2987 | goto nla_put_failure; |
2988 | |
2989 | if (nl80211_put_mbssid_support(wiphy: &rdev->wiphy, msg)) |
2990 | goto nla_put_failure; |
2991 | |
2992 | if (nla_put_u16(skb: msg, attrtype: NL80211_ATTR_MAX_NUM_AKM_SUITES, |
2993 | value: rdev->wiphy.max_num_akm_suites)) |
2994 | goto nla_put_failure; |
2995 | |
2996 | if (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_MLO) |
2997 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_MLO_SUPPORT); |
2998 | |
2999 | if (rdev->wiphy.hw_timestamp_max_peers && |
3000 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_MAX_HW_TIMESTAMP_PEERS, |
3001 | value: rdev->wiphy.hw_timestamp_max_peers)) |
3002 | goto nla_put_failure; |
3003 | |
3004 | /* done */ |
3005 | state->split_start = 0; |
3006 | break; |
3007 | } |
3008 | finish: |
3009 | genlmsg_end(skb: msg, hdr); |
3010 | return 0; |
3011 | |
3012 | nla_put_failure: |
3013 | genlmsg_cancel(skb: msg, hdr); |
3014 | return -EMSGSIZE; |
3015 | } |
3016 | |
3017 | static int nl80211_dump_wiphy_parse(struct sk_buff *skb, |
3018 | struct netlink_callback *cb, |
3019 | struct nl80211_dump_wiphy_state *state) |
3020 | { |
3021 | struct nlattr **tb = kcalloc(n: NUM_NL80211_ATTR, size: sizeof(*tb), GFP_KERNEL); |
3022 | int ret; |
3023 | |
3024 | if (!tb) |
3025 | return -ENOMEM; |
3026 | |
3027 | ret = nlmsg_parse_deprecated(nlh: cb->nlh, |
3028 | GENL_HDRLEN + nl80211_fam.hdrsize, |
3029 | tb, maxtype: nl80211_fam.maxattr, |
3030 | policy: nl80211_policy, NULL); |
3031 | /* ignore parse errors for backward compatibility */ |
3032 | if (ret) { |
3033 | ret = 0; |
3034 | goto out; |
3035 | } |
3036 | |
3037 | state->split = tb[NL80211_ATTR_SPLIT_WIPHY_DUMP]; |
3038 | if (tb[NL80211_ATTR_WIPHY]) |
3039 | state->filter_wiphy = nla_get_u32(nla: tb[NL80211_ATTR_WIPHY]); |
3040 | if (tb[NL80211_ATTR_WDEV]) |
3041 | state->filter_wiphy = nla_get_u64(nla: tb[NL80211_ATTR_WDEV]) >> 32; |
3042 | if (tb[NL80211_ATTR_IFINDEX]) { |
3043 | struct net_device *netdev; |
3044 | struct cfg80211_registered_device *rdev; |
3045 | int ifidx = nla_get_u32(nla: tb[NL80211_ATTR_IFINDEX]); |
3046 | |
3047 | netdev = __dev_get_by_index(net: sock_net(sk: skb->sk), ifindex: ifidx); |
3048 | if (!netdev) { |
3049 | ret = -ENODEV; |
3050 | goto out; |
3051 | } |
3052 | if (netdev->ieee80211_ptr) { |
3053 | rdev = wiphy_to_rdev( |
3054 | wiphy: netdev->ieee80211_ptr->wiphy); |
3055 | state->filter_wiphy = rdev->wiphy_idx; |
3056 | } |
3057 | } |
3058 | |
3059 | ret = 0; |
3060 | out: |
3061 | kfree(objp: tb); |
3062 | return ret; |
3063 | } |
3064 | |
3065 | static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) |
3066 | { |
3067 | int idx = 0, ret; |
3068 | struct nl80211_dump_wiphy_state *state = (void *)cb->args[0]; |
3069 | struct cfg80211_registered_device *rdev; |
3070 | |
3071 | rtnl_lock(); |
3072 | if (!state) { |
3073 | state = kzalloc(size: sizeof(*state), GFP_KERNEL); |
3074 | if (!state) { |
3075 | rtnl_unlock(); |
3076 | return -ENOMEM; |
3077 | } |
3078 | state->filter_wiphy = -1; |
3079 | ret = nl80211_dump_wiphy_parse(skb, cb, state); |
3080 | if (ret) { |
3081 | kfree(objp: state); |
3082 | rtnl_unlock(); |
3083 | return ret; |
3084 | } |
3085 | cb->args[0] = (long)state; |
3086 | } |
3087 | |
3088 | for_each_rdev(rdev) { |
3089 | if (!net_eq(net1: wiphy_net(wiphy: &rdev->wiphy), net2: sock_net(sk: skb->sk))) |
3090 | continue; |
3091 | if (++idx <= state->start) |
3092 | continue; |
3093 | if (state->filter_wiphy != -1 && |
3094 | state->filter_wiphy != rdev->wiphy_idx) |
3095 | continue; |
3096 | wiphy_lock(wiphy: &rdev->wiphy); |
3097 | /* attempt to fit multiple wiphy data chunks into the skb */ |
3098 | do { |
3099 | ret = nl80211_send_wiphy(rdev, cmd: NL80211_CMD_NEW_WIPHY, |
3100 | msg: skb, |
3101 | NETLINK_CB(cb->skb).portid, |
3102 | seq: cb->nlh->nlmsg_seq, |
3103 | NLM_F_MULTI, state); |
3104 | if (ret < 0) { |
3105 | /* |
3106 | * If sending the wiphy data didn't fit (ENOBUFS |
3107 | * or EMSGSIZE returned), this SKB is still |
3108 | * empty (so it's not too big because another |
3109 | * wiphy dataset is already in the skb) and |
3110 | * we've not tried to adjust the dump allocation |
3111 | * yet ... then adjust the alloc size to be |
3112 | * bigger, and return 1 but with the empty skb. |
3113 | * This results in an empty message being RX'ed |
3114 | * in userspace, but that is ignored. |
3115 | * |
3116 | * We can then retry with the larger buffer. |
3117 | */ |
3118 | if ((ret == -ENOBUFS || ret == -EMSGSIZE) && |
3119 | !skb->len && !state->split && |
3120 | cb->min_dump_alloc < 4096) { |
3121 | cb->min_dump_alloc = 4096; |
3122 | state->split_start = 0; |
3123 | wiphy_unlock(wiphy: &rdev->wiphy); |
3124 | rtnl_unlock(); |
3125 | return 1; |
3126 | } |
3127 | idx--; |
3128 | break; |
3129 | } |
3130 | } while (state->split_start > 0); |
3131 | wiphy_unlock(wiphy: &rdev->wiphy); |
3132 | break; |
3133 | } |
3134 | rtnl_unlock(); |
3135 | |
3136 | state->start = idx; |
3137 | |
3138 | return skb->len; |
3139 | } |
3140 | |
3141 | static int nl80211_dump_wiphy_done(struct netlink_callback *cb) |
3142 | { |
3143 | kfree(objp: (void *)cb->args[0]); |
3144 | return 0; |
3145 | } |
3146 | |
3147 | static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info) |
3148 | { |
3149 | struct sk_buff *msg; |
3150 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
3151 | struct nl80211_dump_wiphy_state state = {}; |
3152 | |
3153 | msg = nlmsg_new(payload: 4096, GFP_KERNEL); |
3154 | if (!msg) |
3155 | return -ENOMEM; |
3156 | |
3157 | if (nl80211_send_wiphy(rdev, cmd: NL80211_CMD_NEW_WIPHY, msg, |
3158 | portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
3159 | state: &state) < 0) { |
3160 | nlmsg_free(skb: msg); |
3161 | return -ENOBUFS; |
3162 | } |
3163 | |
3164 | return genlmsg_reply(skb: msg, info); |
3165 | } |
3166 | |
3167 | static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = { |
3168 | [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 }, |
3169 | [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 }, |
3170 | [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 }, |
3171 | [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 }, |
3172 | [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 }, |
3173 | }; |
3174 | |
3175 | static int parse_txq_params(struct nlattr *tb[], |
3176 | struct ieee80211_txq_params *txq_params) |
3177 | { |
3178 | u8 ac; |
3179 | |
3180 | if (!tb[NL80211_TXQ_ATTR_AC] || !tb[NL80211_TXQ_ATTR_TXOP] || |
3181 | !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] || |
3182 | !tb[NL80211_TXQ_ATTR_AIFS]) |
3183 | return -EINVAL; |
3184 | |
3185 | ac = nla_get_u8(nla: tb[NL80211_TXQ_ATTR_AC]); |
3186 | txq_params->txop = nla_get_u16(nla: tb[NL80211_TXQ_ATTR_TXOP]); |
3187 | txq_params->cwmin = nla_get_u16(nla: tb[NL80211_TXQ_ATTR_CWMIN]); |
3188 | txq_params->cwmax = nla_get_u16(nla: tb[NL80211_TXQ_ATTR_CWMAX]); |
3189 | txq_params->aifs = nla_get_u8(nla: tb[NL80211_TXQ_ATTR_AIFS]); |
3190 | |
3191 | if (ac >= NL80211_NUM_ACS) |
3192 | return -EINVAL; |
3193 | txq_params->ac = array_index_nospec(ac, NL80211_NUM_ACS); |
3194 | return 0; |
3195 | } |
3196 | |
3197 | static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) |
3198 | { |
3199 | /* |
3200 | * You can only set the channel explicitly for some interfaces, |
3201 | * most have their channel managed via their respective |
3202 | * "establish a connection" command (connect, join, ...) |
3203 | * |
3204 | * For AP/GO and mesh mode, the channel can be set with the |
3205 | * channel userspace API, but is only stored and passed to the |
3206 | * low-level driver when the AP starts or the mesh is joined. |
3207 | * This is for backward compatibility, userspace can also give |
3208 | * the channel in the start-ap or join-mesh commands instead. |
3209 | * |
3210 | * Monitors are special as they are normally slaved to |
3211 | * whatever else is going on, so they have their own special |
3212 | * operation to set the monitor channel if possible. |
3213 | */ |
3214 | return !wdev || |
3215 | wdev->iftype == NL80211_IFTYPE_AP || |
3216 | wdev->iftype == NL80211_IFTYPE_MESH_POINT || |
3217 | wdev->iftype == NL80211_IFTYPE_MONITOR || |
3218 | wdev->iftype == NL80211_IFTYPE_P2P_GO; |
3219 | } |
3220 | |
3221 | static int _nl80211_parse_chandef(struct cfg80211_registered_device *rdev, |
3222 | struct genl_info *info, bool monitor, |
3223 | struct cfg80211_chan_def *chandef) |
3224 | { |
3225 | struct netlink_ext_ack *extack = info->extack; |
3226 | struct nlattr **attrs = info->attrs; |
3227 | u32 control_freq; |
3228 | |
3229 | if (!attrs[NL80211_ATTR_WIPHY_FREQ]) { |
3230 | NL_SET_ERR_MSG_ATTR(extack, attrs[NL80211_ATTR_WIPHY_FREQ], |
3231 | "Frequency is missing" ); |
3232 | return -EINVAL; |
3233 | } |
3234 | |
3235 | control_freq = MHZ_TO_KHZ( |
3236 | nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); |
3237 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]) |
3238 | control_freq += |
3239 | nla_get_u32(nla: info->attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]); |
3240 | |
3241 | memset(chandef, 0, sizeof(*chandef)); |
3242 | chandef->chan = ieee80211_get_channel_khz(wiphy: &rdev->wiphy, freq: control_freq); |
3243 | chandef->width = NL80211_CHAN_WIDTH_20_NOHT; |
3244 | chandef->center_freq1 = KHZ_TO_MHZ(control_freq); |
3245 | chandef->freq1_offset = control_freq % 1000; |
3246 | chandef->center_freq2 = 0; |
3247 | |
3248 | if (!chandef->chan) { |
3249 | NL_SET_ERR_MSG_ATTR(extack, attrs[NL80211_ATTR_WIPHY_FREQ], |
3250 | "Unknown channel" ); |
3251 | return -EINVAL; |
3252 | } |
3253 | |
3254 | if (attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { |
3255 | enum nl80211_channel_type chantype; |
3256 | |
3257 | chantype = nla_get_u32(nla: attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); |
3258 | |
3259 | switch (chantype) { |
3260 | case NL80211_CHAN_NO_HT: |
3261 | case NL80211_CHAN_HT20: |
3262 | case NL80211_CHAN_HT40PLUS: |
3263 | case NL80211_CHAN_HT40MINUS: |
3264 | cfg80211_chandef_create(chandef, channel: chandef->chan, |
3265 | chantype); |
3266 | /* user input for center_freq is incorrect */ |
3267 | if (attrs[NL80211_ATTR_CENTER_FREQ1] && |
3268 | chandef->center_freq1 != nla_get_u32(nla: attrs[NL80211_ATTR_CENTER_FREQ1])) { |
3269 | NL_SET_ERR_MSG_ATTR(extack, |
3270 | attrs[NL80211_ATTR_CENTER_FREQ1], |
3271 | "bad center frequency 1" ); |
3272 | return -EINVAL; |
3273 | } |
3274 | /* center_freq2 must be zero */ |
3275 | if (attrs[NL80211_ATTR_CENTER_FREQ2] && |
3276 | nla_get_u32(nla: attrs[NL80211_ATTR_CENTER_FREQ2])) { |
3277 | NL_SET_ERR_MSG_ATTR(extack, |
3278 | attrs[NL80211_ATTR_CENTER_FREQ2], |
3279 | "center frequency 2 can't be used" ); |
3280 | return -EINVAL; |
3281 | } |
3282 | break; |
3283 | default: |
3284 | NL_SET_ERR_MSG_ATTR(extack, |
3285 | attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE], |
3286 | "invalid channel type" ); |
3287 | return -EINVAL; |
3288 | } |
3289 | } else if (attrs[NL80211_ATTR_CHANNEL_WIDTH]) { |
3290 | chandef->width = |
3291 | nla_get_u32(nla: attrs[NL80211_ATTR_CHANNEL_WIDTH]); |
3292 | if (chandef->chan->band == NL80211_BAND_S1GHZ) { |
3293 | /* User input error for channel width doesn't match channel */ |
3294 | if (chandef->width != ieee80211_s1g_channel_width(chan: chandef->chan)) { |
3295 | NL_SET_ERR_MSG_ATTR(extack, |
3296 | attrs[NL80211_ATTR_CHANNEL_WIDTH], |
3297 | "bad channel width" ); |
3298 | return -EINVAL; |
3299 | } |
3300 | } |
3301 | if (attrs[NL80211_ATTR_CENTER_FREQ1]) { |
3302 | chandef->center_freq1 = |
3303 | nla_get_u32(nla: attrs[NL80211_ATTR_CENTER_FREQ1]); |
3304 | if (attrs[NL80211_ATTR_CENTER_FREQ1_OFFSET]) |
3305 | chandef->freq1_offset = nla_get_u32( |
3306 | nla: attrs[NL80211_ATTR_CENTER_FREQ1_OFFSET]); |
3307 | else |
3308 | chandef->freq1_offset = 0; |
3309 | } |
3310 | if (attrs[NL80211_ATTR_CENTER_FREQ2]) |
3311 | chandef->center_freq2 = |
3312 | nla_get_u32(nla: attrs[NL80211_ATTR_CENTER_FREQ2]); |
3313 | } |
3314 | |
3315 | if (info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNELS]) { |
3316 | chandef->edmg.channels = |
3317 | nla_get_u8(nla: info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNELS]); |
3318 | |
3319 | if (info->attrs[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG]) |
3320 | chandef->edmg.bw_config = |
3321 | nla_get_u8(nla: info->attrs[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG]); |
3322 | } else { |
3323 | chandef->edmg.bw_config = 0; |
3324 | chandef->edmg.channels = 0; |
3325 | } |
3326 | |
3327 | if (info->attrs[NL80211_ATTR_PUNCT_BITMAP]) { |
3328 | chandef->punctured = |
3329 | nla_get_u32(nla: info->attrs[NL80211_ATTR_PUNCT_BITMAP]); |
3330 | |
3331 | if (chandef->punctured && |
3332 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
3333 | ftidx: NL80211_EXT_FEATURE_PUNCT)) { |
3334 | NL_SET_ERR_MSG(extack, |
3335 | "driver doesn't support puncturing" ); |
3336 | return -EINVAL; |
3337 | } |
3338 | } |
3339 | |
3340 | if (!cfg80211_chandef_valid(chandef)) { |
3341 | NL_SET_ERR_MSG(extack, "invalid channel definition" ); |
3342 | return -EINVAL; |
3343 | } |
3344 | |
3345 | if (!_cfg80211_chandef_usable(wiphy: &rdev->wiphy, chandef, |
3346 | prohibited_flags: IEEE80211_CHAN_DISABLED, |
3347 | monitor)) { |
3348 | NL_SET_ERR_MSG(extack, "(extension) channel is disabled" ); |
3349 | return -EINVAL; |
3350 | } |
3351 | |
3352 | if ((chandef->width == NL80211_CHAN_WIDTH_5 || |
3353 | chandef->width == NL80211_CHAN_WIDTH_10) && |
3354 | !(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ)) { |
3355 | NL_SET_ERR_MSG(extack, "5/10 MHz not supported" ); |
3356 | return -EINVAL; |
3357 | } |
3358 | |
3359 | return 0; |
3360 | } |
3361 | |
3362 | int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, |
3363 | struct genl_info *info, |
3364 | struct cfg80211_chan_def *chandef) |
3365 | { |
3366 | return _nl80211_parse_chandef(rdev, info, monitor: false, chandef); |
3367 | } |
3368 | |
3369 | static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, |
3370 | struct net_device *dev, |
3371 | struct genl_info *info, |
3372 | int _link_id) |
3373 | { |
3374 | struct cfg80211_chan_def chandef; |
3375 | int result; |
3376 | enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR; |
3377 | struct wireless_dev *wdev = NULL; |
3378 | int link_id = _link_id; |
3379 | |
3380 | if (dev) |
3381 | wdev = dev->ieee80211_ptr; |
3382 | if (!nl80211_can_set_dev_channel(wdev)) |
3383 | return -EOPNOTSUPP; |
3384 | if (wdev) |
3385 | iftype = wdev->iftype; |
3386 | |
3387 | if (link_id < 0) { |
3388 | if (wdev && wdev->valid_links) |
3389 | return -EINVAL; |
3390 | link_id = 0; |
3391 | } |
3392 | |
3393 | result = _nl80211_parse_chandef(rdev, info, |
3394 | monitor: iftype == NL80211_IFTYPE_MONITOR, |
3395 | chandef: &chandef); |
3396 | if (result) |
3397 | return result; |
3398 | |
3399 | switch (iftype) { |
3400 | case NL80211_IFTYPE_AP: |
3401 | case NL80211_IFTYPE_P2P_GO: |
3402 | if (!cfg80211_reg_can_beacon_relax(wiphy: &rdev->wiphy, chandef: &chandef, |
3403 | iftype)) |
3404 | return -EINVAL; |
3405 | if (wdev->links[link_id].ap.beacon_interval) { |
3406 | struct ieee80211_channel *cur_chan; |
3407 | |
3408 | if (!dev || !rdev->ops->set_ap_chanwidth || |
3409 | !(rdev->wiphy.features & |
3410 | NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)) |
3411 | return -EBUSY; |
3412 | |
3413 | /* Only allow dynamic channel width changes */ |
3414 | cur_chan = wdev->links[link_id].ap.chandef.chan; |
3415 | if (chandef.chan != cur_chan) |
3416 | return -EBUSY; |
3417 | |
3418 | result = rdev_set_ap_chanwidth(rdev, dev, link_id, |
3419 | chandef: &chandef); |
3420 | if (result) |
3421 | return result; |
3422 | wdev->links[link_id].ap.chandef = chandef; |
3423 | } else { |
3424 | wdev->u.ap.preset_chandef = chandef; |
3425 | } |
3426 | return 0; |
3427 | case NL80211_IFTYPE_MESH_POINT: |
3428 | return cfg80211_set_mesh_channel(rdev, wdev, chandef: &chandef); |
3429 | case NL80211_IFTYPE_MONITOR: |
3430 | return cfg80211_set_monitor_channel(rdev, chandef: &chandef); |
3431 | default: |
3432 | break; |
3433 | } |
3434 | |
3435 | return -EINVAL; |
3436 | } |
3437 | |
3438 | static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info) |
3439 | { |
3440 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
3441 | int link_id = nl80211_link_id_or_invalid(attrs: info->attrs); |
3442 | struct net_device *netdev = info->user_ptr[1]; |
3443 | |
3444 | return __nl80211_set_channel(rdev, dev: netdev, info, link_id: link_id); |
3445 | } |
3446 | |
3447 | static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) |
3448 | { |
3449 | struct cfg80211_registered_device *rdev = NULL; |
3450 | struct net_device *netdev = NULL; |
3451 | struct wireless_dev *wdev; |
3452 | int result = 0, rem_txq_params = 0; |
3453 | struct nlattr *nl_txq_params; |
3454 | u32 changed; |
3455 | u8 retry_short = 0, retry_long = 0; |
3456 | u32 frag_threshold = 0, rts_threshold = 0; |
3457 | u8 coverage_class = 0; |
3458 | u32 txq_limit = 0, txq_memory_limit = 0, txq_quantum = 0; |
3459 | |
3460 | rtnl_lock(); |
3461 | /* |
3462 | * Try to find the wiphy and netdev. Normally this |
3463 | * function shouldn't need the netdev, but this is |
3464 | * done for backward compatibility -- previously |
3465 | * setting the channel was done per wiphy, but now |
3466 | * it is per netdev. Previous userland like hostapd |
3467 | * also passed a netdev to set_wiphy, so that it is |
3468 | * possible to let that go to the right netdev! |
3469 | */ |
3470 | |
3471 | if (info->attrs[NL80211_ATTR_IFINDEX]) { |
3472 | int ifindex = nla_get_u32(nla: info->attrs[NL80211_ATTR_IFINDEX]); |
3473 | |
3474 | netdev = __dev_get_by_index(net: genl_info_net(info), ifindex); |
3475 | if (netdev && netdev->ieee80211_ptr) |
3476 | rdev = wiphy_to_rdev(wiphy: netdev->ieee80211_ptr->wiphy); |
3477 | else |
3478 | netdev = NULL; |
3479 | } |
3480 | |
3481 | if (!netdev) { |
3482 | rdev = __cfg80211_rdev_from_attrs(netns: genl_info_net(info), |
3483 | attrs: info->attrs); |
3484 | if (IS_ERR(ptr: rdev)) { |
3485 | rtnl_unlock(); |
3486 | return PTR_ERR(ptr: rdev); |
3487 | } |
3488 | wdev = NULL; |
3489 | netdev = NULL; |
3490 | result = 0; |
3491 | } else |
3492 | wdev = netdev->ieee80211_ptr; |
3493 | |
3494 | wiphy_lock(wiphy: &rdev->wiphy); |
3495 | |
3496 | /* |
3497 | * end workaround code, by now the rdev is available |
3498 | * and locked, and wdev may or may not be NULL. |
3499 | */ |
3500 | |
3501 | if (info->attrs[NL80211_ATTR_WIPHY_NAME]) |
3502 | result = cfg80211_dev_rename( |
3503 | rdev, newname: nla_data(nla: info->attrs[NL80211_ATTR_WIPHY_NAME])); |
3504 | rtnl_unlock(); |
3505 | |
3506 | if (result) |
3507 | goto out; |
3508 | |
3509 | if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) { |
3510 | struct ieee80211_txq_params txq_params; |
3511 | struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1]; |
3512 | |
3513 | if (!rdev->ops->set_txq_params) { |
3514 | result = -EOPNOTSUPP; |
3515 | goto out; |
3516 | } |
3517 | |
3518 | if (!netdev) { |
3519 | result = -EINVAL; |
3520 | goto out; |
3521 | } |
3522 | |
3523 | if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && |
3524 | netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) { |
3525 | result = -EINVAL; |
3526 | goto out; |
3527 | } |
3528 | |
3529 | if (!netif_running(dev: netdev)) { |
3530 | result = -ENETDOWN; |
3531 | goto out; |
3532 | } |
3533 | |
3534 | nla_for_each_nested(nl_txq_params, |
3535 | info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS], |
3536 | rem_txq_params) { |
3537 | result = nla_parse_nested_deprecated(tb, |
3538 | maxtype: NL80211_TXQ_ATTR_MAX, |
3539 | nla: nl_txq_params, |
3540 | policy: txq_params_policy, |
3541 | extack: info->extack); |
3542 | if (result) |
3543 | goto out; |
3544 | result = parse_txq_params(tb, txq_params: &txq_params); |
3545 | if (result) |
3546 | goto out; |
3547 | |
3548 | txq_params.link_id = |
3549 | nl80211_link_id_or_invalid(attrs: info->attrs); |
3550 | |
3551 | if (txq_params.link_id >= 0 && |
3552 | !(netdev->ieee80211_ptr->valid_links & |
3553 | BIT(txq_params.link_id))) |
3554 | result = -ENOLINK; |
3555 | else if (txq_params.link_id >= 0 && |
3556 | !netdev->ieee80211_ptr->valid_links) |
3557 | result = -EINVAL; |
3558 | else |
3559 | result = rdev_set_txq_params(rdev, dev: netdev, |
3560 | params: &txq_params); |
3561 | if (result) |
3562 | goto out; |
3563 | } |
3564 | } |
3565 | |
3566 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { |
3567 | int link_id = nl80211_link_id_or_invalid(attrs: info->attrs); |
3568 | |
3569 | if (wdev) { |
3570 | result = __nl80211_set_channel( |
3571 | rdev, |
3572 | dev: nl80211_can_set_dev_channel(wdev) ? netdev : NULL, |
3573 | info, link_id: link_id); |
3574 | } else { |
3575 | result = __nl80211_set_channel(rdev, dev: netdev, info, link_id: link_id); |
3576 | } |
3577 | |
3578 | if (result) |
3579 | goto out; |
3580 | } |
3581 | |
3582 | if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) { |
3583 | struct wireless_dev *txp_wdev = wdev; |
3584 | enum nl80211_tx_power_setting type; |
3585 | int idx, mbm = 0; |
3586 | |
3587 | if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER)) |
3588 | txp_wdev = NULL; |
3589 | |
3590 | if (!rdev->ops->set_tx_power) { |
3591 | result = -EOPNOTSUPP; |
3592 | goto out; |
3593 | } |
3594 | |
3595 | idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING; |
3596 | type = nla_get_u32(nla: info->attrs[idx]); |
3597 | |
3598 | if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] && |
3599 | (type != NL80211_TX_POWER_AUTOMATIC)) { |
3600 | result = -EINVAL; |
3601 | goto out; |
3602 | } |
3603 | |
3604 | if (type != NL80211_TX_POWER_AUTOMATIC) { |
3605 | idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL; |
3606 | mbm = nla_get_u32(nla: info->attrs[idx]); |
3607 | } |
3608 | |
3609 | result = rdev_set_tx_power(rdev, wdev: txp_wdev, type, mbm); |
3610 | if (result) |
3611 | goto out; |
3612 | } |
3613 | |
3614 | if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] && |
3615 | info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) { |
3616 | u32 tx_ant, rx_ant; |
3617 | |
3618 | if ((!rdev->wiphy.available_antennas_tx && |
3619 | !rdev->wiphy.available_antennas_rx) || |
3620 | !rdev->ops->set_antenna) { |
3621 | result = -EOPNOTSUPP; |
3622 | goto out; |
3623 | } |
3624 | |
3625 | tx_ant = nla_get_u32(nla: info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]); |
3626 | rx_ant = nla_get_u32(nla: info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]); |
3627 | |
3628 | /* reject antenna configurations which don't match the |
3629 | * available antenna masks, except for the "all" mask */ |
3630 | if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) || |
3631 | (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) { |
3632 | result = -EINVAL; |
3633 | goto out; |
3634 | } |
3635 | |
3636 | tx_ant = tx_ant & rdev->wiphy.available_antennas_tx; |
3637 | rx_ant = rx_ant & rdev->wiphy.available_antennas_rx; |
3638 | |
3639 | result = rdev_set_antenna(rdev, tx_ant, rx_ant); |
3640 | if (result) |
3641 | goto out; |
3642 | } |
3643 | |
3644 | changed = 0; |
3645 | |
3646 | if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) { |
3647 | retry_short = nla_get_u8( |
3648 | nla: info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]); |
3649 | |
3650 | changed |= WIPHY_PARAM_RETRY_SHORT; |
3651 | } |
3652 | |
3653 | if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) { |
3654 | retry_long = nla_get_u8( |
3655 | nla: info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]); |
3656 | |
3657 | changed |= WIPHY_PARAM_RETRY_LONG; |
3658 | } |
3659 | |
3660 | if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) { |
3661 | frag_threshold = nla_get_u32( |
3662 | nla: info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]); |
3663 | if (frag_threshold < 256) { |
3664 | result = -EINVAL; |
3665 | goto out; |
3666 | } |
3667 | |
3668 | if (frag_threshold != (u32) -1) { |
3669 | /* |
3670 | * Fragments (apart from the last one) are required to |
3671 | * have even length. Make the fragmentation code |
3672 | * simpler by stripping LSB should someone try to use |
3673 | * odd threshold value. |
3674 | */ |
3675 | frag_threshold &= ~0x1; |
3676 | } |
3677 | changed |= WIPHY_PARAM_FRAG_THRESHOLD; |
3678 | } |
3679 | |
3680 | if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) { |
3681 | rts_threshold = nla_get_u32( |
3682 | nla: info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]); |
3683 | changed |= WIPHY_PARAM_RTS_THRESHOLD; |
3684 | } |
3685 | |
3686 | if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) { |
3687 | if (info->attrs[NL80211_ATTR_WIPHY_DYN_ACK]) { |
3688 | result = -EINVAL; |
3689 | goto out; |
3690 | } |
3691 | |
3692 | coverage_class = nla_get_u8( |
3693 | nla: info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]); |
3694 | changed |= WIPHY_PARAM_COVERAGE_CLASS; |
3695 | } |
3696 | |
3697 | if (info->attrs[NL80211_ATTR_WIPHY_DYN_ACK]) { |
3698 | if (!(rdev->wiphy.features & NL80211_FEATURE_ACKTO_ESTIMATION)) { |
3699 | result = -EOPNOTSUPP; |
3700 | goto out; |
3701 | } |
3702 | |
3703 | changed |= WIPHY_PARAM_DYN_ACK; |
3704 | } |
3705 | |
3706 | if (info->attrs[NL80211_ATTR_TXQ_LIMIT]) { |
3707 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
3708 | ftidx: NL80211_EXT_FEATURE_TXQS)) { |
3709 | result = -EOPNOTSUPP; |
3710 | goto out; |
3711 | } |
3712 | txq_limit = nla_get_u32( |
3713 | nla: info->attrs[NL80211_ATTR_TXQ_LIMIT]); |
3714 | changed |= WIPHY_PARAM_TXQ_LIMIT; |
3715 | } |
3716 | |
3717 | if (info->attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT]) { |
3718 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
3719 | ftidx: NL80211_EXT_FEATURE_TXQS)) { |
3720 | result = -EOPNOTSUPP; |
3721 | goto out; |
3722 | } |
3723 | txq_memory_limit = nla_get_u32( |
3724 | nla: info->attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT]); |
3725 | changed |= WIPHY_PARAM_TXQ_MEMORY_LIMIT; |
3726 | } |
3727 | |
3728 | if (info->attrs[NL80211_ATTR_TXQ_QUANTUM]) { |
3729 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
3730 | ftidx: NL80211_EXT_FEATURE_TXQS)) { |
3731 | result = -EOPNOTSUPP; |
3732 | goto out; |
3733 | } |
3734 | txq_quantum = nla_get_u32( |
3735 | nla: info->attrs[NL80211_ATTR_TXQ_QUANTUM]); |
3736 | changed |= WIPHY_PARAM_TXQ_QUANTUM; |
3737 | } |
3738 | |
3739 | if (changed) { |
3740 | u8 old_retry_short, old_retry_long; |
3741 | u32 old_frag_threshold, old_rts_threshold; |
3742 | u8 old_coverage_class; |
3743 | u32 old_txq_limit, old_txq_memory_limit, old_txq_quantum; |
3744 | |
3745 | if (!rdev->ops->set_wiphy_params) { |
3746 | result = -EOPNOTSUPP; |
3747 | goto out; |
3748 | } |
3749 | |
3750 | old_retry_short = rdev->wiphy.retry_short; |
3751 | old_retry_long = rdev->wiphy.retry_long; |
3752 | old_frag_threshold = rdev->wiphy.frag_threshold; |
3753 | old_rts_threshold = rdev->wiphy.rts_threshold; |
3754 | old_coverage_class = rdev->wiphy.coverage_class; |
3755 | old_txq_limit = rdev->wiphy.txq_limit; |
3756 | old_txq_memory_limit = rdev->wiphy.txq_memory_limit; |
3757 | old_txq_quantum = rdev->wiphy.txq_quantum; |
3758 | |
3759 | if (changed & WIPHY_PARAM_RETRY_SHORT) |
3760 | rdev->wiphy.retry_short = retry_short; |
3761 | if (changed & WIPHY_PARAM_RETRY_LONG) |
3762 | rdev->wiphy.retry_long = retry_long; |
3763 | if (changed & WIPHY_PARAM_FRAG_THRESHOLD) |
3764 | rdev->wiphy.frag_threshold = frag_threshold; |
3765 | if (changed & WIPHY_PARAM_RTS_THRESHOLD) |
3766 | rdev->wiphy.rts_threshold = rts_threshold; |
3767 | if (changed & WIPHY_PARAM_COVERAGE_CLASS) |
3768 | rdev->wiphy.coverage_class = coverage_class; |
3769 | if (changed & WIPHY_PARAM_TXQ_LIMIT) |
3770 | rdev->wiphy.txq_limit = txq_limit; |
3771 | if (changed & WIPHY_PARAM_TXQ_MEMORY_LIMIT) |
3772 | rdev->wiphy.txq_memory_limit = txq_memory_limit; |
3773 | if (changed & WIPHY_PARAM_TXQ_QUANTUM) |
3774 | rdev->wiphy.txq_quantum = txq_quantum; |
3775 | |
3776 | result = rdev_set_wiphy_params(rdev, changed); |
3777 | if (result) { |
3778 | rdev->wiphy.retry_short = old_retry_short; |
3779 | rdev->wiphy.retry_long = old_retry_long; |
3780 | rdev->wiphy.frag_threshold = old_frag_threshold; |
3781 | rdev->wiphy.rts_threshold = old_rts_threshold; |
3782 | rdev->wiphy.coverage_class = old_coverage_class; |
3783 | rdev->wiphy.txq_limit = old_txq_limit; |
3784 | rdev->wiphy.txq_memory_limit = old_txq_memory_limit; |
3785 | rdev->wiphy.txq_quantum = old_txq_quantum; |
3786 | goto out; |
3787 | } |
3788 | } |
3789 | |
3790 | result = 0; |
3791 | |
3792 | out: |
3793 | wiphy_unlock(wiphy: &rdev->wiphy); |
3794 | return result; |
3795 | } |
3796 | |
3797 | int nl80211_send_chandef(struct sk_buff *msg, const struct cfg80211_chan_def *chandef) |
3798 | { |
3799 | if (WARN_ON(!cfg80211_chandef_valid(chandef))) |
3800 | return -EINVAL; |
3801 | |
3802 | if (nla_put_u32(skb: msg, NL80211_ATTR_WIPHY_FREQ, |
3803 | value: chandef->chan->center_freq)) |
3804 | return -ENOBUFS; |
3805 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY_FREQ_OFFSET, |
3806 | value: chandef->chan->freq_offset)) |
3807 | return -ENOBUFS; |
3808 | switch (chandef->width) { |
3809 | case NL80211_CHAN_WIDTH_20_NOHT: |
3810 | case NL80211_CHAN_WIDTH_20: |
3811 | case NL80211_CHAN_WIDTH_40: |
3812 | if (nla_put_u32(skb: msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, |
3813 | value: cfg80211_get_chandef_type(chandef))) |
3814 | return -ENOBUFS; |
3815 | break; |
3816 | default: |
3817 | break; |
3818 | } |
3819 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CHANNEL_WIDTH, value: chandef->width)) |
3820 | return -ENOBUFS; |
3821 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CENTER_FREQ1, value: chandef->center_freq1)) |
3822 | return -ENOBUFS; |
3823 | if (chandef->center_freq2 && |
3824 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CENTER_FREQ2, value: chandef->center_freq2)) |
3825 | return -ENOBUFS; |
3826 | if (chandef->punctured && |
3827 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_PUNCT_BITMAP, value: chandef->punctured)) |
3828 | return -ENOBUFS; |
3829 | |
3830 | return 0; |
3831 | } |
3832 | EXPORT_SYMBOL(nl80211_send_chandef); |
3833 | |
3834 | static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags, |
3835 | struct cfg80211_registered_device *rdev, |
3836 | struct wireless_dev *wdev, |
3837 | enum nl80211_commands cmd) |
3838 | { |
3839 | struct net_device *dev = wdev->netdev; |
3840 | void *hdr; |
3841 | |
3842 | lockdep_assert_wiphy(&rdev->wiphy); |
3843 | |
3844 | WARN_ON(cmd != NL80211_CMD_NEW_INTERFACE && |
3845 | cmd != NL80211_CMD_DEL_INTERFACE && |
3846 | cmd != NL80211_CMD_SET_INTERFACE); |
3847 | |
3848 | hdr = nl80211hdr_put(skb: msg, portid, seq, flags, cmd); |
3849 | if (!hdr) |
3850 | return -1; |
3851 | |
3852 | if (dev && |
3853 | (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || |
3854 | nla_put_string(skb: msg, attrtype: NL80211_ATTR_IFNAME, str: dev->name))) |
3855 | goto nla_put_failure; |
3856 | |
3857 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
3858 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFTYPE, value: wdev->iftype) || |
3859 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), |
3860 | padattr: NL80211_ATTR_PAD) || |
3861 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: wdev_address(wdev)) || |
3862 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_GENERATION, |
3863 | value: rdev->devlist_generation ^ |
3864 | (cfg80211_rdev_list_generation << 2)) || |
3865 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_4ADDR, value: wdev->use_4addr)) |
3866 | goto nla_put_failure; |
3867 | |
3868 | if (rdev->ops->get_channel && !wdev->valid_links) { |
3869 | struct cfg80211_chan_def chandef = {}; |
3870 | int ret; |
3871 | |
3872 | ret = rdev_get_channel(rdev, wdev, link_id: 0, chandef: &chandef); |
3873 | if (ret == 0 && nl80211_send_chandef(msg, &chandef)) |
3874 | goto nla_put_failure; |
3875 | } |
3876 | |
3877 | if (rdev->ops->get_tx_power) { |
3878 | int dbm, ret; |
3879 | |
3880 | ret = rdev_get_tx_power(rdev, wdev, dbm: &dbm); |
3881 | if (ret == 0 && |
3882 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY_TX_POWER_LEVEL, |
3883 | DBM_TO_MBM(dbm))) |
3884 | goto nla_put_failure; |
3885 | } |
3886 | |
3887 | switch (wdev->iftype) { |
3888 | case NL80211_IFTYPE_AP: |
3889 | case NL80211_IFTYPE_P2P_GO: |
3890 | if (wdev->u.ap.ssid_len && |
3891 | nla_put(skb: msg, NL80211_ATTR_SSID, attrlen: wdev->u.ap.ssid_len, |
3892 | data: wdev->u.ap.ssid)) |
3893 | goto nla_put_failure; |
3894 | break; |
3895 | case NL80211_IFTYPE_STATION: |
3896 | case NL80211_IFTYPE_P2P_CLIENT: |
3897 | if (wdev->u.client.ssid_len && |
3898 | nla_put(skb: msg, NL80211_ATTR_SSID, attrlen: wdev->u.client.ssid_len, |
3899 | data: wdev->u.client.ssid)) |
3900 | goto nla_put_failure; |
3901 | break; |
3902 | case NL80211_IFTYPE_ADHOC: |
3903 | if (wdev->u.ibss.ssid_len && |
3904 | nla_put(skb: msg, NL80211_ATTR_SSID, attrlen: wdev->u.ibss.ssid_len, |
3905 | data: wdev->u.ibss.ssid)) |
3906 | goto nla_put_failure; |
3907 | break; |
3908 | default: |
3909 | /* nothing */ |
3910 | break; |
3911 | } |
3912 | |
3913 | if (rdev->ops->get_txq_stats) { |
3914 | struct cfg80211_txq_stats txqstats = {}; |
3915 | int ret = rdev_get_txq_stats(rdev, wdev, txqstats: &txqstats); |
3916 | |
3917 | if (ret == 0 && |
3918 | !nl80211_put_txq_stats(msg, txqstats: &txqstats, |
3919 | attrtype: NL80211_ATTR_TXQ_STATS)) |
3920 | goto nla_put_failure; |
3921 | } |
3922 | |
3923 | if (wdev->valid_links) { |
3924 | unsigned int link_id; |
3925 | struct nlattr *links = nla_nest_start(skb: msg, |
3926 | attrtype: NL80211_ATTR_MLO_LINKS); |
3927 | |
3928 | if (!links) |
3929 | goto nla_put_failure; |
3930 | |
3931 | for_each_valid_link(wdev, link_id) { |
3932 | struct nlattr *link = nla_nest_start(skb: msg, attrtype: link_id + 1); |
3933 | struct cfg80211_chan_def chandef = {}; |
3934 | int ret; |
3935 | |
3936 | if (!link) |
3937 | goto nla_put_failure; |
3938 | |
3939 | if (nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: link_id)) |
3940 | goto nla_put_failure; |
3941 | if (nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, |
3942 | data: wdev->links[link_id].addr)) |
3943 | goto nla_put_failure; |
3944 | |
3945 | ret = rdev_get_channel(rdev, wdev, link_id, chandef: &chandef); |
3946 | if (ret == 0 && nl80211_send_chandef(msg, &chandef)) |
3947 | goto nla_put_failure; |
3948 | |
3949 | nla_nest_end(skb: msg, start: link); |
3950 | } |
3951 | |
3952 | nla_nest_end(skb: msg, start: links); |
3953 | } |
3954 | |
3955 | genlmsg_end(skb: msg, hdr); |
3956 | return 0; |
3957 | |
3958 | nla_put_failure: |
3959 | genlmsg_cancel(skb: msg, hdr); |
3960 | return -EMSGSIZE; |
3961 | } |
3962 | |
3963 | static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb) |
3964 | { |
3965 | int wp_idx = 0; |
3966 | int if_idx = 0; |
3967 | int wp_start = cb->args[0]; |
3968 | int if_start = cb->args[1]; |
3969 | int filter_wiphy = -1; |
3970 | struct cfg80211_registered_device *rdev; |
3971 | struct wireless_dev *wdev; |
3972 | int ret; |
3973 | |
3974 | rtnl_lock(); |
3975 | if (!cb->args[2]) { |
3976 | struct nl80211_dump_wiphy_state state = { |
3977 | .filter_wiphy = -1, |
3978 | }; |
3979 | |
3980 | ret = nl80211_dump_wiphy_parse(skb, cb, state: &state); |
3981 | if (ret) |
3982 | goto out_unlock; |
3983 | |
3984 | filter_wiphy = state.filter_wiphy; |
3985 | |
3986 | /* |
3987 | * if filtering, set cb->args[2] to +1 since 0 is the default |
3988 | * value needed to determine that parsing is necessary. |
3989 | */ |
3990 | if (filter_wiphy >= 0) |
3991 | cb->args[2] = filter_wiphy + 1; |
3992 | else |
3993 | cb->args[2] = -1; |
3994 | } else if (cb->args[2] > 0) { |
3995 | filter_wiphy = cb->args[2] - 1; |
3996 | } |
3997 | |
3998 | for_each_rdev(rdev) { |
3999 | if (!net_eq(net1: wiphy_net(wiphy: &rdev->wiphy), net2: sock_net(sk: skb->sk))) |
4000 | continue; |
4001 | if (wp_idx < wp_start) { |
4002 | wp_idx++; |
4003 | continue; |
4004 | } |
4005 | |
4006 | if (filter_wiphy >= 0 && filter_wiphy != rdev->wiphy_idx) |
4007 | continue; |
4008 | |
4009 | if_idx = 0; |
4010 | |
4011 | wiphy_lock(wiphy: &rdev->wiphy); |
4012 | list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { |
4013 | if (if_idx < if_start) { |
4014 | if_idx++; |
4015 | continue; |
4016 | } |
4017 | if (nl80211_send_iface(msg: skb, NETLINK_CB(cb->skb).portid, |
4018 | seq: cb->nlh->nlmsg_seq, NLM_F_MULTI, |
4019 | rdev, wdev, |
4020 | cmd: NL80211_CMD_NEW_INTERFACE) < 0) { |
4021 | wiphy_unlock(wiphy: &rdev->wiphy); |
4022 | goto out; |
4023 | } |
4024 | if_idx++; |
4025 | } |
4026 | wiphy_unlock(wiphy: &rdev->wiphy); |
4027 | |
4028 | if_start = 0; |
4029 | wp_idx++; |
4030 | } |
4031 | out: |
4032 | cb->args[0] = wp_idx; |
4033 | cb->args[1] = if_idx; |
4034 | |
4035 | ret = skb->len; |
4036 | out_unlock: |
4037 | rtnl_unlock(); |
4038 | |
4039 | return ret; |
4040 | } |
4041 | |
4042 | static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) |
4043 | { |
4044 | struct sk_buff *msg; |
4045 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
4046 | struct wireless_dev *wdev = info->user_ptr[1]; |
4047 | |
4048 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
4049 | if (!msg) |
4050 | return -ENOMEM; |
4051 | |
4052 | if (nl80211_send_iface(msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
4053 | rdev, wdev, cmd: NL80211_CMD_NEW_INTERFACE) < 0) { |
4054 | nlmsg_free(skb: msg); |
4055 | return -ENOBUFS; |
4056 | } |
4057 | |
4058 | return genlmsg_reply(skb: msg, info); |
4059 | } |
4060 | |
4061 | static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = { |
4062 | [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG }, |
4063 | [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG }, |
4064 | [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG }, |
4065 | [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG }, |
4066 | [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG }, |
4067 | [NL80211_MNTR_FLAG_ACTIVE] = { .type = NLA_FLAG }, |
4068 | }; |
4069 | |
4070 | static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) |
4071 | { |
4072 | struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1]; |
4073 | int flag; |
4074 | |
4075 | *mntrflags = 0; |
4076 | |
4077 | if (!nla) |
4078 | return -EINVAL; |
4079 | |
4080 | if (nla_parse_nested_deprecated(tb: flags, maxtype: NL80211_MNTR_FLAG_MAX, nla, policy: mntr_flags_policy, NULL)) |
4081 | return -EINVAL; |
4082 | |
4083 | for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++) |
4084 | if (flags[flag]) |
4085 | *mntrflags |= (1<<flag); |
4086 | |
4087 | *mntrflags |= MONITOR_FLAG_CHANGED; |
4088 | |
4089 | return 0; |
4090 | } |
4091 | |
4092 | static int nl80211_parse_mon_options(struct cfg80211_registered_device *rdev, |
4093 | enum nl80211_iftype type, |
4094 | struct genl_info *info, |
4095 | struct vif_params *params) |
4096 | { |
4097 | bool change = false; |
4098 | int err; |
4099 | |
4100 | if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { |
4101 | if (type != NL80211_IFTYPE_MONITOR) |
4102 | return -EINVAL; |
4103 | |
4104 | err = parse_monitor_flags(nla: info->attrs[NL80211_ATTR_MNTR_FLAGS], |
4105 | mntrflags: ¶ms->flags); |
4106 | if (err) |
4107 | return err; |
4108 | |
4109 | change = true; |
4110 | } |
4111 | |
4112 | if (params->flags & MONITOR_FLAG_ACTIVE && |
4113 | !(rdev->wiphy.features & NL80211_FEATURE_ACTIVE_MONITOR)) |
4114 | return -EOPNOTSUPP; |
4115 | |
4116 | if (info->attrs[NL80211_ATTR_MU_MIMO_GROUP_DATA]) { |
4117 | const u8 *mumimo_groups; |
4118 | u32 cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER; |
4119 | |
4120 | if (type != NL80211_IFTYPE_MONITOR) |
4121 | return -EINVAL; |
4122 | |
4123 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, ftidx: cap_flag)) |
4124 | return -EOPNOTSUPP; |
4125 | |
4126 | mumimo_groups = |
4127 | nla_data(nla: info->attrs[NL80211_ATTR_MU_MIMO_GROUP_DATA]); |
4128 | |
4129 | /* bits 0 and 63 are reserved and must be zero */ |
4130 | if ((mumimo_groups[0] & BIT(0)) || |
4131 | (mumimo_groups[VHT_MUMIMO_GROUPS_DATA_LEN - 1] & BIT(7))) |
4132 | return -EINVAL; |
4133 | |
4134 | params->vht_mumimo_groups = mumimo_groups; |
4135 | change = true; |
4136 | } |
4137 | |
4138 | if (info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR]) { |
4139 | u32 cap_flag = NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER; |
4140 | |
4141 | if (type != NL80211_IFTYPE_MONITOR) |
4142 | return -EINVAL; |
4143 | |
4144 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, ftidx: cap_flag)) |
4145 | return -EOPNOTSUPP; |
4146 | |
4147 | params->vht_mumimo_follow_addr = |
4148 | nla_data(nla: info->attrs[NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR]); |
4149 | change = true; |
4150 | } |
4151 | |
4152 | return change ? 1 : 0; |
4153 | } |
4154 | |
4155 | static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev, |
4156 | struct net_device *netdev, u8 use_4addr, |
4157 | enum nl80211_iftype iftype) |
4158 | { |
4159 | if (!use_4addr) { |
4160 | if (netdev && netif_is_bridge_port(dev: netdev)) |
4161 | return -EBUSY; |
4162 | return 0; |
4163 | } |
4164 | |
4165 | switch (iftype) { |
4166 | case NL80211_IFTYPE_AP_VLAN: |
4167 | if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP) |
4168 | return 0; |
4169 | break; |
4170 | case NL80211_IFTYPE_STATION: |
4171 | if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION) |
4172 | return 0; |
4173 | break; |
4174 | default: |
4175 | break; |
4176 | } |
4177 | |
4178 | return -EOPNOTSUPP; |
4179 | } |
4180 | |
4181 | static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) |
4182 | { |
4183 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
4184 | struct vif_params params; |
4185 | int err; |
4186 | enum nl80211_iftype otype, ntype; |
4187 | struct net_device *dev = info->user_ptr[1]; |
4188 | bool change = false; |
4189 | |
4190 | memset(¶ms, 0, sizeof(params)); |
4191 | |
4192 | otype = ntype = dev->ieee80211_ptr->iftype; |
4193 | |
4194 | if (info->attrs[NL80211_ATTR_IFTYPE]) { |
4195 | ntype = nla_get_u32(nla: info->attrs[NL80211_ATTR_IFTYPE]); |
4196 | if (otype != ntype) |
4197 | change = true; |
4198 | } |
4199 | |
4200 | if (info->attrs[NL80211_ATTR_MESH_ID]) { |
4201 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
4202 | |
4203 | if (ntype != NL80211_IFTYPE_MESH_POINT) |
4204 | return -EINVAL; |
4205 | if (otype != NL80211_IFTYPE_MESH_POINT) |
4206 | return -EINVAL; |
4207 | if (netif_running(dev)) |
4208 | return -EBUSY; |
4209 | |
4210 | wdev->u.mesh.id_up_len = |
4211 | nla_len(nla: info->attrs[NL80211_ATTR_MESH_ID]); |
4212 | memcpy(wdev->u.mesh.id, |
4213 | nla_data(info->attrs[NL80211_ATTR_MESH_ID]), |
4214 | wdev->u.mesh.id_up_len); |
4215 | } |
4216 | |
4217 | if (info->attrs[NL80211_ATTR_4ADDR]) { |
4218 | params.use_4addr = !!nla_get_u8(nla: info->attrs[NL80211_ATTR_4ADDR]); |
4219 | change = true; |
4220 | err = nl80211_valid_4addr(rdev, netdev: dev, use_4addr: params.use_4addr, iftype: ntype); |
4221 | if (err) |
4222 | return err; |
4223 | } else { |
4224 | params.use_4addr = -1; |
4225 | } |
4226 | |
4227 | err = nl80211_parse_mon_options(rdev, type: ntype, info, params: ¶ms); |
4228 | if (err < 0) |
4229 | return err; |
4230 | if (err > 0) |
4231 | change = true; |
4232 | |
4233 | if (change) |
4234 | err = cfg80211_change_iface(rdev, dev, ntype, params: ¶ms); |
4235 | else |
4236 | err = 0; |
4237 | |
4238 | if (!err && params.use_4addr != -1) |
4239 | dev->ieee80211_ptr->use_4addr = params.use_4addr; |
4240 | |
4241 | if (change && !err) { |
4242 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
4243 | |
4244 | nl80211_notify_iface(rdev, wdev, cmd: NL80211_CMD_SET_INTERFACE); |
4245 | } |
4246 | |
4247 | return err; |
4248 | } |
4249 | |
4250 | static int _nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) |
4251 | { |
4252 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
4253 | struct vif_params params; |
4254 | struct wireless_dev *wdev; |
4255 | struct sk_buff *msg; |
4256 | int err; |
4257 | enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; |
4258 | |
4259 | memset(¶ms, 0, sizeof(params)); |
4260 | |
4261 | if (!info->attrs[NL80211_ATTR_IFNAME]) |
4262 | return -EINVAL; |
4263 | |
4264 | if (info->attrs[NL80211_ATTR_IFTYPE]) |
4265 | type = nla_get_u32(nla: info->attrs[NL80211_ATTR_IFTYPE]); |
4266 | |
4267 | if (!rdev->ops->add_virtual_intf) |
4268 | return -EOPNOTSUPP; |
4269 | |
4270 | if ((type == NL80211_IFTYPE_P2P_DEVICE || type == NL80211_IFTYPE_NAN || |
4271 | rdev->wiphy.features & NL80211_FEATURE_MAC_ON_CREATE) && |
4272 | info->attrs[NL80211_ATTR_MAC]) { |
4273 | nla_memcpy(dest: params.macaddr, src: info->attrs[NL80211_ATTR_MAC], |
4274 | ETH_ALEN); |
4275 | if (!is_valid_ether_addr(addr: params.macaddr)) |
4276 | return -EADDRNOTAVAIL; |
4277 | } |
4278 | |
4279 | if (info->attrs[NL80211_ATTR_4ADDR]) { |
4280 | params.use_4addr = !!nla_get_u8(nla: info->attrs[NL80211_ATTR_4ADDR]); |
4281 | err = nl80211_valid_4addr(rdev, NULL, use_4addr: params.use_4addr, iftype: type); |
4282 | if (err) |
4283 | return err; |
4284 | } |
4285 | |
4286 | if (!cfg80211_iftype_allowed(wiphy: &rdev->wiphy, iftype: type, is_4addr: params.use_4addr, check_swif: 0)) |
4287 | return -EOPNOTSUPP; |
4288 | |
4289 | err = nl80211_parse_mon_options(rdev, type, info, params: ¶ms); |
4290 | if (err < 0) |
4291 | return err; |
4292 | |
4293 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
4294 | if (!msg) |
4295 | return -ENOMEM; |
4296 | |
4297 | wdev = rdev_add_virtual_intf(rdev, |
4298 | name: nla_data(nla: info->attrs[NL80211_ATTR_IFNAME]), |
4299 | NET_NAME_USER, type, params: ¶ms); |
4300 | if (WARN_ON(!wdev)) { |
4301 | nlmsg_free(skb: msg); |
4302 | return -EPROTO; |
4303 | } else if (IS_ERR(ptr: wdev)) { |
4304 | nlmsg_free(skb: msg); |
4305 | return PTR_ERR(ptr: wdev); |
4306 | } |
4307 | |
4308 | if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) |
4309 | wdev->owner_nlportid = info->snd_portid; |
4310 | |
4311 | switch (type) { |
4312 | case NL80211_IFTYPE_MESH_POINT: |
4313 | if (!info->attrs[NL80211_ATTR_MESH_ID]) |
4314 | break; |
4315 | wdev->u.mesh.id_up_len = |
4316 | nla_len(nla: info->attrs[NL80211_ATTR_MESH_ID]); |
4317 | memcpy(wdev->u.mesh.id, |
4318 | nla_data(info->attrs[NL80211_ATTR_MESH_ID]), |
4319 | wdev->u.mesh.id_up_len); |
4320 | break; |
4321 | case NL80211_IFTYPE_NAN: |
4322 | case NL80211_IFTYPE_P2P_DEVICE: |
4323 | /* |
4324 | * P2P Device and NAN do not have a netdev, so don't go |
4325 | * through the netdev notifier and must be added here |
4326 | */ |
4327 | cfg80211_init_wdev(wdev); |
4328 | cfg80211_register_wdev(rdev, wdev); |
4329 | break; |
4330 | default: |
4331 | break; |
4332 | } |
4333 | |
4334 | if (nl80211_send_iface(msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
4335 | rdev, wdev, cmd: NL80211_CMD_NEW_INTERFACE) < 0) { |
4336 | nlmsg_free(skb: msg); |
4337 | return -ENOBUFS; |
4338 | } |
4339 | |
4340 | return genlmsg_reply(skb: msg, info); |
4341 | } |
4342 | |
4343 | static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) |
4344 | { |
4345 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
4346 | int ret; |
4347 | |
4348 | /* to avoid failing a new interface creation due to pending removal */ |
4349 | cfg80211_destroy_ifaces(rdev); |
4350 | |
4351 | wiphy_lock(wiphy: &rdev->wiphy); |
4352 | ret = _nl80211_new_interface(skb, info); |
4353 | wiphy_unlock(wiphy: &rdev->wiphy); |
4354 | |
4355 | return ret; |
4356 | } |
4357 | |
4358 | static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) |
4359 | { |
4360 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
4361 | struct wireless_dev *wdev = info->user_ptr[1]; |
4362 | |
4363 | if (!rdev->ops->del_virtual_intf) |
4364 | return -EOPNOTSUPP; |
4365 | |
4366 | /* |
4367 | * We hold RTNL, so this is safe, without RTNL opencount cannot |
4368 | * reach 0, and thus the rdev cannot be deleted. |
4369 | * |
4370 | * We need to do it for the dev_close(), since that will call |
4371 | * the netdev notifiers, and we need to acquire the mutex there |
4372 | * but don't know if we get there from here or from some other |
4373 | * place (e.g. "ip link set ... down"). |
4374 | */ |
4375 | mutex_unlock(lock: &rdev->wiphy.mtx); |
4376 | |
4377 | /* |
4378 | * If we remove a wireless device without a netdev then clear |
4379 | * user_ptr[1] so that nl80211_post_doit won't dereference it |
4380 | * to check if it needs to do dev_put(). Otherwise it crashes |
4381 | * since the wdev has been freed, unlike with a netdev where |
4382 | * we need the dev_put() for the netdev to really be freed. |
4383 | */ |
4384 | if (!wdev->netdev) |
4385 | info->user_ptr[1] = NULL; |
4386 | else |
4387 | dev_close(dev: wdev->netdev); |
4388 | |
4389 | mutex_lock(&rdev->wiphy.mtx); |
4390 | |
4391 | return cfg80211_remove_virtual_intf(rdev, wdev); |
4392 | } |
4393 | |
4394 | static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info) |
4395 | { |
4396 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
4397 | struct net_device *dev = info->user_ptr[1]; |
4398 | u16 noack_map; |
4399 | |
4400 | if (!info->attrs[NL80211_ATTR_NOACK_MAP]) |
4401 | return -EINVAL; |
4402 | |
4403 | if (!rdev->ops->set_noack_map) |
4404 | return -EOPNOTSUPP; |
4405 | |
4406 | noack_map = nla_get_u16(nla: info->attrs[NL80211_ATTR_NOACK_MAP]); |
4407 | |
4408 | return rdev_set_noack_map(rdev, dev, noack_map); |
4409 | } |
4410 | |
4411 | static int nl80211_validate_key_link_id(struct genl_info *info, |
4412 | struct wireless_dev *wdev, |
4413 | int link_id, bool pairwise) |
4414 | { |
4415 | if (pairwise) { |
4416 | if (link_id != -1) { |
4417 | GENL_SET_ERR_MSG(info, |
4418 | "link ID not allowed for pairwise key" ); |
4419 | return -EINVAL; |
4420 | } |
4421 | |
4422 | return 0; |
4423 | } |
4424 | |
4425 | if (wdev->valid_links) { |
4426 | if (link_id == -1) { |
4427 | GENL_SET_ERR_MSG(info, |
4428 | "link ID must for MLO group key" ); |
4429 | return -EINVAL; |
4430 | } |
4431 | if (!(wdev->valid_links & BIT(link_id))) { |
4432 | GENL_SET_ERR_MSG(info, "invalid link ID for MLO group key" ); |
4433 | return -EINVAL; |
4434 | } |
4435 | } else if (link_id != -1) { |
4436 | GENL_SET_ERR_MSG(info, "link ID not allowed for non-MLO group key" ); |
4437 | return -EINVAL; |
4438 | } |
4439 | |
4440 | return 0; |
4441 | } |
4442 | |
4443 | struct get_key_cookie { |
4444 | struct sk_buff *msg; |
4445 | int error; |
4446 | int idx; |
4447 | }; |
4448 | |
4449 | static void get_key_callback(void *c, struct key_params *params) |
4450 | { |
4451 | struct nlattr *key; |
4452 | struct get_key_cookie *cookie = c; |
4453 | |
4454 | if ((params->key && |
4455 | nla_put(skb: cookie->msg, attrtype: NL80211_ATTR_KEY_DATA, |
4456 | attrlen: params->key_len, data: params->key)) || |
4457 | (params->seq && |
4458 | nla_put(skb: cookie->msg, attrtype: NL80211_ATTR_KEY_SEQ, |
4459 | attrlen: params->seq_len, data: params->seq)) || |
4460 | (params->cipher && |
4461 | nla_put_u32(skb: cookie->msg, attrtype: NL80211_ATTR_KEY_CIPHER, |
4462 | value: params->cipher))) |
4463 | goto nla_put_failure; |
4464 | |
4465 | key = nla_nest_start_noflag(skb: cookie->msg, NL80211_ATTR_KEY); |
4466 | if (!key) |
4467 | goto nla_put_failure; |
4468 | |
4469 | if ((params->key && |
4470 | nla_put(skb: cookie->msg, attrtype: NL80211_KEY_DATA, |
4471 | attrlen: params->key_len, data: params->key)) || |
4472 | (params->seq && |
4473 | nla_put(skb: cookie->msg, attrtype: NL80211_KEY_SEQ, |
4474 | attrlen: params->seq_len, data: params->seq)) || |
4475 | (params->cipher && |
4476 | nla_put_u32(skb: cookie->msg, attrtype: NL80211_KEY_CIPHER, |
4477 | value: params->cipher))) |
4478 | goto nla_put_failure; |
4479 | |
4480 | if (nla_put_u8(skb: cookie->msg, attrtype: NL80211_KEY_IDX, value: cookie->idx)) |
4481 | goto nla_put_failure; |
4482 | |
4483 | nla_nest_end(skb: cookie->msg, start: key); |
4484 | |
4485 | return; |
4486 | nla_put_failure: |
4487 | cookie->error = 1; |
4488 | } |
4489 | |
4490 | static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) |
4491 | { |
4492 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
4493 | int err; |
4494 | struct net_device *dev = info->user_ptr[1]; |
4495 | u8 key_idx = 0; |
4496 | const u8 *mac_addr = NULL; |
4497 | bool pairwise; |
4498 | struct get_key_cookie cookie = { |
4499 | .error = 0, |
4500 | }; |
4501 | void *hdr; |
4502 | struct sk_buff *msg; |
4503 | bool bigtk_support = false; |
4504 | int link_id = nl80211_link_id_or_invalid(attrs: info->attrs); |
4505 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
4506 | |
4507 | if (wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
4508 | ftidx: NL80211_EXT_FEATURE_BEACON_PROTECTION)) |
4509 | bigtk_support = true; |
4510 | |
4511 | if ((wdev->iftype == NL80211_IFTYPE_STATION || |
4512 | wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) && |
4513 | wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
4514 | ftidx: NL80211_EXT_FEATURE_BEACON_PROTECTION_CLIENT)) |
4515 | bigtk_support = true; |
4516 | |
4517 | if (info->attrs[NL80211_ATTR_KEY_IDX]) { |
4518 | key_idx = nla_get_u8(nla: info->attrs[NL80211_ATTR_KEY_IDX]); |
4519 | |
4520 | if (key_idx >= 6 && key_idx <= 7 && !bigtk_support) { |
4521 | GENL_SET_ERR_MSG(info, "BIGTK not supported" ); |
4522 | return -EINVAL; |
4523 | } |
4524 | } |
4525 | |
4526 | if (info->attrs[NL80211_ATTR_MAC]) |
4527 | mac_addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
4528 | |
4529 | pairwise = !!mac_addr; |
4530 | if (info->attrs[NL80211_ATTR_KEY_TYPE]) { |
4531 | u32 kt = nla_get_u32(nla: info->attrs[NL80211_ATTR_KEY_TYPE]); |
4532 | |
4533 | if (kt != NL80211_KEYTYPE_GROUP && |
4534 | kt != NL80211_KEYTYPE_PAIRWISE) |
4535 | return -EINVAL; |
4536 | pairwise = kt == NL80211_KEYTYPE_PAIRWISE; |
4537 | } |
4538 | |
4539 | if (!rdev->ops->get_key) |
4540 | return -EOPNOTSUPP; |
4541 | |
4542 | if (!pairwise && mac_addr && !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) |
4543 | return -ENOENT; |
4544 | |
4545 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
4546 | if (!msg) |
4547 | return -ENOMEM; |
4548 | |
4549 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
4550 | cmd: NL80211_CMD_NEW_KEY); |
4551 | if (!hdr) |
4552 | goto nla_put_failure; |
4553 | |
4554 | cookie.msg = msg; |
4555 | cookie.idx = key_idx; |
4556 | |
4557 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || |
4558 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_KEY_IDX, value: key_idx)) |
4559 | goto nla_put_failure; |
4560 | if (mac_addr && |
4561 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: mac_addr)) |
4562 | goto nla_put_failure; |
4563 | |
4564 | err = nl80211_validate_key_link_id(info, wdev, link_id, pairwise); |
4565 | if (err) |
4566 | goto free_msg; |
4567 | |
4568 | err = rdev_get_key(rdev, netdev: dev, link_id, key_index: key_idx, pairwise, mac_addr, |
4569 | cookie: &cookie, callback: get_key_callback); |
4570 | |
4571 | if (err) |
4572 | goto free_msg; |
4573 | |
4574 | if (cookie.error) |
4575 | goto nla_put_failure; |
4576 | |
4577 | genlmsg_end(skb: msg, hdr); |
4578 | return genlmsg_reply(skb: msg, info); |
4579 | |
4580 | nla_put_failure: |
4581 | err = -ENOBUFS; |
4582 | free_msg: |
4583 | nlmsg_free(skb: msg); |
4584 | return err; |
4585 | } |
4586 | |
4587 | static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) |
4588 | { |
4589 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
4590 | struct key_parse key; |
4591 | int err; |
4592 | struct net_device *dev = info->user_ptr[1]; |
4593 | int link_id = nl80211_link_id_or_invalid(attrs: info->attrs); |
4594 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
4595 | |
4596 | err = nl80211_parse_key(info, k: &key); |
4597 | if (err) |
4598 | return err; |
4599 | |
4600 | if (key.idx < 0) |
4601 | return -EINVAL; |
4602 | |
4603 | /* Only support setting default key and |
4604 | * Extended Key ID action NL80211_KEY_SET_TX. |
4605 | */ |
4606 | if (!key.def && !key.defmgmt && !key.defbeacon && |
4607 | !(key.p.mode == NL80211_KEY_SET_TX)) |
4608 | return -EINVAL; |
4609 | |
4610 | if (key.def) { |
4611 | if (!rdev->ops->set_default_key) |
4612 | return -EOPNOTSUPP; |
4613 | |
4614 | err = nl80211_key_allowed(wdev); |
4615 | if (err) |
4616 | return err; |
4617 | |
4618 | err = nl80211_validate_key_link_id(info, wdev, link_id, pairwise: false); |
4619 | if (err) |
4620 | return err; |
4621 | |
4622 | err = rdev_set_default_key(rdev, netdev: dev, link_id, key_index: key.idx, |
4623 | unicast: key.def_uni, multicast: key.def_multi); |
4624 | |
4625 | if (err) |
4626 | return err; |
4627 | |
4628 | #ifdef CONFIG_CFG80211_WEXT |
4629 | wdev->wext.default_key = key.idx; |
4630 | #endif |
4631 | return 0; |
4632 | } else if (key.defmgmt) { |
4633 | if (key.def_uni || !key.def_multi) |
4634 | return -EINVAL; |
4635 | |
4636 | if (!rdev->ops->set_default_mgmt_key) |
4637 | return -EOPNOTSUPP; |
4638 | |
4639 | err = nl80211_key_allowed(wdev); |
4640 | if (err) |
4641 | return err; |
4642 | |
4643 | err = nl80211_validate_key_link_id(info, wdev, link_id, pairwise: false); |
4644 | if (err) |
4645 | return err; |
4646 | |
4647 | err = rdev_set_default_mgmt_key(rdev, netdev: dev, link_id, key_index: key.idx); |
4648 | if (err) |
4649 | return err; |
4650 | |
4651 | #ifdef CONFIG_CFG80211_WEXT |
4652 | wdev->wext.default_mgmt_key = key.idx; |
4653 | #endif |
4654 | return 0; |
4655 | } else if (key.defbeacon) { |
4656 | if (key.def_uni || !key.def_multi) |
4657 | return -EINVAL; |
4658 | |
4659 | if (!rdev->ops->set_default_beacon_key) |
4660 | return -EOPNOTSUPP; |
4661 | |
4662 | err = nl80211_key_allowed(wdev); |
4663 | if (err) |
4664 | return err; |
4665 | |
4666 | err = nl80211_validate_key_link_id(info, wdev, link_id, pairwise: false); |
4667 | if (err) |
4668 | return err; |
4669 | |
4670 | return rdev_set_default_beacon_key(rdev, netdev: dev, link_id, key_index: key.idx); |
4671 | } else if (key.p.mode == NL80211_KEY_SET_TX && |
4672 | wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
4673 | ftidx: NL80211_EXT_FEATURE_EXT_KEY_ID)) { |
4674 | u8 *mac_addr = NULL; |
4675 | |
4676 | if (info->attrs[NL80211_ATTR_MAC]) |
4677 | mac_addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
4678 | |
4679 | if (!mac_addr || key.idx < 0 || key.idx > 1) |
4680 | return -EINVAL; |
4681 | |
4682 | err = nl80211_validate_key_link_id(info, wdev, link_id, pairwise: true); |
4683 | if (err) |
4684 | return err; |
4685 | |
4686 | return rdev_add_key(rdev, netdev: dev, link_id, key_index: key.idx, |
4687 | pairwise: NL80211_KEYTYPE_PAIRWISE, |
4688 | mac_addr, params: &key.p); |
4689 | } |
4690 | |
4691 | return -EINVAL; |
4692 | } |
4693 | |
4694 | static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) |
4695 | { |
4696 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
4697 | int err; |
4698 | struct net_device *dev = info->user_ptr[1]; |
4699 | struct key_parse key; |
4700 | const u8 *mac_addr = NULL; |
4701 | int link_id = nl80211_link_id_or_invalid(attrs: info->attrs); |
4702 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
4703 | |
4704 | err = nl80211_parse_key(info, k: &key); |
4705 | if (err) |
4706 | return err; |
4707 | |
4708 | if (!key.p.key) { |
4709 | GENL_SET_ERR_MSG(info, "no key" ); |
4710 | return -EINVAL; |
4711 | } |
4712 | |
4713 | if (info->attrs[NL80211_ATTR_MAC]) |
4714 | mac_addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
4715 | |
4716 | if (key.type == -1) { |
4717 | if (mac_addr) |
4718 | key.type = NL80211_KEYTYPE_PAIRWISE; |
4719 | else |
4720 | key.type = NL80211_KEYTYPE_GROUP; |
4721 | } |
4722 | |
4723 | /* for now */ |
4724 | if (key.type != NL80211_KEYTYPE_PAIRWISE && |
4725 | key.type != NL80211_KEYTYPE_GROUP) { |
4726 | GENL_SET_ERR_MSG(info, "key type not pairwise or group" ); |
4727 | return -EINVAL; |
4728 | } |
4729 | |
4730 | if (key.type == NL80211_KEYTYPE_GROUP && |
4731 | info->attrs[NL80211_ATTR_VLAN_ID]) |
4732 | key.p.vlan_id = nla_get_u16(nla: info->attrs[NL80211_ATTR_VLAN_ID]); |
4733 | |
4734 | if (!rdev->ops->add_key) |
4735 | return -EOPNOTSUPP; |
4736 | |
4737 | if (cfg80211_validate_key_settings(rdev, params: &key.p, key_idx: key.idx, |
4738 | pairwise: key.type == NL80211_KEYTYPE_PAIRWISE, |
4739 | mac_addr)) { |
4740 | GENL_SET_ERR_MSG(info, "key setting validation failed" ); |
4741 | return -EINVAL; |
4742 | } |
4743 | |
4744 | err = nl80211_key_allowed(wdev); |
4745 | if (err) |
4746 | GENL_SET_ERR_MSG(info, "key not allowed" ); |
4747 | |
4748 | if (!err) |
4749 | err = nl80211_validate_key_link_id(info, wdev, link_id, |
4750 | pairwise: key.type == NL80211_KEYTYPE_PAIRWISE); |
4751 | |
4752 | if (!err) { |
4753 | err = rdev_add_key(rdev, netdev: dev, link_id, key_index: key.idx, |
4754 | pairwise: key.type == NL80211_KEYTYPE_PAIRWISE, |
4755 | mac_addr, params: &key.p); |
4756 | if (err) |
4757 | GENL_SET_ERR_MSG(info, "key addition failed" ); |
4758 | } |
4759 | |
4760 | return err; |
4761 | } |
4762 | |
4763 | static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) |
4764 | { |
4765 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
4766 | int err; |
4767 | struct net_device *dev = info->user_ptr[1]; |
4768 | u8 *mac_addr = NULL; |
4769 | struct key_parse key; |
4770 | int link_id = nl80211_link_id_or_invalid(attrs: info->attrs); |
4771 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
4772 | |
4773 | err = nl80211_parse_key(info, k: &key); |
4774 | if (err) |
4775 | return err; |
4776 | |
4777 | if (info->attrs[NL80211_ATTR_MAC]) |
4778 | mac_addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
4779 | |
4780 | if (key.type == -1) { |
4781 | if (mac_addr) |
4782 | key.type = NL80211_KEYTYPE_PAIRWISE; |
4783 | else |
4784 | key.type = NL80211_KEYTYPE_GROUP; |
4785 | } |
4786 | |
4787 | /* for now */ |
4788 | if (key.type != NL80211_KEYTYPE_PAIRWISE && |
4789 | key.type != NL80211_KEYTYPE_GROUP) |
4790 | return -EINVAL; |
4791 | |
4792 | if (!cfg80211_valid_key_idx(rdev, key_idx: key.idx, |
4793 | pairwise: key.type == NL80211_KEYTYPE_PAIRWISE)) |
4794 | return -EINVAL; |
4795 | |
4796 | if (!rdev->ops->del_key) |
4797 | return -EOPNOTSUPP; |
4798 | |
4799 | err = nl80211_key_allowed(wdev); |
4800 | |
4801 | if (key.type == NL80211_KEYTYPE_GROUP && mac_addr && |
4802 | !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)) |
4803 | err = -ENOENT; |
4804 | |
4805 | if (!err) |
4806 | err = nl80211_validate_key_link_id(info, wdev, link_id, |
4807 | pairwise: key.type == NL80211_KEYTYPE_PAIRWISE); |
4808 | |
4809 | if (!err) |
4810 | err = rdev_del_key(rdev, netdev: dev, link_id, key_index: key.idx, |
4811 | pairwise: key.type == NL80211_KEYTYPE_PAIRWISE, |
4812 | mac_addr); |
4813 | |
4814 | #ifdef CONFIG_CFG80211_WEXT |
4815 | if (!err) { |
4816 | if (key.idx == wdev->wext.default_key) |
4817 | wdev->wext.default_key = -1; |
4818 | else if (key.idx == wdev->wext.default_mgmt_key) |
4819 | wdev->wext.default_mgmt_key = -1; |
4820 | } |
4821 | #endif |
4822 | |
4823 | return err; |
4824 | } |
4825 | |
4826 | /* This function returns an error or the number of nested attributes */ |
4827 | static int validate_acl_mac_addrs(struct nlattr *nl_attr) |
4828 | { |
4829 | struct nlattr *attr; |
4830 | int n_entries = 0, tmp; |
4831 | |
4832 | nla_for_each_nested(attr, nl_attr, tmp) { |
4833 | if (nla_len(nla: attr) != ETH_ALEN) |
4834 | return -EINVAL; |
4835 | |
4836 | n_entries++; |
4837 | } |
4838 | |
4839 | return n_entries; |
4840 | } |
4841 | |
4842 | /* |
4843 | * This function parses ACL information and allocates memory for ACL data. |
4844 | * On successful return, the calling function is responsible to free the |
4845 | * ACL buffer returned by this function. |
4846 | */ |
4847 | static struct cfg80211_acl_data *parse_acl_data(struct wiphy *wiphy, |
4848 | struct genl_info *info) |
4849 | { |
4850 | enum nl80211_acl_policy acl_policy; |
4851 | struct nlattr *attr; |
4852 | struct cfg80211_acl_data *acl; |
4853 | int i = 0, n_entries, tmp; |
4854 | |
4855 | if (!wiphy->max_acl_mac_addrs) |
4856 | return ERR_PTR(error: -EOPNOTSUPP); |
4857 | |
4858 | if (!info->attrs[NL80211_ATTR_ACL_POLICY]) |
4859 | return ERR_PTR(error: -EINVAL); |
4860 | |
4861 | acl_policy = nla_get_u32(nla: info->attrs[NL80211_ATTR_ACL_POLICY]); |
4862 | if (acl_policy != NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED && |
4863 | acl_policy != NL80211_ACL_POLICY_DENY_UNLESS_LISTED) |
4864 | return ERR_PTR(error: -EINVAL); |
4865 | |
4866 | if (!info->attrs[NL80211_ATTR_MAC_ADDRS]) |
4867 | return ERR_PTR(error: -EINVAL); |
4868 | |
4869 | n_entries = validate_acl_mac_addrs(nl_attr: info->attrs[NL80211_ATTR_MAC_ADDRS]); |
4870 | if (n_entries < 0) |
4871 | return ERR_PTR(error: n_entries); |
4872 | |
4873 | if (n_entries > wiphy->max_acl_mac_addrs) |
4874 | return ERR_PTR(error: -EOPNOTSUPP); |
4875 | |
4876 | acl = kzalloc(struct_size(acl, mac_addrs, n_entries), GFP_KERNEL); |
4877 | if (!acl) |
4878 | return ERR_PTR(error: -ENOMEM); |
4879 | acl->n_acl_entries = n_entries; |
4880 | |
4881 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_MAC_ADDRS], tmp) { |
4882 | memcpy(acl->mac_addrs[i].addr, nla_data(attr), ETH_ALEN); |
4883 | i++; |
4884 | } |
4885 | acl->acl_policy = acl_policy; |
4886 | |
4887 | return acl; |
4888 | } |
4889 | |
4890 | static int nl80211_set_mac_acl(struct sk_buff *skb, struct genl_info *info) |
4891 | { |
4892 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
4893 | struct net_device *dev = info->user_ptr[1]; |
4894 | struct cfg80211_acl_data *acl; |
4895 | int err; |
4896 | |
4897 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && |
4898 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) |
4899 | return -EOPNOTSUPP; |
4900 | |
4901 | if (!dev->ieee80211_ptr->links[0].ap.beacon_interval) |
4902 | return -EINVAL; |
4903 | |
4904 | acl = parse_acl_data(wiphy: &rdev->wiphy, info); |
4905 | if (IS_ERR(ptr: acl)) |
4906 | return PTR_ERR(ptr: acl); |
4907 | |
4908 | err = rdev_set_mac_acl(rdev, dev, params: acl); |
4909 | |
4910 | kfree(objp: acl); |
4911 | |
4912 | return err; |
4913 | } |
4914 | |
4915 | static u32 rateset_to_mask(struct ieee80211_supported_band *sband, |
4916 | u8 *rates, u8 rates_len) |
4917 | { |
4918 | u8 i; |
4919 | u32 mask = 0; |
4920 | |
4921 | for (i = 0; i < rates_len; i++) { |
4922 | int rate = (rates[i] & 0x7f) * 5; |
4923 | int ridx; |
4924 | |
4925 | for (ridx = 0; ridx < sband->n_bitrates; ridx++) { |
4926 | struct ieee80211_rate *srate = |
4927 | &sband->bitrates[ridx]; |
4928 | if (rate == srate->bitrate) { |
4929 | mask |= 1 << ridx; |
4930 | break; |
4931 | } |
4932 | } |
4933 | if (ridx == sband->n_bitrates) |
4934 | return 0; /* rate not found */ |
4935 | } |
4936 | |
4937 | return mask; |
4938 | } |
4939 | |
4940 | static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband, |
4941 | u8 *rates, u8 rates_len, |
4942 | u8 mcs[IEEE80211_HT_MCS_MASK_LEN]) |
4943 | { |
4944 | u8 i; |
4945 | |
4946 | memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN); |
4947 | |
4948 | for (i = 0; i < rates_len; i++) { |
4949 | int ridx, rbit; |
4950 | |
4951 | ridx = rates[i] / 8; |
4952 | rbit = BIT(rates[i] % 8); |
4953 | |
4954 | /* check validity */ |
4955 | if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN)) |
4956 | return false; |
4957 | |
4958 | /* check availability */ |
4959 | ridx = array_index_nospec(ridx, IEEE80211_HT_MCS_MASK_LEN); |
4960 | if (sband->ht_cap.mcs.rx_mask[ridx] & rbit) |
4961 | mcs[ridx] |= rbit; |
4962 | else |
4963 | return false; |
4964 | } |
4965 | |
4966 | return true; |
4967 | } |
4968 | |
4969 | static u16 vht_mcs_map_to_mcs_mask(u8 vht_mcs_map) |
4970 | { |
4971 | u16 mcs_mask = 0; |
4972 | |
4973 | switch (vht_mcs_map) { |
4974 | case IEEE80211_VHT_MCS_NOT_SUPPORTED: |
4975 | break; |
4976 | case IEEE80211_VHT_MCS_SUPPORT_0_7: |
4977 | mcs_mask = 0x00FF; |
4978 | break; |
4979 | case IEEE80211_VHT_MCS_SUPPORT_0_8: |
4980 | mcs_mask = 0x01FF; |
4981 | break; |
4982 | case IEEE80211_VHT_MCS_SUPPORT_0_9: |
4983 | mcs_mask = 0x03FF; |
4984 | break; |
4985 | default: |
4986 | break; |
4987 | } |
4988 | |
4989 | return mcs_mask; |
4990 | } |
4991 | |
4992 | static void vht_build_mcs_mask(u16 vht_mcs_map, |
4993 | u16 vht_mcs_mask[NL80211_VHT_NSS_MAX]) |
4994 | { |
4995 | u8 nss; |
4996 | |
4997 | for (nss = 0; nss < NL80211_VHT_NSS_MAX; nss++) { |
4998 | vht_mcs_mask[nss] = vht_mcs_map_to_mcs_mask(vht_mcs_map: vht_mcs_map & 0x03); |
4999 | vht_mcs_map >>= 2; |
5000 | } |
5001 | } |
5002 | |
5003 | static bool vht_set_mcs_mask(struct ieee80211_supported_band *sband, |
5004 | struct nl80211_txrate_vht *txrate, |
5005 | u16 mcs[NL80211_VHT_NSS_MAX]) |
5006 | { |
5007 | u16 tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); |
5008 | u16 tx_mcs_mask[NL80211_VHT_NSS_MAX] = {}; |
5009 | u8 i; |
5010 | |
5011 | if (!sband->vht_cap.vht_supported) |
5012 | return false; |
5013 | |
5014 | memset(mcs, 0, sizeof(u16) * NL80211_VHT_NSS_MAX); |
5015 | |
5016 | /* Build vht_mcs_mask from VHT capabilities */ |
5017 | vht_build_mcs_mask(vht_mcs_map: tx_mcs_map, vht_mcs_mask: tx_mcs_mask); |
5018 | |
5019 | for (i = 0; i < NL80211_VHT_NSS_MAX; i++) { |
5020 | if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i]) |
5021 | mcs[i] = txrate->mcs[i]; |
5022 | else |
5023 | return false; |
5024 | } |
5025 | |
5026 | return true; |
5027 | } |
5028 | |
5029 | static u16 he_mcs_map_to_mcs_mask(u8 he_mcs_map) |
5030 | { |
5031 | switch (he_mcs_map) { |
5032 | case IEEE80211_HE_MCS_NOT_SUPPORTED: |
5033 | return 0; |
5034 | case IEEE80211_HE_MCS_SUPPORT_0_7: |
5035 | return 0x00FF; |
5036 | case IEEE80211_HE_MCS_SUPPORT_0_9: |
5037 | return 0x03FF; |
5038 | case IEEE80211_HE_MCS_SUPPORT_0_11: |
5039 | return 0xFFF; |
5040 | default: |
5041 | break; |
5042 | } |
5043 | return 0; |
5044 | } |
5045 | |
5046 | static void he_build_mcs_mask(u16 he_mcs_map, |
5047 | u16 he_mcs_mask[NL80211_HE_NSS_MAX]) |
5048 | { |
5049 | u8 nss; |
5050 | |
5051 | for (nss = 0; nss < NL80211_HE_NSS_MAX; nss++) { |
5052 | he_mcs_mask[nss] = he_mcs_map_to_mcs_mask(he_mcs_map: he_mcs_map & 0x03); |
5053 | he_mcs_map >>= 2; |
5054 | } |
5055 | } |
5056 | |
5057 | static u16 he_get_txmcsmap(struct genl_info *info, unsigned int link_id, |
5058 | const struct ieee80211_sta_he_cap *he_cap) |
5059 | { |
5060 | struct net_device *dev = info->user_ptr[1]; |
5061 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
5062 | struct cfg80211_chan_def *chandef; |
5063 | __le16 tx_mcs; |
5064 | |
5065 | chandef = wdev_chandef(wdev, link_id); |
5066 | if (!chandef) { |
5067 | /* |
5068 | * This is probably broken, but we never maintained |
5069 | * a chandef in these cases, so it always was. |
5070 | */ |
5071 | return le16_to_cpu(he_cap->he_mcs_nss_supp.tx_mcs_80); |
5072 | } |
5073 | |
5074 | switch (chandef->width) { |
5075 | case NL80211_CHAN_WIDTH_80P80: |
5076 | tx_mcs = he_cap->he_mcs_nss_supp.tx_mcs_80p80; |
5077 | break; |
5078 | case NL80211_CHAN_WIDTH_160: |
5079 | tx_mcs = he_cap->he_mcs_nss_supp.tx_mcs_160; |
5080 | break; |
5081 | default: |
5082 | tx_mcs = he_cap->he_mcs_nss_supp.tx_mcs_80; |
5083 | break; |
5084 | } |
5085 | |
5086 | return le16_to_cpu(tx_mcs); |
5087 | } |
5088 | |
5089 | static bool he_set_mcs_mask(struct genl_info *info, |
5090 | struct wireless_dev *wdev, |
5091 | struct ieee80211_supported_band *sband, |
5092 | struct nl80211_txrate_he *txrate, |
5093 | u16 mcs[NL80211_HE_NSS_MAX], |
5094 | unsigned int link_id) |
5095 | { |
5096 | const struct ieee80211_sta_he_cap *he_cap; |
5097 | u16 tx_mcs_mask[NL80211_HE_NSS_MAX] = {}; |
5098 | u16 tx_mcs_map = 0; |
5099 | u8 i; |
5100 | |
5101 | he_cap = ieee80211_get_he_iftype_cap(sband, iftype: wdev->iftype); |
5102 | if (!he_cap) |
5103 | return false; |
5104 | |
5105 | memset(mcs, 0, sizeof(u16) * NL80211_HE_NSS_MAX); |
5106 | |
5107 | tx_mcs_map = he_get_txmcsmap(info, link_id, he_cap); |
5108 | |
5109 | /* Build he_mcs_mask from HE capabilities */ |
5110 | he_build_mcs_mask(he_mcs_map: tx_mcs_map, he_mcs_mask: tx_mcs_mask); |
5111 | |
5112 | for (i = 0; i < NL80211_HE_NSS_MAX; i++) { |
5113 | if ((tx_mcs_mask[i] & txrate->mcs[i]) == txrate->mcs[i]) |
5114 | mcs[i] = txrate->mcs[i]; |
5115 | else |
5116 | return false; |
5117 | } |
5118 | |
5119 | return true; |
5120 | } |
5121 | |
5122 | static int nl80211_parse_tx_bitrate_mask(struct genl_info *info, |
5123 | struct nlattr *attrs[], |
5124 | enum nl80211_attrs attr, |
5125 | struct cfg80211_bitrate_mask *mask, |
5126 | struct net_device *dev, |
5127 | bool default_all_enabled, |
5128 | unsigned int link_id) |
5129 | { |
5130 | struct nlattr *tb[NL80211_TXRATE_MAX + 1]; |
5131 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
5132 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
5133 | int rem, i; |
5134 | struct nlattr *tx_rates; |
5135 | struct ieee80211_supported_band *sband; |
5136 | u16 vht_tx_mcs_map, he_tx_mcs_map; |
5137 | |
5138 | memset(mask, 0, sizeof(*mask)); |
5139 | /* Default to all rates enabled */ |
5140 | for (i = 0; i < NUM_NL80211_BANDS; i++) { |
5141 | const struct ieee80211_sta_he_cap *he_cap; |
5142 | |
5143 | if (!default_all_enabled) |
5144 | break; |
5145 | |
5146 | sband = rdev->wiphy.bands[i]; |
5147 | |
5148 | if (!sband) |
5149 | continue; |
5150 | |
5151 | mask->control[i].legacy = (1 << sband->n_bitrates) - 1; |
5152 | memcpy(mask->control[i].ht_mcs, |
5153 | sband->ht_cap.mcs.rx_mask, |
5154 | sizeof(mask->control[i].ht_mcs)); |
5155 | |
5156 | if (sband->vht_cap.vht_supported) { |
5157 | vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map); |
5158 | vht_build_mcs_mask(vht_mcs_map: vht_tx_mcs_map, vht_mcs_mask: mask->control[i].vht_mcs); |
5159 | } |
5160 | |
5161 | he_cap = ieee80211_get_he_iftype_cap(sband, iftype: wdev->iftype); |
5162 | if (!he_cap) |
5163 | continue; |
5164 | |
5165 | he_tx_mcs_map = he_get_txmcsmap(info, link_id, he_cap); |
5166 | he_build_mcs_mask(he_mcs_map: he_tx_mcs_map, he_mcs_mask: mask->control[i].he_mcs); |
5167 | |
5168 | mask->control[i].he_gi = 0xFF; |
5169 | mask->control[i].he_ltf = 0xFF; |
5170 | } |
5171 | |
5172 | /* if no rates are given set it back to the defaults */ |
5173 | if (!attrs[attr]) |
5174 | goto out; |
5175 | |
5176 | /* The nested attribute uses enum nl80211_band as the index. This maps |
5177 | * directly to the enum nl80211_band values used in cfg80211. |
5178 | */ |
5179 | BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8); |
5180 | nla_for_each_nested(tx_rates, attrs[attr], rem) { |
5181 | enum nl80211_band band = nla_type(nla: tx_rates); |
5182 | int err; |
5183 | |
5184 | if (band < 0 || band >= NUM_NL80211_BANDS) |
5185 | return -EINVAL; |
5186 | sband = rdev->wiphy.bands[band]; |
5187 | if (sband == NULL) |
5188 | return -EINVAL; |
5189 | err = nla_parse_nested_deprecated(tb, maxtype: NL80211_TXRATE_MAX, |
5190 | nla: tx_rates, |
5191 | policy: nl80211_txattr_policy, |
5192 | extack: info->extack); |
5193 | if (err) |
5194 | return err; |
5195 | if (tb[NL80211_TXRATE_LEGACY]) { |
5196 | mask->control[band].legacy = rateset_to_mask( |
5197 | sband, |
5198 | rates: nla_data(nla: tb[NL80211_TXRATE_LEGACY]), |
5199 | rates_len: nla_len(nla: tb[NL80211_TXRATE_LEGACY])); |
5200 | if ((mask->control[band].legacy == 0) && |
5201 | nla_len(nla: tb[NL80211_TXRATE_LEGACY])) |
5202 | return -EINVAL; |
5203 | } |
5204 | if (tb[NL80211_TXRATE_HT]) { |
5205 | if (!ht_rateset_to_mask( |
5206 | sband, |
5207 | rates: nla_data(nla: tb[NL80211_TXRATE_HT]), |
5208 | rates_len: nla_len(nla: tb[NL80211_TXRATE_HT]), |
5209 | mcs: mask->control[band].ht_mcs)) |
5210 | return -EINVAL; |
5211 | } |
5212 | |
5213 | if (tb[NL80211_TXRATE_VHT]) { |
5214 | if (!vht_set_mcs_mask( |
5215 | sband, |
5216 | txrate: nla_data(nla: tb[NL80211_TXRATE_VHT]), |
5217 | mcs: mask->control[band].vht_mcs)) |
5218 | return -EINVAL; |
5219 | } |
5220 | |
5221 | if (tb[NL80211_TXRATE_GI]) { |
5222 | mask->control[band].gi = |
5223 | nla_get_u8(nla: tb[NL80211_TXRATE_GI]); |
5224 | if (mask->control[band].gi > NL80211_TXRATE_FORCE_LGI) |
5225 | return -EINVAL; |
5226 | } |
5227 | if (tb[NL80211_TXRATE_HE] && |
5228 | !he_set_mcs_mask(info, wdev, sband, |
5229 | txrate: nla_data(nla: tb[NL80211_TXRATE_HE]), |
5230 | mcs: mask->control[band].he_mcs, |
5231 | link_id)) |
5232 | return -EINVAL; |
5233 | |
5234 | if (tb[NL80211_TXRATE_HE_GI]) |
5235 | mask->control[band].he_gi = |
5236 | nla_get_u8(nla: tb[NL80211_TXRATE_HE_GI]); |
5237 | if (tb[NL80211_TXRATE_HE_LTF]) |
5238 | mask->control[band].he_ltf = |
5239 | nla_get_u8(nla: tb[NL80211_TXRATE_HE_LTF]); |
5240 | |
5241 | if (mask->control[band].legacy == 0) { |
5242 | /* don't allow empty legacy rates if HT, VHT or HE |
5243 | * are not even supported. |
5244 | */ |
5245 | if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported || |
5246 | rdev->wiphy.bands[band]->vht_cap.vht_supported || |
5247 | ieee80211_get_he_iftype_cap(sband, iftype: wdev->iftype))) |
5248 | return -EINVAL; |
5249 | |
5250 | for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) |
5251 | if (mask->control[band].ht_mcs[i]) |
5252 | goto out; |
5253 | |
5254 | for (i = 0; i < NL80211_VHT_NSS_MAX; i++) |
5255 | if (mask->control[band].vht_mcs[i]) |
5256 | goto out; |
5257 | |
5258 | for (i = 0; i < NL80211_HE_NSS_MAX; i++) |
5259 | if (mask->control[band].he_mcs[i]) |
5260 | goto out; |
5261 | |
5262 | /* legacy and mcs rates may not be both empty */ |
5263 | return -EINVAL; |
5264 | } |
5265 | } |
5266 | |
5267 | out: |
5268 | return 0; |
5269 | } |
5270 | |
5271 | static int validate_beacon_tx_rate(struct cfg80211_registered_device *rdev, |
5272 | enum nl80211_band band, |
5273 | struct cfg80211_bitrate_mask *beacon_rate) |
5274 | { |
5275 | u32 count_ht, count_vht, count_he, i; |
5276 | u32 rate = beacon_rate->control[band].legacy; |
5277 | |
5278 | /* Allow only one rate */ |
5279 | if (hweight32(rate) > 1) |
5280 | return -EINVAL; |
5281 | |
5282 | count_ht = 0; |
5283 | for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) { |
5284 | if (hweight8(beacon_rate->control[band].ht_mcs[i]) > 1) { |
5285 | return -EINVAL; |
5286 | } else if (beacon_rate->control[band].ht_mcs[i]) { |
5287 | count_ht++; |
5288 | if (count_ht > 1) |
5289 | return -EINVAL; |
5290 | } |
5291 | if (count_ht && rate) |
5292 | return -EINVAL; |
5293 | } |
5294 | |
5295 | count_vht = 0; |
5296 | for (i = 0; i < NL80211_VHT_NSS_MAX; i++) { |
5297 | if (hweight16(beacon_rate->control[band].vht_mcs[i]) > 1) { |
5298 | return -EINVAL; |
5299 | } else if (beacon_rate->control[band].vht_mcs[i]) { |
5300 | count_vht++; |
5301 | if (count_vht > 1) |
5302 | return -EINVAL; |
5303 | } |
5304 | if (count_vht && rate) |
5305 | return -EINVAL; |
5306 | } |
5307 | |
5308 | count_he = 0; |
5309 | for (i = 0; i < NL80211_HE_NSS_MAX; i++) { |
5310 | if (hweight16(beacon_rate->control[band].he_mcs[i]) > 1) { |
5311 | return -EINVAL; |
5312 | } else if (beacon_rate->control[band].he_mcs[i]) { |
5313 | count_he++; |
5314 | if (count_he > 1) |
5315 | return -EINVAL; |
5316 | } |
5317 | if (count_he && rate) |
5318 | return -EINVAL; |
5319 | } |
5320 | |
5321 | if ((count_ht && count_vht && count_he) || |
5322 | (!rate && !count_ht && !count_vht && !count_he)) |
5323 | return -EINVAL; |
5324 | |
5325 | if (rate && |
5326 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
5327 | ftidx: NL80211_EXT_FEATURE_BEACON_RATE_LEGACY)) |
5328 | return -EINVAL; |
5329 | if (count_ht && |
5330 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
5331 | ftidx: NL80211_EXT_FEATURE_BEACON_RATE_HT)) |
5332 | return -EINVAL; |
5333 | if (count_vht && |
5334 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
5335 | ftidx: NL80211_EXT_FEATURE_BEACON_RATE_VHT)) |
5336 | return -EINVAL; |
5337 | if (count_he && |
5338 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
5339 | ftidx: NL80211_EXT_FEATURE_BEACON_RATE_HE)) |
5340 | return -EINVAL; |
5341 | |
5342 | return 0; |
5343 | } |
5344 | |
5345 | static int nl80211_parse_mbssid_config(struct wiphy *wiphy, |
5346 | struct net_device *dev, |
5347 | struct nlattr *attrs, |
5348 | struct cfg80211_mbssid_config *config, |
5349 | u8 num_elems) |
5350 | { |
5351 | struct nlattr *tb[NL80211_MBSSID_CONFIG_ATTR_MAX + 1]; |
5352 | |
5353 | if (!wiphy->mbssid_max_interfaces) |
5354 | return -EOPNOTSUPP; |
5355 | |
5356 | if (nla_parse_nested(tb, maxtype: NL80211_MBSSID_CONFIG_ATTR_MAX, nla: attrs, NULL, |
5357 | NULL) || |
5358 | !tb[NL80211_MBSSID_CONFIG_ATTR_INDEX]) |
5359 | return -EINVAL; |
5360 | |
5361 | config->ema = nla_get_flag(nla: tb[NL80211_MBSSID_CONFIG_ATTR_EMA]); |
5362 | if (config->ema) { |
5363 | if (!wiphy->ema_max_profile_periodicity) |
5364 | return -EOPNOTSUPP; |
5365 | |
5366 | if (num_elems > wiphy->ema_max_profile_periodicity) |
5367 | return -EINVAL; |
5368 | } |
5369 | |
5370 | config->index = nla_get_u8(nla: tb[NL80211_MBSSID_CONFIG_ATTR_INDEX]); |
5371 | if (config->index >= wiphy->mbssid_max_interfaces || |
5372 | (!config->index && !num_elems)) |
5373 | return -EINVAL; |
5374 | |
5375 | if (tb[NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX]) { |
5376 | u32 tx_ifindex = |
5377 | nla_get_u32(nla: tb[NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX]); |
5378 | |
5379 | if ((!config->index && tx_ifindex != dev->ifindex) || |
5380 | (config->index && tx_ifindex == dev->ifindex)) |
5381 | return -EINVAL; |
5382 | |
5383 | if (tx_ifindex != dev->ifindex) { |
5384 | struct net_device *tx_netdev = |
5385 | dev_get_by_index(net: wiphy_net(wiphy), ifindex: tx_ifindex); |
5386 | |
5387 | if (!tx_netdev || !tx_netdev->ieee80211_ptr || |
5388 | tx_netdev->ieee80211_ptr->wiphy != wiphy || |
5389 | tx_netdev->ieee80211_ptr->iftype != |
5390 | NL80211_IFTYPE_AP) { |
5391 | dev_put(dev: tx_netdev); |
5392 | return -EINVAL; |
5393 | } |
5394 | |
5395 | config->tx_wdev = tx_netdev->ieee80211_ptr; |
5396 | } else { |
5397 | config->tx_wdev = dev->ieee80211_ptr; |
5398 | } |
5399 | } else if (!config->index) { |
5400 | config->tx_wdev = dev->ieee80211_ptr; |
5401 | } else { |
5402 | return -EINVAL; |
5403 | } |
5404 | |
5405 | return 0; |
5406 | } |
5407 | |
5408 | static struct cfg80211_mbssid_elems * |
5409 | nl80211_parse_mbssid_elems(struct wiphy *wiphy, struct nlattr *attrs) |
5410 | { |
5411 | struct nlattr *nl_elems; |
5412 | struct cfg80211_mbssid_elems *elems; |
5413 | int rem_elems; |
5414 | u8 i = 0, num_elems = 0; |
5415 | |
5416 | if (!wiphy->mbssid_max_interfaces) |
5417 | return ERR_PTR(error: -EINVAL); |
5418 | |
5419 | nla_for_each_nested(nl_elems, attrs, rem_elems) { |
5420 | if (num_elems >= 255) |
5421 | return ERR_PTR(error: -EINVAL); |
5422 | num_elems++; |
5423 | } |
5424 | |
5425 | elems = kzalloc(struct_size(elems, elem, num_elems), GFP_KERNEL); |
5426 | if (!elems) |
5427 | return ERR_PTR(error: -ENOMEM); |
5428 | elems->cnt = num_elems; |
5429 | |
5430 | nla_for_each_nested(nl_elems, attrs, rem_elems) { |
5431 | elems->elem[i].data = nla_data(nla: nl_elems); |
5432 | elems->elem[i].len = nla_len(nla: nl_elems); |
5433 | i++; |
5434 | } |
5435 | return elems; |
5436 | } |
5437 | |
5438 | static struct cfg80211_rnr_elems * |
5439 | nl80211_parse_rnr_elems(struct wiphy *wiphy, struct nlattr *attrs, |
5440 | struct netlink_ext_ack *extack) |
5441 | { |
5442 | struct nlattr *nl_elems; |
5443 | struct cfg80211_rnr_elems *elems; |
5444 | int rem_elems; |
5445 | u8 i = 0, num_elems = 0; |
5446 | |
5447 | nla_for_each_nested(nl_elems, attrs, rem_elems) { |
5448 | int ret; |
5449 | |
5450 | ret = validate_ie_attr(attr: nl_elems, extack); |
5451 | if (ret) |
5452 | return ERR_PTR(error: ret); |
5453 | |
5454 | num_elems++; |
5455 | } |
5456 | |
5457 | elems = kzalloc(struct_size(elems, elem, num_elems), GFP_KERNEL); |
5458 | if (!elems) |
5459 | return ERR_PTR(error: -ENOMEM); |
5460 | elems->cnt = num_elems; |
5461 | |
5462 | nla_for_each_nested(nl_elems, attrs, rem_elems) { |
5463 | elems->elem[i].data = nla_data(nla: nl_elems); |
5464 | elems->elem[i].len = nla_len(nla: nl_elems); |
5465 | i++; |
5466 | } |
5467 | return elems; |
5468 | } |
5469 | |
5470 | static int nl80211_parse_he_bss_color(struct nlattr *attrs, |
5471 | struct cfg80211_he_bss_color *he_bss_color) |
5472 | { |
5473 | struct nlattr *tb[NL80211_HE_BSS_COLOR_ATTR_MAX + 1]; |
5474 | int err; |
5475 | |
5476 | err = nla_parse_nested(tb, maxtype: NL80211_HE_BSS_COLOR_ATTR_MAX, nla: attrs, |
5477 | policy: he_bss_color_policy, NULL); |
5478 | if (err) |
5479 | return err; |
5480 | |
5481 | if (!tb[NL80211_HE_BSS_COLOR_ATTR_COLOR]) |
5482 | return -EINVAL; |
5483 | |
5484 | he_bss_color->color = |
5485 | nla_get_u8(nla: tb[NL80211_HE_BSS_COLOR_ATTR_COLOR]); |
5486 | he_bss_color->enabled = |
5487 | !nla_get_flag(nla: tb[NL80211_HE_BSS_COLOR_ATTR_DISABLED]); |
5488 | he_bss_color->partial = |
5489 | nla_get_flag(nla: tb[NL80211_HE_BSS_COLOR_ATTR_PARTIAL]); |
5490 | |
5491 | return 0; |
5492 | } |
5493 | |
5494 | static int nl80211_parse_beacon(struct cfg80211_registered_device *rdev, |
5495 | struct nlattr *attrs[], |
5496 | struct cfg80211_beacon_data *bcn, |
5497 | struct netlink_ext_ack *extack) |
5498 | { |
5499 | bool haveinfo = false; |
5500 | int err; |
5501 | |
5502 | memset(bcn, 0, sizeof(*bcn)); |
5503 | |
5504 | bcn->link_id = nl80211_link_id(attrs); |
5505 | |
5506 | if (attrs[NL80211_ATTR_BEACON_HEAD]) { |
5507 | bcn->head = nla_data(nla: attrs[NL80211_ATTR_BEACON_HEAD]); |
5508 | bcn->head_len = nla_len(nla: attrs[NL80211_ATTR_BEACON_HEAD]); |
5509 | if (!bcn->head_len) |
5510 | return -EINVAL; |
5511 | haveinfo = true; |
5512 | } |
5513 | |
5514 | if (attrs[NL80211_ATTR_BEACON_TAIL]) { |
5515 | bcn->tail = nla_data(nla: attrs[NL80211_ATTR_BEACON_TAIL]); |
5516 | bcn->tail_len = nla_len(nla: attrs[NL80211_ATTR_BEACON_TAIL]); |
5517 | haveinfo = true; |
5518 | } |
5519 | |
5520 | if (!haveinfo) |
5521 | return -EINVAL; |
5522 | |
5523 | if (attrs[NL80211_ATTR_IE]) { |
5524 | bcn->beacon_ies = nla_data(nla: attrs[NL80211_ATTR_IE]); |
5525 | bcn->beacon_ies_len = nla_len(nla: attrs[NL80211_ATTR_IE]); |
5526 | } |
5527 | |
5528 | if (attrs[NL80211_ATTR_IE_PROBE_RESP]) { |
5529 | bcn->proberesp_ies = |
5530 | nla_data(nla: attrs[NL80211_ATTR_IE_PROBE_RESP]); |
5531 | bcn->proberesp_ies_len = |
5532 | nla_len(nla: attrs[NL80211_ATTR_IE_PROBE_RESP]); |
5533 | } |
5534 | |
5535 | if (attrs[NL80211_ATTR_IE_ASSOC_RESP]) { |
5536 | bcn->assocresp_ies = |
5537 | nla_data(nla: attrs[NL80211_ATTR_IE_ASSOC_RESP]); |
5538 | bcn->assocresp_ies_len = |
5539 | nla_len(nla: attrs[NL80211_ATTR_IE_ASSOC_RESP]); |
5540 | } |
5541 | |
5542 | if (attrs[NL80211_ATTR_PROBE_RESP]) { |
5543 | bcn->probe_resp = nla_data(nla: attrs[NL80211_ATTR_PROBE_RESP]); |
5544 | bcn->probe_resp_len = nla_len(nla: attrs[NL80211_ATTR_PROBE_RESP]); |
5545 | } |
5546 | |
5547 | if (attrs[NL80211_ATTR_FTM_RESPONDER]) { |
5548 | struct nlattr *tb[NL80211_FTM_RESP_ATTR_MAX + 1]; |
5549 | |
5550 | err = nla_parse_nested_deprecated(tb, |
5551 | maxtype: NL80211_FTM_RESP_ATTR_MAX, |
5552 | nla: attrs[NL80211_ATTR_FTM_RESPONDER], |
5553 | NULL, NULL); |
5554 | if (err) |
5555 | return err; |
5556 | |
5557 | if (tb[NL80211_FTM_RESP_ATTR_ENABLED] && |
5558 | wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
5559 | ftidx: NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER)) |
5560 | bcn->ftm_responder = 1; |
5561 | else |
5562 | return -EOPNOTSUPP; |
5563 | |
5564 | if (tb[NL80211_FTM_RESP_ATTR_LCI]) { |
5565 | bcn->lci = nla_data(nla: tb[NL80211_FTM_RESP_ATTR_LCI]); |
5566 | bcn->lci_len = nla_len(nla: tb[NL80211_FTM_RESP_ATTR_LCI]); |
5567 | } |
5568 | |
5569 | if (tb[NL80211_FTM_RESP_ATTR_CIVICLOC]) { |
5570 | bcn->civicloc = nla_data(nla: tb[NL80211_FTM_RESP_ATTR_CIVICLOC]); |
5571 | bcn->civicloc_len = nla_len(nla: tb[NL80211_FTM_RESP_ATTR_CIVICLOC]); |
5572 | } |
5573 | } else { |
5574 | bcn->ftm_responder = -1; |
5575 | } |
5576 | |
5577 | if (attrs[NL80211_ATTR_HE_BSS_COLOR]) { |
5578 | err = nl80211_parse_he_bss_color(attrs: attrs[NL80211_ATTR_HE_BSS_COLOR], |
5579 | he_bss_color: &bcn->he_bss_color); |
5580 | if (err) |
5581 | return err; |
5582 | bcn->he_bss_color_valid = true; |
5583 | } |
5584 | |
5585 | if (attrs[NL80211_ATTR_MBSSID_ELEMS]) { |
5586 | struct cfg80211_mbssid_elems *mbssid = |
5587 | nl80211_parse_mbssid_elems(wiphy: &rdev->wiphy, |
5588 | attrs: attrs[NL80211_ATTR_MBSSID_ELEMS]); |
5589 | |
5590 | if (IS_ERR(ptr: mbssid)) |
5591 | return PTR_ERR(ptr: mbssid); |
5592 | |
5593 | bcn->mbssid_ies = mbssid; |
5594 | |
5595 | if (bcn->mbssid_ies && attrs[NL80211_ATTR_EMA_RNR_ELEMS]) { |
5596 | struct cfg80211_rnr_elems *rnr = |
5597 | nl80211_parse_rnr_elems(wiphy: &rdev->wiphy, |
5598 | attrs: attrs[NL80211_ATTR_EMA_RNR_ELEMS], |
5599 | extack); |
5600 | |
5601 | if (IS_ERR(ptr: rnr)) |
5602 | return PTR_ERR(ptr: rnr); |
5603 | |
5604 | if (rnr && rnr->cnt < bcn->mbssid_ies->cnt) |
5605 | return -EINVAL; |
5606 | |
5607 | bcn->rnr_ies = rnr; |
5608 | } |
5609 | } |
5610 | |
5611 | return 0; |
5612 | } |
5613 | |
5614 | static int nl80211_parse_he_obss_pd(struct nlattr *attrs, |
5615 | struct ieee80211_he_obss_pd *he_obss_pd) |
5616 | { |
5617 | struct nlattr *tb[NL80211_HE_OBSS_PD_ATTR_MAX + 1]; |
5618 | int err; |
5619 | |
5620 | err = nla_parse_nested(tb, maxtype: NL80211_HE_OBSS_PD_ATTR_MAX, nla: attrs, |
5621 | policy: he_obss_pd_policy, NULL); |
5622 | if (err) |
5623 | return err; |
5624 | |
5625 | if (!tb[NL80211_HE_OBSS_PD_ATTR_SR_CTRL]) |
5626 | return -EINVAL; |
5627 | |
5628 | he_obss_pd->sr_ctrl = nla_get_u8(nla: tb[NL80211_HE_OBSS_PD_ATTR_SR_CTRL]); |
5629 | |
5630 | if (tb[NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET]) |
5631 | he_obss_pd->min_offset = |
5632 | nla_get_u8(nla: tb[NL80211_HE_OBSS_PD_ATTR_MIN_OFFSET]); |
5633 | if (tb[NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET]) |
5634 | he_obss_pd->max_offset = |
5635 | nla_get_u8(nla: tb[NL80211_HE_OBSS_PD_ATTR_MAX_OFFSET]); |
5636 | if (tb[NL80211_HE_OBSS_PD_ATTR_NON_SRG_MAX_OFFSET]) |
5637 | he_obss_pd->non_srg_max_offset = |
5638 | nla_get_u8(nla: tb[NL80211_HE_OBSS_PD_ATTR_NON_SRG_MAX_OFFSET]); |
5639 | |
5640 | if (he_obss_pd->min_offset > he_obss_pd->max_offset) |
5641 | return -EINVAL; |
5642 | |
5643 | if (tb[NL80211_HE_OBSS_PD_ATTR_BSS_COLOR_BITMAP]) |
5644 | memcpy(he_obss_pd->bss_color_bitmap, |
5645 | nla_data(tb[NL80211_HE_OBSS_PD_ATTR_BSS_COLOR_BITMAP]), |
5646 | sizeof(he_obss_pd->bss_color_bitmap)); |
5647 | |
5648 | if (tb[NL80211_HE_OBSS_PD_ATTR_PARTIAL_BSSID_BITMAP]) |
5649 | memcpy(he_obss_pd->partial_bssid_bitmap, |
5650 | nla_data(tb[NL80211_HE_OBSS_PD_ATTR_PARTIAL_BSSID_BITMAP]), |
5651 | sizeof(he_obss_pd->partial_bssid_bitmap)); |
5652 | |
5653 | he_obss_pd->enable = true; |
5654 | |
5655 | return 0; |
5656 | } |
5657 | |
5658 | static int nl80211_parse_fils_discovery(struct cfg80211_registered_device *rdev, |
5659 | struct nlattr *attrs, |
5660 | struct cfg80211_fils_discovery *fd) |
5661 | { |
5662 | struct nlattr *tb[NL80211_FILS_DISCOVERY_ATTR_MAX + 1]; |
5663 | int ret; |
5664 | |
5665 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
5666 | ftidx: NL80211_EXT_FEATURE_FILS_DISCOVERY)) |
5667 | return -EINVAL; |
5668 | |
5669 | ret = nla_parse_nested(tb, maxtype: NL80211_FILS_DISCOVERY_ATTR_MAX, nla: attrs, |
5670 | NULL, NULL); |
5671 | if (ret) |
5672 | return ret; |
5673 | |
5674 | if (!tb[NL80211_FILS_DISCOVERY_ATTR_INT_MIN] && |
5675 | !tb[NL80211_FILS_DISCOVERY_ATTR_INT_MAX] && |
5676 | !tb[NL80211_FILS_DISCOVERY_ATTR_TMPL]) { |
5677 | fd->update = true; |
5678 | return 0; |
5679 | } |
5680 | |
5681 | if (!tb[NL80211_FILS_DISCOVERY_ATTR_INT_MIN] || |
5682 | !tb[NL80211_FILS_DISCOVERY_ATTR_INT_MAX] || |
5683 | !tb[NL80211_FILS_DISCOVERY_ATTR_TMPL]) |
5684 | return -EINVAL; |
5685 | |
5686 | fd->tmpl_len = nla_len(nla: tb[NL80211_FILS_DISCOVERY_ATTR_TMPL]); |
5687 | fd->tmpl = nla_data(nla: tb[NL80211_FILS_DISCOVERY_ATTR_TMPL]); |
5688 | fd->min_interval = nla_get_u32(nla: tb[NL80211_FILS_DISCOVERY_ATTR_INT_MIN]); |
5689 | fd->max_interval = nla_get_u32(nla: tb[NL80211_FILS_DISCOVERY_ATTR_INT_MAX]); |
5690 | fd->update = true; |
5691 | return 0; |
5692 | } |
5693 | |
5694 | static int |
5695 | nl80211_parse_unsol_bcast_probe_resp(struct cfg80211_registered_device *rdev, |
5696 | struct nlattr *attrs, |
5697 | struct cfg80211_unsol_bcast_probe_resp *presp) |
5698 | { |
5699 | struct nlattr *tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_MAX + 1]; |
5700 | int ret; |
5701 | |
5702 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
5703 | ftidx: NL80211_EXT_FEATURE_UNSOL_BCAST_PROBE_RESP)) |
5704 | return -EINVAL; |
5705 | |
5706 | ret = nla_parse_nested(tb, maxtype: NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_MAX, |
5707 | nla: attrs, NULL, NULL); |
5708 | if (ret) |
5709 | return ret; |
5710 | |
5711 | if (!tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT] && |
5712 | !tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL]) { |
5713 | presp->update = true; |
5714 | return 0; |
5715 | } |
5716 | |
5717 | if (!tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT] || |
5718 | !tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL]) |
5719 | return -EINVAL; |
5720 | |
5721 | presp->tmpl = nla_data(nla: tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL]); |
5722 | presp->tmpl_len = nla_len(nla: tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_TMPL]); |
5723 | presp->interval = nla_get_u32(nla: tb[NL80211_UNSOL_BCAST_PROBE_RESP_ATTR_INT]); |
5724 | presp->update = true; |
5725 | return 0; |
5726 | } |
5727 | |
5728 | static void nl80211_check_ap_rate_selectors(struct cfg80211_ap_settings *params, |
5729 | const struct element *rates) |
5730 | { |
5731 | int i; |
5732 | |
5733 | if (!rates) |
5734 | return; |
5735 | |
5736 | for (i = 0; i < rates->datalen; i++) { |
5737 | if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_HT_PHY) |
5738 | params->ht_required = true; |
5739 | if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_VHT_PHY) |
5740 | params->vht_required = true; |
5741 | if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_HE_PHY) |
5742 | params->he_required = true; |
5743 | if (rates->data[i] == BSS_MEMBERSHIP_SELECTOR_SAE_H2E) |
5744 | params->sae_h2e_required = true; |
5745 | } |
5746 | } |
5747 | |
5748 | /* |
5749 | * Since the nl80211 API didn't include, from the beginning, attributes about |
5750 | * HT/VHT requirements/capabilities, we parse them out of the IEs for the |
5751 | * benefit of drivers that rebuild IEs in the firmware. |
5752 | */ |
5753 | static int nl80211_calculate_ap_params(struct cfg80211_ap_settings *params) |
5754 | { |
5755 | const struct cfg80211_beacon_data *bcn = ¶ms->beacon; |
5756 | size_t ies_len = bcn->tail_len; |
5757 | const u8 *ies = bcn->tail; |
5758 | const struct element *rates; |
5759 | const struct element *cap; |
5760 | |
5761 | rates = cfg80211_find_elem(eid: WLAN_EID_SUPP_RATES, ies, len: ies_len); |
5762 | nl80211_check_ap_rate_selectors(params, rates); |
5763 | |
5764 | rates = cfg80211_find_elem(eid: WLAN_EID_EXT_SUPP_RATES, ies, len: ies_len); |
5765 | nl80211_check_ap_rate_selectors(params, rates); |
5766 | |
5767 | cap = cfg80211_find_elem(eid: WLAN_EID_HT_CAPABILITY, ies, len: ies_len); |
5768 | if (cap && cap->datalen >= sizeof(*params->ht_cap)) |
5769 | params->ht_cap = (void *)cap->data; |
5770 | cap = cfg80211_find_elem(eid: WLAN_EID_VHT_CAPABILITY, ies, len: ies_len); |
5771 | if (cap && cap->datalen >= sizeof(*params->vht_cap)) |
5772 | params->vht_cap = (void *)cap->data; |
5773 | cap = cfg80211_find_ext_elem(ext_eid: WLAN_EID_EXT_HE_CAPABILITY, ies, len: ies_len); |
5774 | if (cap && cap->datalen >= sizeof(*params->he_cap) + 1) |
5775 | params->he_cap = (void *)(cap->data + 1); |
5776 | cap = cfg80211_find_ext_elem(ext_eid: WLAN_EID_EXT_HE_OPERATION, ies, len: ies_len); |
5777 | if (cap && cap->datalen >= sizeof(*params->he_oper) + 1) |
5778 | params->he_oper = (void *)(cap->data + 1); |
5779 | cap = cfg80211_find_ext_elem(ext_eid: WLAN_EID_EXT_EHT_CAPABILITY, ies, len: ies_len); |
5780 | if (cap) { |
5781 | if (!cap->datalen) |
5782 | return -EINVAL; |
5783 | params->eht_cap = (void *)(cap->data + 1); |
5784 | if (!ieee80211_eht_capa_size_ok(he_capa: (const u8 *)params->he_cap, |
5785 | data: (const u8 *)params->eht_cap, |
5786 | len: cap->datalen - 1, from_ap: true)) |
5787 | return -EINVAL; |
5788 | } |
5789 | cap = cfg80211_find_ext_elem(ext_eid: WLAN_EID_EXT_EHT_OPERATION, ies, len: ies_len); |
5790 | if (cap) { |
5791 | if (!cap->datalen) |
5792 | return -EINVAL; |
5793 | params->eht_oper = (void *)(cap->data + 1); |
5794 | if (!ieee80211_eht_oper_size_ok(data: (const u8 *)params->eht_oper, |
5795 | len: cap->datalen - 1)) |
5796 | return -EINVAL; |
5797 | } |
5798 | return 0; |
5799 | } |
5800 | |
5801 | static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev, |
5802 | struct cfg80211_ap_settings *params) |
5803 | { |
5804 | struct wireless_dev *wdev; |
5805 | |
5806 | list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { |
5807 | if (wdev->iftype != NL80211_IFTYPE_AP && |
5808 | wdev->iftype != NL80211_IFTYPE_P2P_GO) |
5809 | continue; |
5810 | |
5811 | if (!wdev->u.ap.preset_chandef.chan) |
5812 | continue; |
5813 | |
5814 | params->chandef = wdev->u.ap.preset_chandef; |
5815 | return true; |
5816 | } |
5817 | |
5818 | return false; |
5819 | } |
5820 | |
5821 | static bool nl80211_valid_auth_type(struct cfg80211_registered_device *rdev, |
5822 | enum nl80211_auth_type auth_type, |
5823 | enum nl80211_commands cmd) |
5824 | { |
5825 | if (auth_type > NL80211_AUTHTYPE_MAX) |
5826 | return false; |
5827 | |
5828 | switch (cmd) { |
5829 | case NL80211_CMD_AUTHENTICATE: |
5830 | if (!(rdev->wiphy.features & NL80211_FEATURE_SAE) && |
5831 | auth_type == NL80211_AUTHTYPE_SAE) |
5832 | return false; |
5833 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
5834 | ftidx: NL80211_EXT_FEATURE_FILS_STA) && |
5835 | (auth_type == NL80211_AUTHTYPE_FILS_SK || |
5836 | auth_type == NL80211_AUTHTYPE_FILS_SK_PFS || |
5837 | auth_type == NL80211_AUTHTYPE_FILS_PK)) |
5838 | return false; |
5839 | return true; |
5840 | case NL80211_CMD_CONNECT: |
5841 | if (!(rdev->wiphy.features & NL80211_FEATURE_SAE) && |
5842 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
5843 | ftidx: NL80211_EXT_FEATURE_SAE_OFFLOAD) && |
5844 | auth_type == NL80211_AUTHTYPE_SAE) |
5845 | return false; |
5846 | |
5847 | /* FILS with SK PFS or PK not supported yet */ |
5848 | if (auth_type == NL80211_AUTHTYPE_FILS_SK_PFS || |
5849 | auth_type == NL80211_AUTHTYPE_FILS_PK) |
5850 | return false; |
5851 | if (!wiphy_ext_feature_isset( |
5852 | wiphy: &rdev->wiphy, |
5853 | ftidx: NL80211_EXT_FEATURE_FILS_SK_OFFLOAD) && |
5854 | auth_type == NL80211_AUTHTYPE_FILS_SK) |
5855 | return false; |
5856 | return true; |
5857 | case NL80211_CMD_START_AP: |
5858 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
5859 | ftidx: NL80211_EXT_FEATURE_SAE_OFFLOAD_AP) && |
5860 | auth_type == NL80211_AUTHTYPE_SAE) |
5861 | return false; |
5862 | /* FILS not supported yet */ |
5863 | if (auth_type == NL80211_AUTHTYPE_FILS_SK || |
5864 | auth_type == NL80211_AUTHTYPE_FILS_SK_PFS || |
5865 | auth_type == NL80211_AUTHTYPE_FILS_PK) |
5866 | return false; |
5867 | return true; |
5868 | default: |
5869 | return false; |
5870 | } |
5871 | } |
5872 | |
5873 | static void nl80211_send_ap_started(struct wireless_dev *wdev, |
5874 | unsigned int link_id) |
5875 | { |
5876 | struct wiphy *wiphy = wdev->wiphy; |
5877 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
5878 | struct sk_buff *msg; |
5879 | void *hdr; |
5880 | |
5881 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
5882 | if (!msg) |
5883 | return; |
5884 | |
5885 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_START_AP); |
5886 | if (!hdr) |
5887 | goto out; |
5888 | |
5889 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
5890 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: wdev->netdev->ifindex) || |
5891 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), |
5892 | padattr: NL80211_ATTR_PAD) || |
5893 | (wdev->u.ap.ssid_len && |
5894 | nla_put(skb: msg, NL80211_ATTR_SSID, attrlen: wdev->u.ap.ssid_len, |
5895 | data: wdev->u.ap.ssid)) || |
5896 | (wdev->valid_links && |
5897 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: link_id))) |
5898 | goto out; |
5899 | |
5900 | genlmsg_end(skb: msg, hdr); |
5901 | |
5902 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy), skb: msg, portid: 0, |
5903 | group: NL80211_MCGRP_MLME, GFP_KERNEL); |
5904 | return; |
5905 | out: |
5906 | nlmsg_free(skb: msg); |
5907 | } |
5908 | |
5909 | static int nl80211_validate_ap_phy_operation(struct cfg80211_ap_settings *params) |
5910 | { |
5911 | struct ieee80211_channel *channel = params->chandef.chan; |
5912 | |
5913 | if ((params->he_cap || params->he_oper) && |
5914 | (channel->flags & IEEE80211_CHAN_NO_HE)) |
5915 | return -EOPNOTSUPP; |
5916 | |
5917 | if ((params->eht_cap || params->eht_oper) && |
5918 | (channel->flags & IEEE80211_CHAN_NO_EHT)) |
5919 | return -EOPNOTSUPP; |
5920 | |
5921 | return 0; |
5922 | } |
5923 | |
5924 | static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) |
5925 | { |
5926 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
5927 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); |
5928 | struct net_device *dev = info->user_ptr[1]; |
5929 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
5930 | struct cfg80211_ap_settings *params; |
5931 | int err; |
5932 | |
5933 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && |
5934 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) |
5935 | return -EOPNOTSUPP; |
5936 | |
5937 | if (!rdev->ops->start_ap) |
5938 | return -EOPNOTSUPP; |
5939 | |
5940 | if (wdev->links[link_id].ap.beacon_interval) |
5941 | return -EALREADY; |
5942 | |
5943 | /* these are required for START_AP */ |
5944 | if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] || |
5945 | !info->attrs[NL80211_ATTR_DTIM_PERIOD] || |
5946 | !info->attrs[NL80211_ATTR_BEACON_HEAD]) |
5947 | return -EINVAL; |
5948 | |
5949 | params = kzalloc(size: sizeof(*params), GFP_KERNEL); |
5950 | if (!params) |
5951 | return -ENOMEM; |
5952 | |
5953 | err = nl80211_parse_beacon(rdev, attrs: info->attrs, bcn: ¶ms->beacon, |
5954 | extack: info->extack); |
5955 | if (err) |
5956 | goto out; |
5957 | |
5958 | params->beacon_interval = |
5959 | nla_get_u32(nla: info->attrs[NL80211_ATTR_BEACON_INTERVAL]); |
5960 | params->dtim_period = |
5961 | nla_get_u32(nla: info->attrs[NL80211_ATTR_DTIM_PERIOD]); |
5962 | |
5963 | err = cfg80211_validate_beacon_int(rdev, iftype: dev->ieee80211_ptr->iftype, |
5964 | beacon_int: params->beacon_interval); |
5965 | if (err) |
5966 | goto out; |
5967 | |
5968 | /* |
5969 | * In theory, some of these attributes should be required here |
5970 | * but since they were not used when the command was originally |
5971 | * added, keep them optional for old user space programs to let |
5972 | * them continue to work with drivers that do not need the |
5973 | * additional information -- drivers must check! |
5974 | */ |
5975 | if (info->attrs[NL80211_ATTR_SSID]) { |
5976 | params->ssid = nla_data(nla: info->attrs[NL80211_ATTR_SSID]); |
5977 | params->ssid_len = |
5978 | nla_len(nla: info->attrs[NL80211_ATTR_SSID]); |
5979 | if (params->ssid_len == 0) { |
5980 | err = -EINVAL; |
5981 | goto out; |
5982 | } |
5983 | |
5984 | if (wdev->u.ap.ssid_len && |
5985 | (wdev->u.ap.ssid_len != params->ssid_len || |
5986 | memcmp(p: wdev->u.ap.ssid, q: params->ssid, size: params->ssid_len))) { |
5987 | /* require identical SSID for MLO */ |
5988 | err = -EINVAL; |
5989 | goto out; |
5990 | } |
5991 | } else if (wdev->valid_links) { |
5992 | /* require SSID for MLO */ |
5993 | err = -EINVAL; |
5994 | goto out; |
5995 | } |
5996 | |
5997 | if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) |
5998 | params->hidden_ssid = nla_get_u32( |
5999 | nla: info->attrs[NL80211_ATTR_HIDDEN_SSID]); |
6000 | |
6001 | params->privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; |
6002 | |
6003 | if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { |
6004 | params->auth_type = nla_get_u32( |
6005 | nla: info->attrs[NL80211_ATTR_AUTH_TYPE]); |
6006 | if (!nl80211_valid_auth_type(rdev, auth_type: params->auth_type, |
6007 | cmd: NL80211_CMD_START_AP)) { |
6008 | err = -EINVAL; |
6009 | goto out; |
6010 | } |
6011 | } else |
6012 | params->auth_type = NL80211_AUTHTYPE_AUTOMATIC; |
6013 | |
6014 | err = nl80211_crypto_settings(rdev, info, settings: ¶ms->crypto, |
6015 | NL80211_MAX_NR_CIPHER_SUITES); |
6016 | if (err) |
6017 | goto out; |
6018 | |
6019 | if (info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]) { |
6020 | if (!(rdev->wiphy.features & NL80211_FEATURE_INACTIVITY_TIMER)) { |
6021 | err = -EOPNOTSUPP; |
6022 | goto out; |
6023 | } |
6024 | params->inactivity_timeout = nla_get_u16( |
6025 | nla: info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]); |
6026 | } |
6027 | |
6028 | if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) { |
6029 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) { |
6030 | err = -EINVAL; |
6031 | goto out; |
6032 | } |
6033 | params->p2p_ctwindow = |
6034 | nla_get_u8(nla: info->attrs[NL80211_ATTR_P2P_CTWINDOW]); |
6035 | if (params->p2p_ctwindow != 0 && |
6036 | !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN)) { |
6037 | err = -EINVAL; |
6038 | goto out; |
6039 | } |
6040 | } |
6041 | |
6042 | if (info->attrs[NL80211_ATTR_P2P_OPPPS]) { |
6043 | u8 tmp; |
6044 | |
6045 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) { |
6046 | err = -EINVAL; |
6047 | goto out; |
6048 | } |
6049 | tmp = nla_get_u8(nla: info->attrs[NL80211_ATTR_P2P_OPPPS]); |
6050 | params->p2p_opp_ps = tmp; |
6051 | if (params->p2p_opp_ps != 0 && |
6052 | !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS)) { |
6053 | err = -EINVAL; |
6054 | goto out; |
6055 | } |
6056 | } |
6057 | |
6058 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { |
6059 | err = nl80211_parse_chandef(rdev, info, chandef: ¶ms->chandef); |
6060 | if (err) |
6061 | goto out; |
6062 | } else if (wdev->valid_links) { |
6063 | /* with MLD need to specify the channel configuration */ |
6064 | err = -EINVAL; |
6065 | goto out; |
6066 | } else if (wdev->u.ap.preset_chandef.chan) { |
6067 | params->chandef = wdev->u.ap.preset_chandef; |
6068 | } else if (!nl80211_get_ap_channel(rdev, params)) { |
6069 | err = -EINVAL; |
6070 | goto out; |
6071 | } |
6072 | |
6073 | if (!cfg80211_reg_can_beacon_relax(wiphy: &rdev->wiphy, chandef: ¶ms->chandef, |
6074 | iftype: wdev->iftype)) { |
6075 | err = -EINVAL; |
6076 | goto out; |
6077 | } |
6078 | |
6079 | if (info->attrs[NL80211_ATTR_TX_RATES]) { |
6080 | err = nl80211_parse_tx_bitrate_mask(info, attrs: info->attrs, |
6081 | attr: NL80211_ATTR_TX_RATES, |
6082 | mask: ¶ms->beacon_rate, |
6083 | dev, default_all_enabled: false, link_id); |
6084 | if (err) |
6085 | goto out; |
6086 | |
6087 | err = validate_beacon_tx_rate(rdev, band: params->chandef.chan->band, |
6088 | beacon_rate: ¶ms->beacon_rate); |
6089 | if (err) |
6090 | goto out; |
6091 | } |
6092 | |
6093 | if (info->attrs[NL80211_ATTR_SMPS_MODE]) { |
6094 | params->smps_mode = |
6095 | nla_get_u8(nla: info->attrs[NL80211_ATTR_SMPS_MODE]); |
6096 | switch (params->smps_mode) { |
6097 | case NL80211_SMPS_OFF: |
6098 | break; |
6099 | case NL80211_SMPS_STATIC: |
6100 | if (!(rdev->wiphy.features & |
6101 | NL80211_FEATURE_STATIC_SMPS)) { |
6102 | err = -EINVAL; |
6103 | goto out; |
6104 | } |
6105 | break; |
6106 | case NL80211_SMPS_DYNAMIC: |
6107 | if (!(rdev->wiphy.features & |
6108 | NL80211_FEATURE_DYNAMIC_SMPS)) { |
6109 | err = -EINVAL; |
6110 | goto out; |
6111 | } |
6112 | break; |
6113 | default: |
6114 | err = -EINVAL; |
6115 | goto out; |
6116 | } |
6117 | } else { |
6118 | params->smps_mode = NL80211_SMPS_OFF; |
6119 | } |
6120 | |
6121 | params->pbss = nla_get_flag(nla: info->attrs[NL80211_ATTR_PBSS]); |
6122 | if (params->pbss && !rdev->wiphy.bands[NL80211_BAND_60GHZ]) { |
6123 | err = -EOPNOTSUPP; |
6124 | goto out; |
6125 | } |
6126 | |
6127 | if (info->attrs[NL80211_ATTR_ACL_POLICY]) { |
6128 | params->acl = parse_acl_data(wiphy: &rdev->wiphy, info); |
6129 | if (IS_ERR(ptr: params->acl)) { |
6130 | err = PTR_ERR(ptr: params->acl); |
6131 | params->acl = NULL; |
6132 | goto out; |
6133 | } |
6134 | } |
6135 | |
6136 | params->twt_responder = |
6137 | nla_get_flag(nla: info->attrs[NL80211_ATTR_TWT_RESPONDER]); |
6138 | |
6139 | if (info->attrs[NL80211_ATTR_HE_OBSS_PD]) { |
6140 | err = nl80211_parse_he_obss_pd( |
6141 | attrs: info->attrs[NL80211_ATTR_HE_OBSS_PD], |
6142 | he_obss_pd: ¶ms->he_obss_pd); |
6143 | if (err) |
6144 | goto out; |
6145 | } |
6146 | |
6147 | if (info->attrs[NL80211_ATTR_FILS_DISCOVERY]) { |
6148 | err = nl80211_parse_fils_discovery(rdev, |
6149 | attrs: info->attrs[NL80211_ATTR_FILS_DISCOVERY], |
6150 | fd: ¶ms->fils_discovery); |
6151 | if (err) |
6152 | goto out; |
6153 | } |
6154 | |
6155 | if (info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP]) { |
6156 | err = nl80211_parse_unsol_bcast_probe_resp( |
6157 | rdev, attrs: info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP], |
6158 | presp: ¶ms->unsol_bcast_probe_resp); |
6159 | if (err) |
6160 | goto out; |
6161 | } |
6162 | |
6163 | if (info->attrs[NL80211_ATTR_MBSSID_CONFIG]) { |
6164 | err = nl80211_parse_mbssid_config(wiphy: &rdev->wiphy, dev, |
6165 | attrs: info->attrs[NL80211_ATTR_MBSSID_CONFIG], |
6166 | config: ¶ms->mbssid_config, |
6167 | num_elems: params->beacon.mbssid_ies ? |
6168 | params->beacon.mbssid_ies->cnt : |
6169 | 0); |
6170 | if (err) |
6171 | goto out; |
6172 | } |
6173 | |
6174 | if (!params->mbssid_config.ema && params->beacon.rnr_ies) { |
6175 | err = -EINVAL; |
6176 | goto out; |
6177 | } |
6178 | |
6179 | err = nl80211_calculate_ap_params(params); |
6180 | if (err) |
6181 | goto out; |
6182 | |
6183 | err = nl80211_validate_ap_phy_operation(params); |
6184 | if (err) |
6185 | goto out; |
6186 | |
6187 | if (info->attrs[NL80211_ATTR_AP_SETTINGS_FLAGS]) |
6188 | params->flags = nla_get_u32( |
6189 | nla: info->attrs[NL80211_ATTR_AP_SETTINGS_FLAGS]); |
6190 | else if (info->attrs[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT]) |
6191 | params->flags |= NL80211_AP_SETTINGS_EXTERNAL_AUTH_SUPPORT; |
6192 | |
6193 | if (wdev->conn_owner_nlportid && |
6194 | info->attrs[NL80211_ATTR_SOCKET_OWNER] && |
6195 | wdev->conn_owner_nlportid != info->snd_portid) { |
6196 | err = -EINVAL; |
6197 | goto out; |
6198 | } |
6199 | |
6200 | /* FIXME: validate MLO/link-id against driver capabilities */ |
6201 | |
6202 | err = rdev_start_ap(rdev, dev, settings: params); |
6203 | if (!err) { |
6204 | wdev->links[link_id].ap.beacon_interval = params->beacon_interval; |
6205 | wdev->links[link_id].ap.chandef = params->chandef; |
6206 | wdev->u.ap.ssid_len = params->ssid_len; |
6207 | memcpy(wdev->u.ap.ssid, params->ssid, |
6208 | params->ssid_len); |
6209 | |
6210 | if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) |
6211 | wdev->conn_owner_nlportid = info->snd_portid; |
6212 | |
6213 | nl80211_send_ap_started(wdev, link_id); |
6214 | } |
6215 | out: |
6216 | kfree(objp: params->acl); |
6217 | kfree(objp: params->beacon.mbssid_ies); |
6218 | if (params->mbssid_config.tx_wdev && |
6219 | params->mbssid_config.tx_wdev->netdev && |
6220 | params->mbssid_config.tx_wdev->netdev != dev) |
6221 | dev_put(dev: params->mbssid_config.tx_wdev->netdev); |
6222 | kfree(objp: params->beacon.rnr_ies); |
6223 | kfree(objp: params); |
6224 | |
6225 | return err; |
6226 | } |
6227 | |
6228 | static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info) |
6229 | { |
6230 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
6231 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); |
6232 | struct net_device *dev = info->user_ptr[1]; |
6233 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
6234 | struct cfg80211_ap_update *params; |
6235 | struct nlattr *attr; |
6236 | int err; |
6237 | |
6238 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && |
6239 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) |
6240 | return -EOPNOTSUPP; |
6241 | |
6242 | if (!rdev->ops->change_beacon) |
6243 | return -EOPNOTSUPP; |
6244 | |
6245 | if (!wdev->links[link_id].ap.beacon_interval) |
6246 | return -EINVAL; |
6247 | |
6248 | params = kzalloc(size: sizeof(*params), GFP_KERNEL); |
6249 | if (!params) |
6250 | return -ENOMEM; |
6251 | |
6252 | err = nl80211_parse_beacon(rdev, attrs: info->attrs, bcn: ¶ms->beacon, |
6253 | extack: info->extack); |
6254 | if (err) |
6255 | goto out; |
6256 | |
6257 | attr = info->attrs[NL80211_ATTR_FILS_DISCOVERY]; |
6258 | if (attr) { |
6259 | err = nl80211_parse_fils_discovery(rdev, attrs: attr, |
6260 | fd: ¶ms->fils_discovery); |
6261 | if (err) |
6262 | goto out; |
6263 | } |
6264 | |
6265 | attr = info->attrs[NL80211_ATTR_UNSOL_BCAST_PROBE_RESP]; |
6266 | if (attr) { |
6267 | err = nl80211_parse_unsol_bcast_probe_resp(rdev, attrs: attr, |
6268 | presp: ¶ms->unsol_bcast_probe_resp); |
6269 | if (err) |
6270 | goto out; |
6271 | } |
6272 | |
6273 | err = rdev_change_beacon(rdev, dev, info: params); |
6274 | |
6275 | out: |
6276 | kfree(objp: params->beacon.mbssid_ies); |
6277 | kfree(objp: params->beacon.rnr_ies); |
6278 | kfree(objp: params); |
6279 | return err; |
6280 | } |
6281 | |
6282 | static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info) |
6283 | { |
6284 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
6285 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); |
6286 | struct net_device *dev = info->user_ptr[1]; |
6287 | |
6288 | return cfg80211_stop_ap(rdev, dev, link: link_id, notify: false); |
6289 | } |
6290 | |
6291 | static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = { |
6292 | [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG }, |
6293 | [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG }, |
6294 | [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG }, |
6295 | [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG }, |
6296 | [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG }, |
6297 | [NL80211_STA_FLAG_TDLS_PEER] = { .type = NLA_FLAG }, |
6298 | }; |
6299 | |
6300 | static int parse_station_flags(struct genl_info *info, |
6301 | enum nl80211_iftype iftype, |
6302 | struct station_parameters *params) |
6303 | { |
6304 | struct nlattr *flags[NL80211_STA_FLAG_MAX + 1]; |
6305 | struct nlattr *nla; |
6306 | int flag; |
6307 | |
6308 | /* |
6309 | * Try parsing the new attribute first so userspace |
6310 | * can specify both for older kernels. |
6311 | */ |
6312 | nla = info->attrs[NL80211_ATTR_STA_FLAGS2]; |
6313 | if (nla) { |
6314 | struct nl80211_sta_flag_update *sta_flags; |
6315 | |
6316 | sta_flags = nla_data(nla); |
6317 | params->sta_flags_mask = sta_flags->mask; |
6318 | params->sta_flags_set = sta_flags->set; |
6319 | params->sta_flags_set &= params->sta_flags_mask; |
6320 | if ((params->sta_flags_mask | |
6321 | params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID)) |
6322 | return -EINVAL; |
6323 | return 0; |
6324 | } |
6325 | |
6326 | /* if present, parse the old attribute */ |
6327 | |
6328 | nla = info->attrs[NL80211_ATTR_STA_FLAGS]; |
6329 | if (!nla) |
6330 | return 0; |
6331 | |
6332 | if (nla_parse_nested_deprecated(tb: flags, maxtype: NL80211_STA_FLAG_MAX, nla, policy: sta_flags_policy, extack: info->extack)) |
6333 | return -EINVAL; |
6334 | |
6335 | /* |
6336 | * Only allow certain flags for interface types so that |
6337 | * other attributes are silently ignored. Remember that |
6338 | * this is backward compatibility code with old userspace |
6339 | * and shouldn't be hit in other cases anyway. |
6340 | */ |
6341 | switch (iftype) { |
6342 | case NL80211_IFTYPE_AP: |
6343 | case NL80211_IFTYPE_AP_VLAN: |
6344 | case NL80211_IFTYPE_P2P_GO: |
6345 | params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) | |
6346 | BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | |
6347 | BIT(NL80211_STA_FLAG_WME) | |
6348 | BIT(NL80211_STA_FLAG_MFP); |
6349 | break; |
6350 | case NL80211_IFTYPE_P2P_CLIENT: |
6351 | case NL80211_IFTYPE_STATION: |
6352 | params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) | |
6353 | BIT(NL80211_STA_FLAG_TDLS_PEER); |
6354 | break; |
6355 | case NL80211_IFTYPE_MESH_POINT: |
6356 | params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHENTICATED) | |
6357 | BIT(NL80211_STA_FLAG_MFP) | |
6358 | BIT(NL80211_STA_FLAG_AUTHORIZED); |
6359 | break; |
6360 | default: |
6361 | return -EINVAL; |
6362 | } |
6363 | |
6364 | for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++) { |
6365 | if (flags[flag]) { |
6366 | params->sta_flags_set |= (1<<flag); |
6367 | |
6368 | /* no longer support new API additions in old API */ |
6369 | if (flag > NL80211_STA_FLAG_MAX_OLD_API) |
6370 | return -EINVAL; |
6371 | } |
6372 | } |
6373 | |
6374 | return 0; |
6375 | } |
6376 | |
6377 | bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, int attr) |
6378 | { |
6379 | struct nlattr *rate; |
6380 | u32 bitrate; |
6381 | u16 bitrate_compat; |
6382 | enum nl80211_rate_info rate_flg; |
6383 | |
6384 | rate = nla_nest_start_noflag(skb: msg, attrtype: attr); |
6385 | if (!rate) |
6386 | return false; |
6387 | |
6388 | /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ |
6389 | bitrate = cfg80211_calculate_bitrate(rate: info); |
6390 | /* report 16-bit bitrate only if we can */ |
6391 | bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0; |
6392 | if (bitrate > 0 && |
6393 | nla_put_u32(skb: msg, attrtype: NL80211_RATE_INFO_BITRATE32, value: bitrate)) |
6394 | return false; |
6395 | if (bitrate_compat > 0 && |
6396 | nla_put_u16(skb: msg, attrtype: NL80211_RATE_INFO_BITRATE, value: bitrate_compat)) |
6397 | return false; |
6398 | |
6399 | switch (info->bw) { |
6400 | case RATE_INFO_BW_1: |
6401 | rate_flg = NL80211_RATE_INFO_1_MHZ_WIDTH; |
6402 | break; |
6403 | case RATE_INFO_BW_2: |
6404 | rate_flg = NL80211_RATE_INFO_2_MHZ_WIDTH; |
6405 | break; |
6406 | case RATE_INFO_BW_4: |
6407 | rate_flg = NL80211_RATE_INFO_4_MHZ_WIDTH; |
6408 | break; |
6409 | case RATE_INFO_BW_5: |
6410 | rate_flg = NL80211_RATE_INFO_5_MHZ_WIDTH; |
6411 | break; |
6412 | case RATE_INFO_BW_8: |
6413 | rate_flg = NL80211_RATE_INFO_8_MHZ_WIDTH; |
6414 | break; |
6415 | case RATE_INFO_BW_10: |
6416 | rate_flg = NL80211_RATE_INFO_10_MHZ_WIDTH; |
6417 | break; |
6418 | case RATE_INFO_BW_16: |
6419 | rate_flg = NL80211_RATE_INFO_16_MHZ_WIDTH; |
6420 | break; |
6421 | default: |
6422 | WARN_ON(1); |
6423 | fallthrough; |
6424 | case RATE_INFO_BW_20: |
6425 | rate_flg = 0; |
6426 | break; |
6427 | case RATE_INFO_BW_40: |
6428 | rate_flg = NL80211_RATE_INFO_40_MHZ_WIDTH; |
6429 | break; |
6430 | case RATE_INFO_BW_80: |
6431 | rate_flg = NL80211_RATE_INFO_80_MHZ_WIDTH; |
6432 | break; |
6433 | case RATE_INFO_BW_160: |
6434 | rate_flg = NL80211_RATE_INFO_160_MHZ_WIDTH; |
6435 | break; |
6436 | case RATE_INFO_BW_HE_RU: |
6437 | rate_flg = 0; |
6438 | WARN_ON(!(info->flags & RATE_INFO_FLAGS_HE_MCS)); |
6439 | break; |
6440 | case RATE_INFO_BW_320: |
6441 | rate_flg = NL80211_RATE_INFO_320_MHZ_WIDTH; |
6442 | break; |
6443 | case RATE_INFO_BW_EHT_RU: |
6444 | rate_flg = 0; |
6445 | WARN_ON(!(info->flags & RATE_INFO_FLAGS_EHT_MCS)); |
6446 | break; |
6447 | } |
6448 | |
6449 | if (rate_flg && nla_put_flag(skb: msg, attrtype: rate_flg)) |
6450 | return false; |
6451 | |
6452 | if (info->flags & RATE_INFO_FLAGS_MCS) { |
6453 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_MCS, value: info->mcs)) |
6454 | return false; |
6455 | if (info->flags & RATE_INFO_FLAGS_SHORT_GI && |
6456 | nla_put_flag(skb: msg, attrtype: NL80211_RATE_INFO_SHORT_GI)) |
6457 | return false; |
6458 | } else if (info->flags & RATE_INFO_FLAGS_VHT_MCS) { |
6459 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_VHT_MCS, value: info->mcs)) |
6460 | return false; |
6461 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_VHT_NSS, value: info->nss)) |
6462 | return false; |
6463 | if (info->flags & RATE_INFO_FLAGS_SHORT_GI && |
6464 | nla_put_flag(skb: msg, attrtype: NL80211_RATE_INFO_SHORT_GI)) |
6465 | return false; |
6466 | } else if (info->flags & RATE_INFO_FLAGS_HE_MCS) { |
6467 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_HE_MCS, value: info->mcs)) |
6468 | return false; |
6469 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_HE_NSS, value: info->nss)) |
6470 | return false; |
6471 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_HE_GI, value: info->he_gi)) |
6472 | return false; |
6473 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_HE_DCM, value: info->he_dcm)) |
6474 | return false; |
6475 | if (info->bw == RATE_INFO_BW_HE_RU && |
6476 | nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_HE_RU_ALLOC, |
6477 | value: info->he_ru_alloc)) |
6478 | return false; |
6479 | } else if (info->flags & RATE_INFO_FLAGS_S1G_MCS) { |
6480 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_S1G_MCS, value: info->mcs)) |
6481 | return false; |
6482 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_S1G_NSS, value: info->nss)) |
6483 | return false; |
6484 | if (info->flags & RATE_INFO_FLAGS_SHORT_GI && |
6485 | nla_put_flag(skb: msg, attrtype: NL80211_RATE_INFO_SHORT_GI)) |
6486 | return false; |
6487 | } else if (info->flags & RATE_INFO_FLAGS_EHT_MCS) { |
6488 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_EHT_MCS, value: info->mcs)) |
6489 | return false; |
6490 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_EHT_NSS, value: info->nss)) |
6491 | return false; |
6492 | if (nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_EHT_GI, value: info->eht_gi)) |
6493 | return false; |
6494 | if (info->bw == RATE_INFO_BW_EHT_RU && |
6495 | nla_put_u8(skb: msg, attrtype: NL80211_RATE_INFO_EHT_RU_ALLOC, |
6496 | value: info->eht_ru_alloc)) |
6497 | return false; |
6498 | } |
6499 | |
6500 | nla_nest_end(skb: msg, start: rate); |
6501 | return true; |
6502 | } |
6503 | |
6504 | static bool nl80211_put_signal(struct sk_buff *msg, u8 mask, s8 *signal, |
6505 | int id) |
6506 | { |
6507 | void *attr; |
6508 | int i = 0; |
6509 | |
6510 | if (!mask) |
6511 | return true; |
6512 | |
6513 | attr = nla_nest_start_noflag(skb: msg, attrtype: id); |
6514 | if (!attr) |
6515 | return false; |
6516 | |
6517 | for (i = 0; i < IEEE80211_MAX_CHAINS; i++) { |
6518 | if (!(mask & BIT(i))) |
6519 | continue; |
6520 | |
6521 | if (nla_put_u8(skb: msg, attrtype: i, value: signal[i])) |
6522 | return false; |
6523 | } |
6524 | |
6525 | nla_nest_end(skb: msg, start: attr); |
6526 | |
6527 | return true; |
6528 | } |
6529 | |
6530 | static int nl80211_send_station(struct sk_buff *msg, u32 cmd, u32 portid, |
6531 | u32 seq, int flags, |
6532 | struct cfg80211_registered_device *rdev, |
6533 | struct net_device *dev, |
6534 | const u8 *mac_addr, struct station_info *sinfo) |
6535 | { |
6536 | void *hdr; |
6537 | struct nlattr *sinfoattr, *bss_param; |
6538 | |
6539 | hdr = nl80211hdr_put(skb: msg, portid, seq, flags, cmd); |
6540 | if (!hdr) { |
6541 | cfg80211_sinfo_release_content(sinfo); |
6542 | return -1; |
6543 | } |
6544 | |
6545 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || |
6546 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: mac_addr) || |
6547 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_GENERATION, value: sinfo->generation)) |
6548 | goto nla_put_failure; |
6549 | |
6550 | sinfoattr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_STA_INFO); |
6551 | if (!sinfoattr) |
6552 | goto nla_put_failure; |
6553 | |
6554 | #define PUT_SINFO(attr, memb, type) do { \ |
6555 | BUILD_BUG_ON(sizeof(type) == sizeof(u64)); \ |
6556 | if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_ ## attr) && \ |
6557 | nla_put_ ## type(msg, NL80211_STA_INFO_ ## attr, \ |
6558 | sinfo->memb)) \ |
6559 | goto nla_put_failure; \ |
6560 | } while (0) |
6561 | #define PUT_SINFO_U64(attr, memb) do { \ |
6562 | if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_ ## attr) && \ |
6563 | nla_put_u64_64bit(msg, NL80211_STA_INFO_ ## attr, \ |
6564 | sinfo->memb, NL80211_STA_INFO_PAD)) \ |
6565 | goto nla_put_failure; \ |
6566 | } while (0) |
6567 | |
6568 | PUT_SINFO(CONNECTED_TIME, connected_time, u32); |
6569 | PUT_SINFO(INACTIVE_TIME, inactive_time, u32); |
6570 | PUT_SINFO_U64(ASSOC_AT_BOOTTIME, assoc_at); |
6571 | |
6572 | if (sinfo->filled & (BIT_ULL(NL80211_STA_INFO_RX_BYTES) | |
6573 | BIT_ULL(NL80211_STA_INFO_RX_BYTES64)) && |
6574 | nla_put_u32(skb: msg, attrtype: NL80211_STA_INFO_RX_BYTES, |
6575 | value: (u32)sinfo->rx_bytes)) |
6576 | goto nla_put_failure; |
6577 | |
6578 | if (sinfo->filled & (BIT_ULL(NL80211_STA_INFO_TX_BYTES) | |
6579 | BIT_ULL(NL80211_STA_INFO_TX_BYTES64)) && |
6580 | nla_put_u32(skb: msg, attrtype: NL80211_STA_INFO_TX_BYTES, |
6581 | value: (u32)sinfo->tx_bytes)) |
6582 | goto nla_put_failure; |
6583 | |
6584 | PUT_SINFO_U64(RX_BYTES64, rx_bytes); |
6585 | PUT_SINFO_U64(TX_BYTES64, tx_bytes); |
6586 | PUT_SINFO(LLID, llid, u16); |
6587 | PUT_SINFO(PLID, plid, u16); |
6588 | PUT_SINFO(PLINK_STATE, plink_state, u8); |
6589 | PUT_SINFO_U64(RX_DURATION, rx_duration); |
6590 | PUT_SINFO_U64(TX_DURATION, tx_duration); |
6591 | |
6592 | if (wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
6593 | ftidx: NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) |
6594 | PUT_SINFO(AIRTIME_WEIGHT, airtime_weight, u16); |
6595 | |
6596 | switch (rdev->wiphy.signal_type) { |
6597 | case CFG80211_SIGNAL_TYPE_MBM: |
6598 | PUT_SINFO(SIGNAL, signal, u8); |
6599 | PUT_SINFO(SIGNAL_AVG, signal_avg, u8); |
6600 | break; |
6601 | default: |
6602 | break; |
6603 | } |
6604 | if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) { |
6605 | if (!nl80211_put_signal(msg, mask: sinfo->chains, |
6606 | signal: sinfo->chain_signal, |
6607 | id: NL80211_STA_INFO_CHAIN_SIGNAL)) |
6608 | goto nla_put_failure; |
6609 | } |
6610 | if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) { |
6611 | if (!nl80211_put_signal(msg, mask: sinfo->chains, |
6612 | signal: sinfo->chain_signal_avg, |
6613 | id: NL80211_STA_INFO_CHAIN_SIGNAL_AVG)) |
6614 | goto nla_put_failure; |
6615 | } |
6616 | if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_TX_BITRATE)) { |
6617 | if (!nl80211_put_sta_rate(msg, info: &sinfo->txrate, |
6618 | attr: NL80211_STA_INFO_TX_BITRATE)) |
6619 | goto nla_put_failure; |
6620 | } |
6621 | if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_RX_BITRATE)) { |
6622 | if (!nl80211_put_sta_rate(msg, info: &sinfo->rxrate, |
6623 | attr: NL80211_STA_INFO_RX_BITRATE)) |
6624 | goto nla_put_failure; |
6625 | } |
6626 | |
6627 | PUT_SINFO(RX_PACKETS, rx_packets, u32); |
6628 | PUT_SINFO(TX_PACKETS, tx_packets, u32); |
6629 | PUT_SINFO(TX_RETRIES, tx_retries, u32); |
6630 | PUT_SINFO(TX_FAILED, tx_failed, u32); |
6631 | PUT_SINFO(EXPECTED_THROUGHPUT, expected_throughput, u32); |
6632 | PUT_SINFO(AIRTIME_LINK_METRIC, airtime_link_metric, u32); |
6633 | PUT_SINFO(BEACON_LOSS, beacon_loss_count, u32); |
6634 | PUT_SINFO(LOCAL_PM, local_pm, u32); |
6635 | PUT_SINFO(PEER_PM, peer_pm, u32); |
6636 | PUT_SINFO(NONPEER_PM, nonpeer_pm, u32); |
6637 | PUT_SINFO(CONNECTED_TO_GATE, connected_to_gate, u8); |
6638 | PUT_SINFO(CONNECTED_TO_AS, connected_to_as, u8); |
6639 | |
6640 | if (sinfo->filled & BIT_ULL(NL80211_STA_INFO_BSS_PARAM)) { |
6641 | bss_param = nla_nest_start_noflag(skb: msg, |
6642 | attrtype: NL80211_STA_INFO_BSS_PARAM); |
6643 | if (!bss_param) |
6644 | goto nla_put_failure; |
6645 | |
6646 | if (((sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT) && |
6647 | nla_put_flag(skb: msg, attrtype: NL80211_STA_BSS_PARAM_CTS_PROT)) || |
6648 | ((sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE) && |
6649 | nla_put_flag(skb: msg, attrtype: NL80211_STA_BSS_PARAM_SHORT_PREAMBLE)) || |
6650 | ((sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME) && |
6651 | nla_put_flag(skb: msg, attrtype: NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME)) || |
6652 | nla_put_u8(skb: msg, attrtype: NL80211_STA_BSS_PARAM_DTIM_PERIOD, |
6653 | value: sinfo->bss_param.dtim_period) || |
6654 | nla_put_u16(skb: msg, attrtype: NL80211_STA_BSS_PARAM_BEACON_INTERVAL, |
6655 | value: sinfo->bss_param.beacon_interval)) |
6656 | goto nla_put_failure; |
6657 | |
6658 | nla_nest_end(skb: msg, start: bss_param); |
6659 | } |
6660 | if ((sinfo->filled & BIT_ULL(NL80211_STA_INFO_STA_FLAGS)) && |
6661 | nla_put(skb: msg, attrtype: NL80211_STA_INFO_STA_FLAGS, |
6662 | attrlen: sizeof(struct nl80211_sta_flag_update), |
6663 | data: &sinfo->sta_flags)) |
6664 | goto nla_put_failure; |
6665 | |
6666 | PUT_SINFO_U64(T_OFFSET, t_offset); |
6667 | PUT_SINFO_U64(RX_DROP_MISC, rx_dropped_misc); |
6668 | PUT_SINFO_U64(BEACON_RX, rx_beacon); |
6669 | PUT_SINFO(BEACON_SIGNAL_AVG, rx_beacon_signal_avg, u8); |
6670 | PUT_SINFO(RX_MPDUS, rx_mpdu_count, u32); |
6671 | PUT_SINFO(FCS_ERROR_COUNT, fcs_err_count, u32); |
6672 | if (wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
6673 | ftidx: NL80211_EXT_FEATURE_ACK_SIGNAL_SUPPORT)) { |
6674 | PUT_SINFO(ACK_SIGNAL, ack_signal, u8); |
6675 | PUT_SINFO(ACK_SIGNAL_AVG, avg_ack_signal, s8); |
6676 | } |
6677 | |
6678 | #undef PUT_SINFO |
6679 | #undef PUT_SINFO_U64 |
6680 | |
6681 | if (sinfo->pertid) { |
6682 | struct nlattr *tidsattr; |
6683 | int tid; |
6684 | |
6685 | tidsattr = nla_nest_start_noflag(skb: msg, |
6686 | attrtype: NL80211_STA_INFO_TID_STATS); |
6687 | if (!tidsattr) |
6688 | goto nla_put_failure; |
6689 | |
6690 | for (tid = 0; tid < IEEE80211_NUM_TIDS + 1; tid++) { |
6691 | struct cfg80211_tid_stats *tidstats; |
6692 | struct nlattr *tidattr; |
6693 | |
6694 | tidstats = &sinfo->pertid[tid]; |
6695 | |
6696 | if (!tidstats->filled) |
6697 | continue; |
6698 | |
6699 | tidattr = nla_nest_start_noflag(skb: msg, attrtype: tid + 1); |
6700 | if (!tidattr) |
6701 | goto nla_put_failure; |
6702 | |
6703 | #define PUT_TIDVAL_U64(attr, memb) do { \ |
6704 | if (tidstats->filled & BIT(NL80211_TID_STATS_ ## attr) && \ |
6705 | nla_put_u64_64bit(msg, NL80211_TID_STATS_ ## attr, \ |
6706 | tidstats->memb, NL80211_TID_STATS_PAD)) \ |
6707 | goto nla_put_failure; \ |
6708 | } while (0) |
6709 | |
6710 | PUT_TIDVAL_U64(RX_MSDU, rx_msdu); |
6711 | PUT_TIDVAL_U64(TX_MSDU, tx_msdu); |
6712 | PUT_TIDVAL_U64(TX_MSDU_RETRIES, tx_msdu_retries); |
6713 | PUT_TIDVAL_U64(TX_MSDU_FAILED, tx_msdu_failed); |
6714 | |
6715 | #undef PUT_TIDVAL_U64 |
6716 | if ((tidstats->filled & |
6717 | BIT(NL80211_TID_STATS_TXQ_STATS)) && |
6718 | !nl80211_put_txq_stats(msg, txqstats: &tidstats->txq_stats, |
6719 | attrtype: NL80211_TID_STATS_TXQ_STATS)) |
6720 | goto nla_put_failure; |
6721 | |
6722 | nla_nest_end(skb: msg, start: tidattr); |
6723 | } |
6724 | |
6725 | nla_nest_end(skb: msg, start: tidsattr); |
6726 | } |
6727 | |
6728 | nla_nest_end(skb: msg, start: sinfoattr); |
6729 | |
6730 | if (sinfo->assoc_req_ies_len && |
6731 | nla_put(skb: msg, NL80211_ATTR_IE, attrlen: sinfo->assoc_req_ies_len, |
6732 | data: sinfo->assoc_req_ies)) |
6733 | goto nla_put_failure; |
6734 | |
6735 | if (sinfo->assoc_resp_ies_len && |
6736 | nla_put(skb: msg, attrtype: NL80211_ATTR_RESP_IE, attrlen: sinfo->assoc_resp_ies_len, |
6737 | data: sinfo->assoc_resp_ies)) |
6738 | goto nla_put_failure; |
6739 | |
6740 | if (sinfo->mlo_params_valid) { |
6741 | if (nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, |
6742 | value: sinfo->assoc_link_id)) |
6743 | goto nla_put_failure; |
6744 | |
6745 | if (!is_zero_ether_addr(addr: sinfo->mld_addr) && |
6746 | nla_put(skb: msg, attrtype: NL80211_ATTR_MLD_ADDR, ETH_ALEN, |
6747 | data: sinfo->mld_addr)) |
6748 | goto nla_put_failure; |
6749 | } |
6750 | |
6751 | cfg80211_sinfo_release_content(sinfo); |
6752 | genlmsg_end(skb: msg, hdr); |
6753 | return 0; |
6754 | |
6755 | nla_put_failure: |
6756 | cfg80211_sinfo_release_content(sinfo); |
6757 | genlmsg_cancel(skb: msg, hdr); |
6758 | return -EMSGSIZE; |
6759 | } |
6760 | |
6761 | static int nl80211_dump_station(struct sk_buff *skb, |
6762 | struct netlink_callback *cb) |
6763 | { |
6764 | struct station_info sinfo; |
6765 | struct cfg80211_registered_device *rdev; |
6766 | struct wireless_dev *wdev; |
6767 | u8 mac_addr[ETH_ALEN]; |
6768 | int sta_idx = cb->args[2]; |
6769 | int err; |
6770 | |
6771 | err = nl80211_prepare_wdev_dump(cb, rdev: &rdev, wdev: &wdev, NULL); |
6772 | if (err) |
6773 | return err; |
6774 | /* nl80211_prepare_wdev_dump acquired it in the successful case */ |
6775 | __acquire(&rdev->wiphy.mtx); |
6776 | |
6777 | if (!wdev->netdev) { |
6778 | err = -EINVAL; |
6779 | goto out_err; |
6780 | } |
6781 | |
6782 | if (!rdev->ops->dump_station) { |
6783 | err = -EOPNOTSUPP; |
6784 | goto out_err; |
6785 | } |
6786 | |
6787 | while (1) { |
6788 | memset(&sinfo, 0, sizeof(sinfo)); |
6789 | err = rdev_dump_station(rdev, dev: wdev->netdev, idx: sta_idx, |
6790 | mac: mac_addr, sinfo: &sinfo); |
6791 | if (err == -ENOENT) |
6792 | break; |
6793 | if (err) |
6794 | goto out_err; |
6795 | |
6796 | if (nl80211_send_station(msg: skb, cmd: NL80211_CMD_NEW_STATION, |
6797 | NETLINK_CB(cb->skb).portid, |
6798 | seq: cb->nlh->nlmsg_seq, NLM_F_MULTI, |
6799 | rdev, dev: wdev->netdev, mac_addr, |
6800 | sinfo: &sinfo) < 0) |
6801 | goto out; |
6802 | |
6803 | sta_idx++; |
6804 | } |
6805 | |
6806 | out: |
6807 | cb->args[2] = sta_idx; |
6808 | err = skb->len; |
6809 | out_err: |
6810 | wiphy_unlock(wiphy: &rdev->wiphy); |
6811 | |
6812 | return err; |
6813 | } |
6814 | |
6815 | static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) |
6816 | { |
6817 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
6818 | struct net_device *dev = info->user_ptr[1]; |
6819 | struct station_info sinfo; |
6820 | struct sk_buff *msg; |
6821 | u8 *mac_addr = NULL; |
6822 | int err; |
6823 | |
6824 | memset(&sinfo, 0, sizeof(sinfo)); |
6825 | |
6826 | if (!info->attrs[NL80211_ATTR_MAC]) |
6827 | return -EINVAL; |
6828 | |
6829 | mac_addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
6830 | |
6831 | if (!rdev->ops->get_station) |
6832 | return -EOPNOTSUPP; |
6833 | |
6834 | err = rdev_get_station(rdev, dev, mac: mac_addr, sinfo: &sinfo); |
6835 | if (err) |
6836 | return err; |
6837 | |
6838 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
6839 | if (!msg) { |
6840 | cfg80211_sinfo_release_content(sinfo: &sinfo); |
6841 | return -ENOMEM; |
6842 | } |
6843 | |
6844 | if (nl80211_send_station(msg, cmd: NL80211_CMD_NEW_STATION, |
6845 | portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
6846 | rdev, dev, mac_addr, sinfo: &sinfo) < 0) { |
6847 | nlmsg_free(skb: msg); |
6848 | return -ENOBUFS; |
6849 | } |
6850 | |
6851 | return genlmsg_reply(skb: msg, info); |
6852 | } |
6853 | |
6854 | int cfg80211_check_station_change(struct wiphy *wiphy, |
6855 | struct station_parameters *params, |
6856 | enum cfg80211_station_type statype) |
6857 | { |
6858 | if (params->listen_interval != -1 && |
6859 | statype != CFG80211_STA_AP_CLIENT_UNASSOC) |
6860 | return -EINVAL; |
6861 | |
6862 | if (params->support_p2p_ps != -1 && |
6863 | statype != CFG80211_STA_AP_CLIENT_UNASSOC) |
6864 | return -EINVAL; |
6865 | |
6866 | if (params->aid && |
6867 | !(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) && |
6868 | statype != CFG80211_STA_AP_CLIENT_UNASSOC) |
6869 | return -EINVAL; |
6870 | |
6871 | /* When you run into this, adjust the code below for the new flag */ |
6872 | BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 8); |
6873 | |
6874 | switch (statype) { |
6875 | case CFG80211_STA_MESH_PEER_KERNEL: |
6876 | case CFG80211_STA_MESH_PEER_USER: |
6877 | /* |
6878 | * No ignoring the TDLS flag here -- the userspace mesh |
6879 | * code doesn't have the bug of including TDLS in the |
6880 | * mask everywhere. |
6881 | */ |
6882 | if (params->sta_flags_mask & |
6883 | ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) | |
6884 | BIT(NL80211_STA_FLAG_MFP) | |
6885 | BIT(NL80211_STA_FLAG_AUTHORIZED))) |
6886 | return -EINVAL; |
6887 | break; |
6888 | case CFG80211_STA_TDLS_PEER_SETUP: |
6889 | case CFG80211_STA_TDLS_PEER_ACTIVE: |
6890 | if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) |
6891 | return -EINVAL; |
6892 | /* ignore since it can't change */ |
6893 | params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); |
6894 | break; |
6895 | default: |
6896 | /* disallow mesh-specific things */ |
6897 | if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION) |
6898 | return -EINVAL; |
6899 | if (params->local_pm) |
6900 | return -EINVAL; |
6901 | if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) |
6902 | return -EINVAL; |
6903 | } |
6904 | |
6905 | if (statype != CFG80211_STA_TDLS_PEER_SETUP && |
6906 | statype != CFG80211_STA_TDLS_PEER_ACTIVE) { |
6907 | /* TDLS can't be set, ... */ |
6908 | if (params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) |
6909 | return -EINVAL; |
6910 | /* |
6911 | * ... but don't bother the driver with it. This works around |
6912 | * a hostapd/wpa_supplicant issue -- it always includes the |
6913 | * TLDS_PEER flag in the mask even for AP mode. |
6914 | */ |
6915 | params->sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); |
6916 | } |
6917 | |
6918 | if (statype != CFG80211_STA_TDLS_PEER_SETUP && |
6919 | statype != CFG80211_STA_AP_CLIENT_UNASSOC) { |
6920 | /* reject other things that can't change */ |
6921 | if (params->sta_modify_mask & STATION_PARAM_APPLY_UAPSD) |
6922 | return -EINVAL; |
6923 | if (params->sta_modify_mask & STATION_PARAM_APPLY_CAPABILITY) |
6924 | return -EINVAL; |
6925 | if (params->link_sta_params.supported_rates) |
6926 | return -EINVAL; |
6927 | if (params->ext_capab || params->link_sta_params.ht_capa || |
6928 | params->link_sta_params.vht_capa || |
6929 | params->link_sta_params.he_capa || |
6930 | params->link_sta_params.eht_capa) |
6931 | return -EINVAL; |
6932 | if (params->sta_flags_mask & BIT(NL80211_STA_FLAG_SPP_AMSDU)) |
6933 | return -EINVAL; |
6934 | } |
6935 | |
6936 | if (statype != CFG80211_STA_AP_CLIENT && |
6937 | statype != CFG80211_STA_AP_CLIENT_UNASSOC) { |
6938 | if (params->vlan) |
6939 | return -EINVAL; |
6940 | } |
6941 | |
6942 | switch (statype) { |
6943 | case CFG80211_STA_AP_MLME_CLIENT: |
6944 | /* Use this only for authorizing/unauthorizing a station */ |
6945 | if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))) |
6946 | return -EOPNOTSUPP; |
6947 | break; |
6948 | case CFG80211_STA_AP_CLIENT: |
6949 | case CFG80211_STA_AP_CLIENT_UNASSOC: |
6950 | /* accept only the listed bits */ |
6951 | if (params->sta_flags_mask & |
6952 | ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | |
6953 | BIT(NL80211_STA_FLAG_AUTHENTICATED) | |
6954 | BIT(NL80211_STA_FLAG_ASSOCIATED) | |
6955 | BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) | |
6956 | BIT(NL80211_STA_FLAG_WME) | |
6957 | BIT(NL80211_STA_FLAG_MFP) | |
6958 | BIT(NL80211_STA_FLAG_SPP_AMSDU))) |
6959 | return -EINVAL; |
6960 | |
6961 | /* but authenticated/associated only if driver handles it */ |
6962 | if (!(wiphy->features & NL80211_FEATURE_FULL_AP_CLIENT_STATE) && |
6963 | params->sta_flags_mask & |
6964 | (BIT(NL80211_STA_FLAG_AUTHENTICATED) | |
6965 | BIT(NL80211_STA_FLAG_ASSOCIATED))) |
6966 | return -EINVAL; |
6967 | break; |
6968 | case CFG80211_STA_IBSS: |
6969 | case CFG80211_STA_AP_STA: |
6970 | /* reject any changes other than AUTHORIZED */ |
6971 | if (params->sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED)) |
6972 | return -EINVAL; |
6973 | break; |
6974 | case CFG80211_STA_TDLS_PEER_SETUP: |
6975 | /* reject any changes other than AUTHORIZED or WME */ |
6976 | if (params->sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) | |
6977 | BIT(NL80211_STA_FLAG_WME))) |
6978 | return -EINVAL; |
6979 | /* force (at least) rates when authorizing */ |
6980 | if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED) && |
6981 | !params->link_sta_params.supported_rates) |
6982 | return -EINVAL; |
6983 | break; |
6984 | case CFG80211_STA_TDLS_PEER_ACTIVE: |
6985 | /* reject any changes */ |
6986 | return -EINVAL; |
6987 | case CFG80211_STA_MESH_PEER_KERNEL: |
6988 | if (params->sta_modify_mask & STATION_PARAM_APPLY_PLINK_STATE) |
6989 | return -EINVAL; |
6990 | break; |
6991 | case CFG80211_STA_MESH_PEER_USER: |
6992 | if (params->plink_action != NL80211_PLINK_ACTION_NO_ACTION && |
6993 | params->plink_action != NL80211_PLINK_ACTION_BLOCK) |
6994 | return -EINVAL; |
6995 | break; |
6996 | } |
6997 | |
6998 | /* |
6999 | * Older kernel versions ignored this attribute entirely, so don't |
7000 | * reject attempts to update it but mark it as unused instead so the |
7001 | * driver won't look at the data. |
7002 | */ |
7003 | if (statype != CFG80211_STA_AP_CLIENT_UNASSOC && |
7004 | statype != CFG80211_STA_TDLS_PEER_SETUP) |
7005 | params->link_sta_params.opmode_notif_used = false; |
7006 | |
7007 | return 0; |
7008 | } |
7009 | EXPORT_SYMBOL(cfg80211_check_station_change); |
7010 | |
7011 | /* |
7012 | * Get vlan interface making sure it is running and on the right wiphy. |
7013 | */ |
7014 | static struct net_device *get_vlan(struct genl_info *info, |
7015 | struct cfg80211_registered_device *rdev) |
7016 | { |
7017 | struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN]; |
7018 | struct net_device *v; |
7019 | int ret; |
7020 | |
7021 | if (!vlanattr) |
7022 | return NULL; |
7023 | |
7024 | v = dev_get_by_index(net: genl_info_net(info), ifindex: nla_get_u32(nla: vlanattr)); |
7025 | if (!v) |
7026 | return ERR_PTR(error: -ENODEV); |
7027 | |
7028 | if (!v->ieee80211_ptr || v->ieee80211_ptr->wiphy != &rdev->wiphy) { |
7029 | ret = -EINVAL; |
7030 | goto error; |
7031 | } |
7032 | |
7033 | if (v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && |
7034 | v->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && |
7035 | v->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) { |
7036 | ret = -EINVAL; |
7037 | goto error; |
7038 | } |
7039 | |
7040 | if (!netif_running(dev: v)) { |
7041 | ret = -ENETDOWN; |
7042 | goto error; |
7043 | } |
7044 | |
7045 | return v; |
7046 | error: |
7047 | dev_put(dev: v); |
7048 | return ERR_PTR(error: ret); |
7049 | } |
7050 | |
7051 | static int nl80211_parse_sta_wme(struct genl_info *info, |
7052 | struct station_parameters *params) |
7053 | { |
7054 | struct nlattr *tb[NL80211_STA_WME_MAX + 1]; |
7055 | struct nlattr *nla; |
7056 | int err; |
7057 | |
7058 | /* parse WME attributes if present */ |
7059 | if (!info->attrs[NL80211_ATTR_STA_WME]) |
7060 | return 0; |
7061 | |
7062 | nla = info->attrs[NL80211_ATTR_STA_WME]; |
7063 | err = nla_parse_nested_deprecated(tb, maxtype: NL80211_STA_WME_MAX, nla, |
7064 | policy: nl80211_sta_wme_policy, |
7065 | extack: info->extack); |
7066 | if (err) |
7067 | return err; |
7068 | |
7069 | if (tb[NL80211_STA_WME_UAPSD_QUEUES]) |
7070 | params->uapsd_queues = nla_get_u8( |
7071 | nla: tb[NL80211_STA_WME_UAPSD_QUEUES]); |
7072 | if (params->uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) |
7073 | return -EINVAL; |
7074 | |
7075 | if (tb[NL80211_STA_WME_MAX_SP]) |
7076 | params->max_sp = nla_get_u8(nla: tb[NL80211_STA_WME_MAX_SP]); |
7077 | |
7078 | if (params->max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) |
7079 | return -EINVAL; |
7080 | |
7081 | params->sta_modify_mask |= STATION_PARAM_APPLY_UAPSD; |
7082 | |
7083 | return 0; |
7084 | } |
7085 | |
7086 | static int nl80211_parse_sta_channel_info(struct genl_info *info, |
7087 | struct station_parameters *params) |
7088 | { |
7089 | if (info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]) { |
7090 | params->supported_channels = |
7091 | nla_data(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]); |
7092 | params->supported_channels_len = |
7093 | nla_len(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]); |
7094 | /* |
7095 | * Need to include at least one (first channel, number of |
7096 | * channels) tuple for each subband (checked in policy), |
7097 | * and must have proper tuples for the rest of the data as well. |
7098 | */ |
7099 | if (params->supported_channels_len % 2) |
7100 | return -EINVAL; |
7101 | } |
7102 | |
7103 | if (info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]) { |
7104 | params->supported_oper_classes = |
7105 | nla_data(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]); |
7106 | params->supported_oper_classes_len = |
7107 | nla_len(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]); |
7108 | } |
7109 | return 0; |
7110 | } |
7111 | |
7112 | static int nl80211_set_station_tdls(struct genl_info *info, |
7113 | struct station_parameters *params) |
7114 | { |
7115 | int err; |
7116 | /* Dummy STA entry gets updated once the peer capabilities are known */ |
7117 | if (info->attrs[NL80211_ATTR_PEER_AID]) |
7118 | params->aid = nla_get_u16(nla: info->attrs[NL80211_ATTR_PEER_AID]); |
7119 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) |
7120 | params->link_sta_params.ht_capa = |
7121 | nla_data(nla: info->attrs[NL80211_ATTR_HT_CAPABILITY]); |
7122 | if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) |
7123 | params->link_sta_params.vht_capa = |
7124 | nla_data(nla: info->attrs[NL80211_ATTR_VHT_CAPABILITY]); |
7125 | if (info->attrs[NL80211_ATTR_HE_CAPABILITY]) { |
7126 | params->link_sta_params.he_capa = |
7127 | nla_data(nla: info->attrs[NL80211_ATTR_HE_CAPABILITY]); |
7128 | params->link_sta_params.he_capa_len = |
7129 | nla_len(nla: info->attrs[NL80211_ATTR_HE_CAPABILITY]); |
7130 | |
7131 | if (info->attrs[NL80211_ATTR_EHT_CAPABILITY]) { |
7132 | params->link_sta_params.eht_capa = |
7133 | nla_data(nla: info->attrs[NL80211_ATTR_EHT_CAPABILITY]); |
7134 | params->link_sta_params.eht_capa_len = |
7135 | nla_len(nla: info->attrs[NL80211_ATTR_EHT_CAPABILITY]); |
7136 | |
7137 | if (!ieee80211_eht_capa_size_ok(he_capa: (const u8 *)params->link_sta_params.he_capa, |
7138 | data: (const u8 *)params->link_sta_params.eht_capa, |
7139 | len: params->link_sta_params.eht_capa_len, |
7140 | from_ap: false)) |
7141 | return -EINVAL; |
7142 | } |
7143 | } |
7144 | |
7145 | err = nl80211_parse_sta_channel_info(info, params); |
7146 | if (err) |
7147 | return err; |
7148 | |
7149 | return nl80211_parse_sta_wme(info, params); |
7150 | } |
7151 | |
7152 | static int nl80211_parse_sta_txpower_setting(struct genl_info *info, |
7153 | struct sta_txpwr *txpwr, |
7154 | bool *txpwr_set) |
7155 | { |
7156 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
7157 | int idx; |
7158 | |
7159 | if (info->attrs[NL80211_ATTR_STA_TX_POWER_SETTING]) { |
7160 | if (!rdev->ops->set_tx_power || |
7161 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
7162 | ftidx: NL80211_EXT_FEATURE_STA_TX_PWR)) |
7163 | return -EOPNOTSUPP; |
7164 | |
7165 | idx = NL80211_ATTR_STA_TX_POWER_SETTING; |
7166 | txpwr->type = nla_get_u8(nla: info->attrs[idx]); |
7167 | |
7168 | if (txpwr->type == NL80211_TX_POWER_LIMITED) { |
7169 | idx = NL80211_ATTR_STA_TX_POWER; |
7170 | |
7171 | if (info->attrs[idx]) |
7172 | txpwr->power = nla_get_s16(nla: info->attrs[idx]); |
7173 | else |
7174 | return -EINVAL; |
7175 | } |
7176 | |
7177 | *txpwr_set = true; |
7178 | } else { |
7179 | *txpwr_set = false; |
7180 | } |
7181 | |
7182 | return 0; |
7183 | } |
7184 | |
7185 | static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) |
7186 | { |
7187 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
7188 | struct net_device *dev = info->user_ptr[1]; |
7189 | struct station_parameters params; |
7190 | u8 *mac_addr; |
7191 | int err; |
7192 | |
7193 | memset(¶ms, 0, sizeof(params)); |
7194 | |
7195 | if (!rdev->ops->change_station) |
7196 | return -EOPNOTSUPP; |
7197 | |
7198 | /* |
7199 | * AID and listen_interval properties can be set only for unassociated |
7200 | * station. Include these parameters here and will check them in |
7201 | * cfg80211_check_station_change(). |
7202 | */ |
7203 | if (info->attrs[NL80211_ATTR_STA_AID]) |
7204 | params.aid = nla_get_u16(nla: info->attrs[NL80211_ATTR_STA_AID]); |
7205 | |
7206 | if (info->attrs[NL80211_ATTR_VLAN_ID]) |
7207 | params.vlan_id = nla_get_u16(nla: info->attrs[NL80211_ATTR_VLAN_ID]); |
7208 | |
7209 | if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) |
7210 | params.listen_interval = |
7211 | nla_get_u16(nla: info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); |
7212 | else |
7213 | params.listen_interval = -1; |
7214 | |
7215 | if (info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]) |
7216 | params.support_p2p_ps = |
7217 | nla_get_u8(nla: info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]); |
7218 | else |
7219 | params.support_p2p_ps = -1; |
7220 | |
7221 | if (!info->attrs[NL80211_ATTR_MAC]) |
7222 | return -EINVAL; |
7223 | |
7224 | params.link_sta_params.link_id = |
7225 | nl80211_link_id_or_invalid(attrs: info->attrs); |
7226 | |
7227 | if (info->attrs[NL80211_ATTR_MLD_ADDR]) { |
7228 | /* If MLD_ADDR attribute is set then this is an MLD station |
7229 | * and the MLD_ADDR attribute holds the MLD address and the |
7230 | * MAC attribute holds for the LINK address. |
7231 | * In that case, the link_id is also expected to be valid. |
7232 | */ |
7233 | if (params.link_sta_params.link_id < 0) |
7234 | return -EINVAL; |
7235 | |
7236 | mac_addr = nla_data(nla: info->attrs[NL80211_ATTR_MLD_ADDR]); |
7237 | params.link_sta_params.mld_mac = mac_addr; |
7238 | params.link_sta_params.link_mac = |
7239 | nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
7240 | if (!is_valid_ether_addr(addr: params.link_sta_params.link_mac)) |
7241 | return -EINVAL; |
7242 | } else { |
7243 | mac_addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
7244 | } |
7245 | |
7246 | |
7247 | if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) { |
7248 | params.link_sta_params.supported_rates = |
7249 | nla_data(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); |
7250 | params.link_sta_params.supported_rates_len = |
7251 | nla_len(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); |
7252 | } |
7253 | |
7254 | if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) { |
7255 | params.capability = |
7256 | nla_get_u16(nla: info->attrs[NL80211_ATTR_STA_CAPABILITY]); |
7257 | params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY; |
7258 | } |
7259 | |
7260 | if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) { |
7261 | params.ext_capab = |
7262 | nla_data(nla: info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); |
7263 | params.ext_capab_len = |
7264 | nla_len(nla: info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); |
7265 | } |
7266 | |
7267 | if (parse_station_flags(info, iftype: dev->ieee80211_ptr->iftype, params: ¶ms)) |
7268 | return -EINVAL; |
7269 | |
7270 | if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) |
7271 | params.plink_action = |
7272 | nla_get_u8(nla: info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); |
7273 | |
7274 | if (info->attrs[NL80211_ATTR_STA_PLINK_STATE]) { |
7275 | params.plink_state = |
7276 | nla_get_u8(nla: info->attrs[NL80211_ATTR_STA_PLINK_STATE]); |
7277 | if (info->attrs[NL80211_ATTR_MESH_PEER_AID]) |
7278 | params.peer_aid = nla_get_u16( |
7279 | nla: info->attrs[NL80211_ATTR_MESH_PEER_AID]); |
7280 | params.sta_modify_mask |= STATION_PARAM_APPLY_PLINK_STATE; |
7281 | } |
7282 | |
7283 | if (info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]) |
7284 | params.local_pm = nla_get_u32( |
7285 | nla: info->attrs[NL80211_ATTR_LOCAL_MESH_POWER_MODE]); |
7286 | |
7287 | if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) { |
7288 | params.link_sta_params.opmode_notif_used = true; |
7289 | params.link_sta_params.opmode_notif = |
7290 | nla_get_u8(nla: info->attrs[NL80211_ATTR_OPMODE_NOTIF]); |
7291 | } |
7292 | |
7293 | if (info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]) |
7294 | params.link_sta_params.he_6ghz_capa = |
7295 | nla_data(nla: info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]); |
7296 | |
7297 | if (info->attrs[NL80211_ATTR_AIRTIME_WEIGHT]) |
7298 | params.airtime_weight = |
7299 | nla_get_u16(nla: info->attrs[NL80211_ATTR_AIRTIME_WEIGHT]); |
7300 | |
7301 | if (params.airtime_weight && |
7302 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
7303 | ftidx: NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) |
7304 | return -EOPNOTSUPP; |
7305 | |
7306 | err = nl80211_parse_sta_txpower_setting(info, |
7307 | txpwr: ¶ms.link_sta_params.txpwr, |
7308 | txpwr_set: ¶ms.link_sta_params.txpwr_set); |
7309 | if (err) |
7310 | return err; |
7311 | |
7312 | /* Include parameters for TDLS peer (will check later) */ |
7313 | err = nl80211_set_station_tdls(info, params: ¶ms); |
7314 | if (err) |
7315 | return err; |
7316 | |
7317 | params.vlan = get_vlan(info, rdev); |
7318 | if (IS_ERR(ptr: params.vlan)) |
7319 | return PTR_ERR(ptr: params.vlan); |
7320 | |
7321 | switch (dev->ieee80211_ptr->iftype) { |
7322 | case NL80211_IFTYPE_AP: |
7323 | case NL80211_IFTYPE_AP_VLAN: |
7324 | case NL80211_IFTYPE_P2P_GO: |
7325 | case NL80211_IFTYPE_P2P_CLIENT: |
7326 | case NL80211_IFTYPE_STATION: |
7327 | case NL80211_IFTYPE_ADHOC: |
7328 | case NL80211_IFTYPE_MESH_POINT: |
7329 | break; |
7330 | default: |
7331 | err = -EOPNOTSUPP; |
7332 | goto out_put_vlan; |
7333 | } |
7334 | |
7335 | /* driver will call cfg80211_check_station_change() */ |
7336 | err = rdev_change_station(rdev, dev, mac: mac_addr, params: ¶ms); |
7337 | |
7338 | out_put_vlan: |
7339 | dev_put(dev: params.vlan); |
7340 | |
7341 | return err; |
7342 | } |
7343 | |
7344 | static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) |
7345 | { |
7346 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
7347 | int err; |
7348 | struct net_device *dev = info->user_ptr[1]; |
7349 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
7350 | struct station_parameters params; |
7351 | u8 *mac_addr = NULL; |
7352 | u32 auth_assoc = BIT(NL80211_STA_FLAG_AUTHENTICATED) | |
7353 | BIT(NL80211_STA_FLAG_ASSOCIATED); |
7354 | |
7355 | memset(¶ms, 0, sizeof(params)); |
7356 | |
7357 | if (!rdev->ops->add_station) |
7358 | return -EOPNOTSUPP; |
7359 | |
7360 | if (!info->attrs[NL80211_ATTR_MAC]) |
7361 | return -EINVAL; |
7362 | |
7363 | if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) |
7364 | return -EINVAL; |
7365 | |
7366 | if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) |
7367 | return -EINVAL; |
7368 | |
7369 | if (!info->attrs[NL80211_ATTR_STA_AID] && |
7370 | !info->attrs[NL80211_ATTR_PEER_AID]) |
7371 | return -EINVAL; |
7372 | |
7373 | params.link_sta_params.link_id = |
7374 | nl80211_link_id_or_invalid(attrs: info->attrs); |
7375 | |
7376 | if (info->attrs[NL80211_ATTR_MLD_ADDR]) { |
7377 | mac_addr = nla_data(nla: info->attrs[NL80211_ATTR_MLD_ADDR]); |
7378 | params.link_sta_params.mld_mac = mac_addr; |
7379 | params.link_sta_params.link_mac = |
7380 | nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
7381 | if (!is_valid_ether_addr(addr: params.link_sta_params.link_mac)) |
7382 | return -EINVAL; |
7383 | } else { |
7384 | mac_addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
7385 | } |
7386 | |
7387 | params.link_sta_params.supported_rates = |
7388 | nla_data(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); |
7389 | params.link_sta_params.supported_rates_len = |
7390 | nla_len(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); |
7391 | params.listen_interval = |
7392 | nla_get_u16(nla: info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); |
7393 | |
7394 | if (info->attrs[NL80211_ATTR_VLAN_ID]) |
7395 | params.vlan_id = nla_get_u16(nla: info->attrs[NL80211_ATTR_VLAN_ID]); |
7396 | |
7397 | if (info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]) { |
7398 | params.support_p2p_ps = |
7399 | nla_get_u8(nla: info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]); |
7400 | } else { |
7401 | /* |
7402 | * if not specified, assume it's supported for P2P GO interface, |
7403 | * and is NOT supported for AP interface |
7404 | */ |
7405 | params.support_p2p_ps = |
7406 | dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO; |
7407 | } |
7408 | |
7409 | if (info->attrs[NL80211_ATTR_PEER_AID]) |
7410 | params.aid = nla_get_u16(nla: info->attrs[NL80211_ATTR_PEER_AID]); |
7411 | else |
7412 | params.aid = nla_get_u16(nla: info->attrs[NL80211_ATTR_STA_AID]); |
7413 | |
7414 | if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) { |
7415 | params.capability = |
7416 | nla_get_u16(nla: info->attrs[NL80211_ATTR_STA_CAPABILITY]); |
7417 | params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY; |
7418 | } |
7419 | |
7420 | if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) { |
7421 | params.ext_capab = |
7422 | nla_data(nla: info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); |
7423 | params.ext_capab_len = |
7424 | nla_len(nla: info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]); |
7425 | } |
7426 | |
7427 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) |
7428 | params.link_sta_params.ht_capa = |
7429 | nla_data(nla: info->attrs[NL80211_ATTR_HT_CAPABILITY]); |
7430 | |
7431 | if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) |
7432 | params.link_sta_params.vht_capa = |
7433 | nla_data(nla: info->attrs[NL80211_ATTR_VHT_CAPABILITY]); |
7434 | |
7435 | if (info->attrs[NL80211_ATTR_HE_CAPABILITY]) { |
7436 | params.link_sta_params.he_capa = |
7437 | nla_data(nla: info->attrs[NL80211_ATTR_HE_CAPABILITY]); |
7438 | params.link_sta_params.he_capa_len = |
7439 | nla_len(nla: info->attrs[NL80211_ATTR_HE_CAPABILITY]); |
7440 | |
7441 | if (info->attrs[NL80211_ATTR_EHT_CAPABILITY]) { |
7442 | params.link_sta_params.eht_capa = |
7443 | nla_data(nla: info->attrs[NL80211_ATTR_EHT_CAPABILITY]); |
7444 | params.link_sta_params.eht_capa_len = |
7445 | nla_len(nla: info->attrs[NL80211_ATTR_EHT_CAPABILITY]); |
7446 | |
7447 | if (!ieee80211_eht_capa_size_ok(he_capa: (const u8 *)params.link_sta_params.he_capa, |
7448 | data: (const u8 *)params.link_sta_params.eht_capa, |
7449 | len: params.link_sta_params.eht_capa_len, |
7450 | from_ap: false)) |
7451 | return -EINVAL; |
7452 | } |
7453 | } |
7454 | |
7455 | if (info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]) |
7456 | params.link_sta_params.he_6ghz_capa = |
7457 | nla_data(nla: info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]); |
7458 | |
7459 | if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) { |
7460 | params.link_sta_params.opmode_notif_used = true; |
7461 | params.link_sta_params.opmode_notif = |
7462 | nla_get_u8(nla: info->attrs[NL80211_ATTR_OPMODE_NOTIF]); |
7463 | } |
7464 | |
7465 | if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) |
7466 | params.plink_action = |
7467 | nla_get_u8(nla: info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); |
7468 | |
7469 | if (info->attrs[NL80211_ATTR_AIRTIME_WEIGHT]) |
7470 | params.airtime_weight = |
7471 | nla_get_u16(nla: info->attrs[NL80211_ATTR_AIRTIME_WEIGHT]); |
7472 | |
7473 | if (params.airtime_weight && |
7474 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
7475 | ftidx: NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) |
7476 | return -EOPNOTSUPP; |
7477 | |
7478 | err = nl80211_parse_sta_txpower_setting(info, |
7479 | txpwr: ¶ms.link_sta_params.txpwr, |
7480 | txpwr_set: ¶ms.link_sta_params.txpwr_set); |
7481 | if (err) |
7482 | return err; |
7483 | |
7484 | err = nl80211_parse_sta_channel_info(info, params: ¶ms); |
7485 | if (err) |
7486 | return err; |
7487 | |
7488 | err = nl80211_parse_sta_wme(info, params: ¶ms); |
7489 | if (err) |
7490 | return err; |
7491 | |
7492 | if (parse_station_flags(info, iftype: dev->ieee80211_ptr->iftype, params: ¶ms)) |
7493 | return -EINVAL; |
7494 | |
7495 | /* HT/VHT requires QoS, but if we don't have that just ignore HT/VHT |
7496 | * as userspace might just pass through the capabilities from the IEs |
7497 | * directly, rather than enforcing this restriction and returning an |
7498 | * error in this case. |
7499 | */ |
7500 | if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME))) { |
7501 | params.link_sta_params.ht_capa = NULL; |
7502 | params.link_sta_params.vht_capa = NULL; |
7503 | |
7504 | /* HE and EHT require WME */ |
7505 | if (params.link_sta_params.he_capa_len || |
7506 | params.link_sta_params.he_6ghz_capa || |
7507 | params.link_sta_params.eht_capa_len) |
7508 | return -EINVAL; |
7509 | } |
7510 | |
7511 | /* Ensure that HT/VHT capabilities are not set for 6 GHz HE STA */ |
7512 | if (params.link_sta_params.he_6ghz_capa && |
7513 | (params.link_sta_params.ht_capa || params.link_sta_params.vht_capa)) |
7514 | return -EINVAL; |
7515 | |
7516 | /* When you run into this, adjust the code below for the new flag */ |
7517 | BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 8); |
7518 | |
7519 | switch (dev->ieee80211_ptr->iftype) { |
7520 | case NL80211_IFTYPE_AP: |
7521 | case NL80211_IFTYPE_AP_VLAN: |
7522 | case NL80211_IFTYPE_P2P_GO: |
7523 | /* ignore WME attributes if iface/sta is not capable */ |
7524 | if (!(rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) || |
7525 | !(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME))) |
7526 | params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD; |
7527 | |
7528 | /* TDLS peers cannot be added */ |
7529 | if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) || |
7530 | info->attrs[NL80211_ATTR_PEER_AID]) |
7531 | return -EINVAL; |
7532 | /* but don't bother the driver with it */ |
7533 | params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); |
7534 | |
7535 | /* allow authenticated/associated only if driver handles it */ |
7536 | if (!(rdev->wiphy.features & |
7537 | NL80211_FEATURE_FULL_AP_CLIENT_STATE) && |
7538 | params.sta_flags_mask & auth_assoc) |
7539 | return -EINVAL; |
7540 | |
7541 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
7542 | ftidx: NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT) && |
7543 | params.sta_flags_mask & BIT(NL80211_STA_FLAG_SPP_AMSDU)) |
7544 | return -EINVAL; |
7545 | |
7546 | /* Older userspace, or userspace wanting to be compatible with |
7547 | * !NL80211_FEATURE_FULL_AP_CLIENT_STATE, will not set the auth |
7548 | * and assoc flags in the mask, but assumes the station will be |
7549 | * added as associated anyway since this was the required driver |
7550 | * behaviour before NL80211_FEATURE_FULL_AP_CLIENT_STATE was |
7551 | * introduced. |
7552 | * In order to not bother drivers with this quirk in the API |
7553 | * set the flags in both the mask and set for new stations in |
7554 | * this case. |
7555 | */ |
7556 | if (!(params.sta_flags_mask & auth_assoc)) { |
7557 | params.sta_flags_mask |= auth_assoc; |
7558 | params.sta_flags_set |= auth_assoc; |
7559 | } |
7560 | |
7561 | /* must be last in here for error handling */ |
7562 | params.vlan = get_vlan(info, rdev); |
7563 | if (IS_ERR(ptr: params.vlan)) |
7564 | return PTR_ERR(ptr: params.vlan); |
7565 | break; |
7566 | case NL80211_IFTYPE_MESH_POINT: |
7567 | /* ignore uAPSD data */ |
7568 | params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD; |
7569 | |
7570 | /* associated is disallowed */ |
7571 | if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED)) |
7572 | return -EINVAL; |
7573 | /* TDLS peers cannot be added */ |
7574 | if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) || |
7575 | info->attrs[NL80211_ATTR_PEER_AID]) |
7576 | return -EINVAL; |
7577 | break; |
7578 | case NL80211_IFTYPE_STATION: |
7579 | case NL80211_IFTYPE_P2P_CLIENT: |
7580 | /* ignore uAPSD data */ |
7581 | params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD; |
7582 | |
7583 | /* these are disallowed */ |
7584 | if (params.sta_flags_mask & |
7585 | (BIT(NL80211_STA_FLAG_ASSOCIATED) | |
7586 | BIT(NL80211_STA_FLAG_AUTHENTICATED))) |
7587 | return -EINVAL; |
7588 | /* Only TDLS peers can be added */ |
7589 | if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) |
7590 | return -EINVAL; |
7591 | /* Can only add if TDLS ... */ |
7592 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS)) |
7593 | return -EOPNOTSUPP; |
7594 | /* ... with external setup is supported */ |
7595 | if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)) |
7596 | return -EOPNOTSUPP; |
7597 | /* |
7598 | * Older wpa_supplicant versions always mark the TDLS peer |
7599 | * as authorized, but it shouldn't yet be. |
7600 | */ |
7601 | params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_AUTHORIZED); |
7602 | break; |
7603 | default: |
7604 | return -EOPNOTSUPP; |
7605 | } |
7606 | |
7607 | /* be aware of params.vlan when changing code here */ |
7608 | |
7609 | if (wdev->valid_links) { |
7610 | if (params.link_sta_params.link_id < 0) { |
7611 | err = -EINVAL; |
7612 | goto out; |
7613 | } |
7614 | if (!(wdev->valid_links & BIT(params.link_sta_params.link_id))) { |
7615 | err = -ENOLINK; |
7616 | goto out; |
7617 | } |
7618 | } else { |
7619 | if (params.link_sta_params.link_id >= 0) { |
7620 | err = -EINVAL; |
7621 | goto out; |
7622 | } |
7623 | } |
7624 | err = rdev_add_station(rdev, dev, mac: mac_addr, params: ¶ms); |
7625 | out: |
7626 | dev_put(dev: params.vlan); |
7627 | return err; |
7628 | } |
7629 | |
7630 | static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) |
7631 | { |
7632 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
7633 | struct net_device *dev = info->user_ptr[1]; |
7634 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
7635 | struct station_del_parameters params; |
7636 | int link_id = nl80211_link_id_or_invalid(attrs: info->attrs); |
7637 | |
7638 | memset(¶ms, 0, sizeof(params)); |
7639 | |
7640 | if (info->attrs[NL80211_ATTR_MAC]) |
7641 | params.mac = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
7642 | |
7643 | switch (wdev->iftype) { |
7644 | case NL80211_IFTYPE_AP: |
7645 | case NL80211_IFTYPE_AP_VLAN: |
7646 | case NL80211_IFTYPE_MESH_POINT: |
7647 | case NL80211_IFTYPE_P2P_GO: |
7648 | /* always accept these */ |
7649 | break; |
7650 | case NL80211_IFTYPE_ADHOC: |
7651 | /* conditionally accept */ |
7652 | if (wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
7653 | ftidx: NL80211_EXT_FEATURE_DEL_IBSS_STA)) |
7654 | break; |
7655 | return -EINVAL; |
7656 | default: |
7657 | return -EINVAL; |
7658 | } |
7659 | |
7660 | if (!rdev->ops->del_station) |
7661 | return -EOPNOTSUPP; |
7662 | |
7663 | if (info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) { |
7664 | params.subtype = |
7665 | nla_get_u8(nla: info->attrs[NL80211_ATTR_MGMT_SUBTYPE]); |
7666 | if (params.subtype != IEEE80211_STYPE_DISASSOC >> 4 && |
7667 | params.subtype != IEEE80211_STYPE_DEAUTH >> 4) |
7668 | return -EINVAL; |
7669 | } else { |
7670 | /* Default to Deauthentication frame */ |
7671 | params.subtype = IEEE80211_STYPE_DEAUTH >> 4; |
7672 | } |
7673 | |
7674 | if (info->attrs[NL80211_ATTR_REASON_CODE]) { |
7675 | params.reason_code = |
7676 | nla_get_u16(nla: info->attrs[NL80211_ATTR_REASON_CODE]); |
7677 | if (params.reason_code == 0) |
7678 | return -EINVAL; /* 0 is reserved */ |
7679 | } else { |
7680 | /* Default to reason code 2 */ |
7681 | params.reason_code = WLAN_REASON_PREV_AUTH_NOT_VALID; |
7682 | } |
7683 | |
7684 | /* Link ID not expected in case of non-ML operation */ |
7685 | if (!wdev->valid_links && link_id != -1) |
7686 | return -EINVAL; |
7687 | |
7688 | /* If given, a valid link ID should be passed during MLO */ |
7689 | if (wdev->valid_links && link_id >= 0 && |
7690 | !(wdev->valid_links & BIT(link_id))) |
7691 | return -EINVAL; |
7692 | |
7693 | params.link_id = link_id; |
7694 | |
7695 | return rdev_del_station(rdev, dev, params: ¶ms); |
7696 | } |
7697 | |
7698 | static int nl80211_send_mpath(struct sk_buff *msg, u32 portid, u32 seq, |
7699 | int flags, struct net_device *dev, |
7700 | u8 *dst, u8 *next_hop, |
7701 | struct mpath_info *pinfo) |
7702 | { |
7703 | void *hdr; |
7704 | struct nlattr *pinfoattr; |
7705 | |
7706 | hdr = nl80211hdr_put(skb: msg, portid, seq, flags, cmd: NL80211_CMD_NEW_MPATH); |
7707 | if (!hdr) |
7708 | return -1; |
7709 | |
7710 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || |
7711 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: dst) || |
7712 | nla_put(skb: msg, attrtype: NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, data: next_hop) || |
7713 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_GENERATION, value: pinfo->generation)) |
7714 | goto nla_put_failure; |
7715 | |
7716 | pinfoattr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_MPATH_INFO); |
7717 | if (!pinfoattr) |
7718 | goto nla_put_failure; |
7719 | if ((pinfo->filled & MPATH_INFO_FRAME_QLEN) && |
7720 | nla_put_u32(skb: msg, attrtype: NL80211_MPATH_INFO_FRAME_QLEN, |
7721 | value: pinfo->frame_qlen)) |
7722 | goto nla_put_failure; |
7723 | if (((pinfo->filled & MPATH_INFO_SN) && |
7724 | nla_put_u32(skb: msg, attrtype: NL80211_MPATH_INFO_SN, value: pinfo->sn)) || |
7725 | ((pinfo->filled & MPATH_INFO_METRIC) && |
7726 | nla_put_u32(skb: msg, attrtype: NL80211_MPATH_INFO_METRIC, |
7727 | value: pinfo->metric)) || |
7728 | ((pinfo->filled & MPATH_INFO_EXPTIME) && |
7729 | nla_put_u32(skb: msg, attrtype: NL80211_MPATH_INFO_EXPTIME, |
7730 | value: pinfo->exptime)) || |
7731 | ((pinfo->filled & MPATH_INFO_FLAGS) && |
7732 | nla_put_u8(skb: msg, attrtype: NL80211_MPATH_INFO_FLAGS, |
7733 | value: pinfo->flags)) || |
7734 | ((pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT) && |
7735 | nla_put_u32(skb: msg, attrtype: NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, |
7736 | value: pinfo->discovery_timeout)) || |
7737 | ((pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES) && |
7738 | nla_put_u8(skb: msg, attrtype: NL80211_MPATH_INFO_DISCOVERY_RETRIES, |
7739 | value: pinfo->discovery_retries)) || |
7740 | ((pinfo->filled & MPATH_INFO_HOP_COUNT) && |
7741 | nla_put_u8(skb: msg, attrtype: NL80211_MPATH_INFO_HOP_COUNT, |
7742 | value: pinfo->hop_count)) || |
7743 | ((pinfo->filled & MPATH_INFO_PATH_CHANGE) && |
7744 | nla_put_u32(skb: msg, attrtype: NL80211_MPATH_INFO_PATH_CHANGE, |
7745 | value: pinfo->path_change_count))) |
7746 | goto nla_put_failure; |
7747 | |
7748 | nla_nest_end(skb: msg, start: pinfoattr); |
7749 | |
7750 | genlmsg_end(skb: msg, hdr); |
7751 | return 0; |
7752 | |
7753 | nla_put_failure: |
7754 | genlmsg_cancel(skb: msg, hdr); |
7755 | return -EMSGSIZE; |
7756 | } |
7757 | |
7758 | static int nl80211_dump_mpath(struct sk_buff *skb, |
7759 | struct netlink_callback *cb) |
7760 | { |
7761 | struct mpath_info pinfo; |
7762 | struct cfg80211_registered_device *rdev; |
7763 | struct wireless_dev *wdev; |
7764 | u8 dst[ETH_ALEN]; |
7765 | u8 next_hop[ETH_ALEN]; |
7766 | int path_idx = cb->args[2]; |
7767 | int err; |
7768 | |
7769 | err = nl80211_prepare_wdev_dump(cb, rdev: &rdev, wdev: &wdev, NULL); |
7770 | if (err) |
7771 | return err; |
7772 | /* nl80211_prepare_wdev_dump acquired it in the successful case */ |
7773 | __acquire(&rdev->wiphy.mtx); |
7774 | |
7775 | if (!rdev->ops->dump_mpath) { |
7776 | err = -EOPNOTSUPP; |
7777 | goto out_err; |
7778 | } |
7779 | |
7780 | if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) { |
7781 | err = -EOPNOTSUPP; |
7782 | goto out_err; |
7783 | } |
7784 | |
7785 | while (1) { |
7786 | err = rdev_dump_mpath(rdev, dev: wdev->netdev, idx: path_idx, dst, |
7787 | next_hop, pinfo: &pinfo); |
7788 | if (err == -ENOENT) |
7789 | break; |
7790 | if (err) |
7791 | goto out_err; |
7792 | |
7793 | if (nl80211_send_mpath(msg: skb, NETLINK_CB(cb->skb).portid, |
7794 | seq: cb->nlh->nlmsg_seq, NLM_F_MULTI, |
7795 | dev: wdev->netdev, dst, next_hop, |
7796 | pinfo: &pinfo) < 0) |
7797 | goto out; |
7798 | |
7799 | path_idx++; |
7800 | } |
7801 | |
7802 | out: |
7803 | cb->args[2] = path_idx; |
7804 | err = skb->len; |
7805 | out_err: |
7806 | wiphy_unlock(wiphy: &rdev->wiphy); |
7807 | return err; |
7808 | } |
7809 | |
7810 | static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) |
7811 | { |
7812 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
7813 | int err; |
7814 | struct net_device *dev = info->user_ptr[1]; |
7815 | struct mpath_info pinfo; |
7816 | struct sk_buff *msg; |
7817 | u8 *dst = NULL; |
7818 | u8 next_hop[ETH_ALEN]; |
7819 | |
7820 | memset(&pinfo, 0, sizeof(pinfo)); |
7821 | |
7822 | if (!info->attrs[NL80211_ATTR_MAC]) |
7823 | return -EINVAL; |
7824 | |
7825 | dst = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
7826 | |
7827 | if (!rdev->ops->get_mpath) |
7828 | return -EOPNOTSUPP; |
7829 | |
7830 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) |
7831 | return -EOPNOTSUPP; |
7832 | |
7833 | err = rdev_get_mpath(rdev, dev, dst, next_hop, pinfo: &pinfo); |
7834 | if (err) |
7835 | return err; |
7836 | |
7837 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
7838 | if (!msg) |
7839 | return -ENOMEM; |
7840 | |
7841 | if (nl80211_send_mpath(msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
7842 | dev, dst, next_hop, pinfo: &pinfo) < 0) { |
7843 | nlmsg_free(skb: msg); |
7844 | return -ENOBUFS; |
7845 | } |
7846 | |
7847 | return genlmsg_reply(skb: msg, info); |
7848 | } |
7849 | |
7850 | static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) |
7851 | { |
7852 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
7853 | struct net_device *dev = info->user_ptr[1]; |
7854 | u8 *dst = NULL; |
7855 | u8 *next_hop = NULL; |
7856 | |
7857 | if (!info->attrs[NL80211_ATTR_MAC]) |
7858 | return -EINVAL; |
7859 | |
7860 | if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]) |
7861 | return -EINVAL; |
7862 | |
7863 | dst = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
7864 | next_hop = nla_data(nla: info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); |
7865 | |
7866 | if (!rdev->ops->change_mpath) |
7867 | return -EOPNOTSUPP; |
7868 | |
7869 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) |
7870 | return -EOPNOTSUPP; |
7871 | |
7872 | return rdev_change_mpath(rdev, dev, dst, next_hop); |
7873 | } |
7874 | |
7875 | static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) |
7876 | { |
7877 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
7878 | struct net_device *dev = info->user_ptr[1]; |
7879 | u8 *dst = NULL; |
7880 | u8 *next_hop = NULL; |
7881 | |
7882 | if (!info->attrs[NL80211_ATTR_MAC]) |
7883 | return -EINVAL; |
7884 | |
7885 | if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]) |
7886 | return -EINVAL; |
7887 | |
7888 | dst = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
7889 | next_hop = nla_data(nla: info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); |
7890 | |
7891 | if (!rdev->ops->add_mpath) |
7892 | return -EOPNOTSUPP; |
7893 | |
7894 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) |
7895 | return -EOPNOTSUPP; |
7896 | |
7897 | return rdev_add_mpath(rdev, dev, dst, next_hop); |
7898 | } |
7899 | |
7900 | static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) |
7901 | { |
7902 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
7903 | struct net_device *dev = info->user_ptr[1]; |
7904 | u8 *dst = NULL; |
7905 | |
7906 | if (info->attrs[NL80211_ATTR_MAC]) |
7907 | dst = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
7908 | |
7909 | if (!rdev->ops->del_mpath) |
7910 | return -EOPNOTSUPP; |
7911 | |
7912 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) |
7913 | return -EOPNOTSUPP; |
7914 | |
7915 | return rdev_del_mpath(rdev, dev, dst); |
7916 | } |
7917 | |
7918 | static int nl80211_get_mpp(struct sk_buff *skb, struct genl_info *info) |
7919 | { |
7920 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
7921 | int err; |
7922 | struct net_device *dev = info->user_ptr[1]; |
7923 | struct mpath_info pinfo; |
7924 | struct sk_buff *msg; |
7925 | u8 *dst = NULL; |
7926 | u8 mpp[ETH_ALEN]; |
7927 | |
7928 | memset(&pinfo, 0, sizeof(pinfo)); |
7929 | |
7930 | if (!info->attrs[NL80211_ATTR_MAC]) |
7931 | return -EINVAL; |
7932 | |
7933 | dst = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
7934 | |
7935 | if (!rdev->ops->get_mpp) |
7936 | return -EOPNOTSUPP; |
7937 | |
7938 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) |
7939 | return -EOPNOTSUPP; |
7940 | |
7941 | err = rdev_get_mpp(rdev, dev, dst, mpp, pinfo: &pinfo); |
7942 | if (err) |
7943 | return err; |
7944 | |
7945 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
7946 | if (!msg) |
7947 | return -ENOMEM; |
7948 | |
7949 | if (nl80211_send_mpath(msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
7950 | dev, dst, next_hop: mpp, pinfo: &pinfo) < 0) { |
7951 | nlmsg_free(skb: msg); |
7952 | return -ENOBUFS; |
7953 | } |
7954 | |
7955 | return genlmsg_reply(skb: msg, info); |
7956 | } |
7957 | |
7958 | static int nl80211_dump_mpp(struct sk_buff *skb, |
7959 | struct netlink_callback *cb) |
7960 | { |
7961 | struct mpath_info pinfo; |
7962 | struct cfg80211_registered_device *rdev; |
7963 | struct wireless_dev *wdev; |
7964 | u8 dst[ETH_ALEN]; |
7965 | u8 mpp[ETH_ALEN]; |
7966 | int path_idx = cb->args[2]; |
7967 | int err; |
7968 | |
7969 | err = nl80211_prepare_wdev_dump(cb, rdev: &rdev, wdev: &wdev, NULL); |
7970 | if (err) |
7971 | return err; |
7972 | /* nl80211_prepare_wdev_dump acquired it in the successful case */ |
7973 | __acquire(&rdev->wiphy.mtx); |
7974 | |
7975 | if (!rdev->ops->dump_mpp) { |
7976 | err = -EOPNOTSUPP; |
7977 | goto out_err; |
7978 | } |
7979 | |
7980 | if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) { |
7981 | err = -EOPNOTSUPP; |
7982 | goto out_err; |
7983 | } |
7984 | |
7985 | while (1) { |
7986 | err = rdev_dump_mpp(rdev, dev: wdev->netdev, idx: path_idx, dst, |
7987 | mpp, pinfo: &pinfo); |
7988 | if (err == -ENOENT) |
7989 | break; |
7990 | if (err) |
7991 | goto out_err; |
7992 | |
7993 | if (nl80211_send_mpath(msg: skb, NETLINK_CB(cb->skb).portid, |
7994 | seq: cb->nlh->nlmsg_seq, NLM_F_MULTI, |
7995 | dev: wdev->netdev, dst, next_hop: mpp, |
7996 | pinfo: &pinfo) < 0) |
7997 | goto out; |
7998 | |
7999 | path_idx++; |
8000 | } |
8001 | |
8002 | out: |
8003 | cb->args[2] = path_idx; |
8004 | err = skb->len; |
8005 | out_err: |
8006 | wiphy_unlock(wiphy: &rdev->wiphy); |
8007 | return err; |
8008 | } |
8009 | |
8010 | static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) |
8011 | { |
8012 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
8013 | struct net_device *dev = info->user_ptr[1]; |
8014 | struct bss_parameters params; |
8015 | |
8016 | memset(¶ms, 0, sizeof(params)); |
8017 | params.link_id = nl80211_link_id_or_invalid(attrs: info->attrs); |
8018 | /* default to not changing parameters */ |
8019 | params.use_cts_prot = -1; |
8020 | params.use_short_preamble = -1; |
8021 | params.use_short_slot_time = -1; |
8022 | params.ap_isolate = -1; |
8023 | params.ht_opmode = -1; |
8024 | params.p2p_ctwindow = -1; |
8025 | params.p2p_opp_ps = -1; |
8026 | |
8027 | if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) |
8028 | params.use_cts_prot = |
8029 | nla_get_u8(nla: info->attrs[NL80211_ATTR_BSS_CTS_PROT]); |
8030 | if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]) |
8031 | params.use_short_preamble = |
8032 | nla_get_u8(nla: info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]); |
8033 | if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]) |
8034 | params.use_short_slot_time = |
8035 | nla_get_u8(nla: info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]); |
8036 | if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { |
8037 | params.basic_rates = |
8038 | nla_data(nla: info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); |
8039 | params.basic_rates_len = |
8040 | nla_len(nla: info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); |
8041 | } |
8042 | if (info->attrs[NL80211_ATTR_AP_ISOLATE]) |
8043 | params.ap_isolate = !!nla_get_u8(nla: info->attrs[NL80211_ATTR_AP_ISOLATE]); |
8044 | if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE]) |
8045 | params.ht_opmode = |
8046 | nla_get_u16(nla: info->attrs[NL80211_ATTR_BSS_HT_OPMODE]); |
8047 | |
8048 | if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) { |
8049 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) |
8050 | return -EINVAL; |
8051 | params.p2p_ctwindow = |
8052 | nla_get_u8(nla: info->attrs[NL80211_ATTR_P2P_CTWINDOW]); |
8053 | if (params.p2p_ctwindow != 0 && |
8054 | !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN)) |
8055 | return -EINVAL; |
8056 | } |
8057 | |
8058 | if (info->attrs[NL80211_ATTR_P2P_OPPPS]) { |
8059 | u8 tmp; |
8060 | |
8061 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) |
8062 | return -EINVAL; |
8063 | tmp = nla_get_u8(nla: info->attrs[NL80211_ATTR_P2P_OPPPS]); |
8064 | params.p2p_opp_ps = tmp; |
8065 | if (params.p2p_opp_ps && |
8066 | !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS)) |
8067 | return -EINVAL; |
8068 | } |
8069 | |
8070 | if (!rdev->ops->change_bss) |
8071 | return -EOPNOTSUPP; |
8072 | |
8073 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && |
8074 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) |
8075 | return -EOPNOTSUPP; |
8076 | |
8077 | return rdev_change_bss(rdev, dev, params: ¶ms); |
8078 | } |
8079 | |
8080 | static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) |
8081 | { |
8082 | char *data = NULL; |
8083 | bool is_indoor; |
8084 | enum nl80211_user_reg_hint_type user_reg_hint_type; |
8085 | u32 owner_nlportid; |
8086 | |
8087 | /* |
8088 | * You should only get this when cfg80211 hasn't yet initialized |
8089 | * completely when built-in to the kernel right between the time |
8090 | * window between nl80211_init() and regulatory_init(), if that is |
8091 | * even possible. |
8092 | */ |
8093 | if (unlikely(!rcu_access_pointer(cfg80211_regdomain))) |
8094 | return -EINPROGRESS; |
8095 | |
8096 | if (info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]) |
8097 | user_reg_hint_type = |
8098 | nla_get_u32(nla: info->attrs[NL80211_ATTR_USER_REG_HINT_TYPE]); |
8099 | else |
8100 | user_reg_hint_type = NL80211_USER_REG_HINT_USER; |
8101 | |
8102 | switch (user_reg_hint_type) { |
8103 | case NL80211_USER_REG_HINT_USER: |
8104 | case NL80211_USER_REG_HINT_CELL_BASE: |
8105 | if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) |
8106 | return -EINVAL; |
8107 | |
8108 | data = nla_data(nla: info->attrs[NL80211_ATTR_REG_ALPHA2]); |
8109 | return regulatory_hint_user(alpha2: data, user_reg_hint_type); |
8110 | case NL80211_USER_REG_HINT_INDOOR: |
8111 | if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) { |
8112 | owner_nlportid = info->snd_portid; |
8113 | is_indoor = !!info->attrs[NL80211_ATTR_REG_INDOOR]; |
8114 | } else { |
8115 | owner_nlportid = 0; |
8116 | is_indoor = true; |
8117 | } |
8118 | |
8119 | return regulatory_hint_indoor(is_indoor, portid: owner_nlportid); |
8120 | default: |
8121 | return -EINVAL; |
8122 | } |
8123 | } |
8124 | |
8125 | static int nl80211_reload_regdb(struct sk_buff *skb, struct genl_info *info) |
8126 | { |
8127 | return reg_reload_regdb(); |
8128 | } |
8129 | |
8130 | static int nl80211_get_mesh_config(struct sk_buff *skb, |
8131 | struct genl_info *info) |
8132 | { |
8133 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
8134 | struct net_device *dev = info->user_ptr[1]; |
8135 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
8136 | struct mesh_config cur_params; |
8137 | int err = 0; |
8138 | void *hdr; |
8139 | struct nlattr *pinfoattr; |
8140 | struct sk_buff *msg; |
8141 | |
8142 | if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) |
8143 | return -EOPNOTSUPP; |
8144 | |
8145 | if (!rdev->ops->get_mesh_config) |
8146 | return -EOPNOTSUPP; |
8147 | |
8148 | /* If not connected, get default parameters */ |
8149 | if (!wdev->u.mesh.id_len) |
8150 | memcpy(&cur_params, &default_mesh_config, sizeof(cur_params)); |
8151 | else |
8152 | err = rdev_get_mesh_config(rdev, dev, conf: &cur_params); |
8153 | |
8154 | if (err) |
8155 | return err; |
8156 | |
8157 | /* Draw up a netlink message to send back */ |
8158 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
8159 | if (!msg) |
8160 | return -ENOMEM; |
8161 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
8162 | cmd: NL80211_CMD_GET_MESH_CONFIG); |
8163 | if (!hdr) |
8164 | goto out; |
8165 | pinfoattr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_MESH_CONFIG); |
8166 | if (!pinfoattr) |
8167 | goto nla_put_failure; |
8168 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || |
8169 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_RETRY_TIMEOUT, |
8170 | value: cur_params.dot11MeshRetryTimeout) || |
8171 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_CONFIRM_TIMEOUT, |
8172 | value: cur_params.dot11MeshConfirmTimeout) || |
8173 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_HOLDING_TIMEOUT, |
8174 | value: cur_params.dot11MeshHoldingTimeout) || |
8175 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_MAX_PEER_LINKS, |
8176 | value: cur_params.dot11MeshMaxPeerLinks) || |
8177 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_MAX_RETRIES, |
8178 | value: cur_params.dot11MeshMaxRetries) || |
8179 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_TTL, |
8180 | value: cur_params.dot11MeshTTL) || |
8181 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_ELEMENT_TTL, |
8182 | value: cur_params.element_ttl) || |
8183 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_AUTO_OPEN_PLINKS, |
8184 | value: cur_params.auto_open_plinks) || |
8185 | nla_put_u32(skb: msg, attrtype: NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, |
8186 | value: cur_params.dot11MeshNbrOffsetMaxNeighbor) || |
8187 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, |
8188 | value: cur_params.dot11MeshHWMPmaxPREQretries) || |
8189 | nla_put_u32(skb: msg, attrtype: NL80211_MESHCONF_PATH_REFRESH_TIME, |
8190 | value: cur_params.path_refresh_time) || |
8191 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, |
8192 | value: cur_params.min_discovery_timeout) || |
8193 | nla_put_u32(skb: msg, attrtype: NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, |
8194 | value: cur_params.dot11MeshHWMPactivePathTimeout) || |
8195 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, |
8196 | value: cur_params.dot11MeshHWMPpreqMinInterval) || |
8197 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, |
8198 | value: cur_params.dot11MeshHWMPperrMinInterval) || |
8199 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, |
8200 | value: cur_params.dot11MeshHWMPnetDiameterTraversalTime) || |
8201 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_HWMP_ROOTMODE, |
8202 | value: cur_params.dot11MeshHWMPRootMode) || |
8203 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_HWMP_RANN_INTERVAL, |
8204 | value: cur_params.dot11MeshHWMPRannInterval) || |
8205 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_GATE_ANNOUNCEMENTS, |
8206 | value: cur_params.dot11MeshGateAnnouncementProtocol) || |
8207 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_FORWARDING, |
8208 | value: cur_params.dot11MeshForwarding) || |
8209 | nla_put_s32(skb: msg, attrtype: NL80211_MESHCONF_RSSI_THRESHOLD, |
8210 | value: cur_params.rssi_threshold) || |
8211 | nla_put_u32(skb: msg, attrtype: NL80211_MESHCONF_HT_OPMODE, |
8212 | value: cur_params.ht_opmode) || |
8213 | nla_put_u32(skb: msg, attrtype: NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, |
8214 | value: cur_params.dot11MeshHWMPactivePathToRootTimeout) || |
8215 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_HWMP_ROOT_INTERVAL, |
8216 | value: cur_params.dot11MeshHWMProotInterval) || |
8217 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, |
8218 | value: cur_params.dot11MeshHWMPconfirmationInterval) || |
8219 | nla_put_u32(skb: msg, attrtype: NL80211_MESHCONF_POWER_MODE, |
8220 | value: cur_params.power_mode) || |
8221 | nla_put_u16(skb: msg, attrtype: NL80211_MESHCONF_AWAKE_WINDOW, |
8222 | value: cur_params.dot11MeshAwakeWindowDuration) || |
8223 | nla_put_u32(skb: msg, attrtype: NL80211_MESHCONF_PLINK_TIMEOUT, |
8224 | value: cur_params.plink_timeout) || |
8225 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_CONNECTED_TO_GATE, |
8226 | value: cur_params.dot11MeshConnectedToMeshGate) || |
8227 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_NOLEARN, |
8228 | value: cur_params.dot11MeshNolearn) || |
8229 | nla_put_u8(skb: msg, attrtype: NL80211_MESHCONF_CONNECTED_TO_AS, |
8230 | value: cur_params.dot11MeshConnectedToAuthServer)) |
8231 | goto nla_put_failure; |
8232 | nla_nest_end(skb: msg, start: pinfoattr); |
8233 | genlmsg_end(skb: msg, hdr); |
8234 | return genlmsg_reply(skb: msg, info); |
8235 | |
8236 | nla_put_failure: |
8237 | out: |
8238 | nlmsg_free(skb: msg); |
8239 | return -ENOBUFS; |
8240 | } |
8241 | |
8242 | static const struct nla_policy |
8243 | nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = { |
8244 | [NL80211_MESHCONF_RETRY_TIMEOUT] = |
8245 | NLA_POLICY_RANGE(NLA_U16, 1, 255), |
8246 | [NL80211_MESHCONF_CONFIRM_TIMEOUT] = |
8247 | NLA_POLICY_RANGE(NLA_U16, 1, 255), |
8248 | [NL80211_MESHCONF_HOLDING_TIMEOUT] = |
8249 | NLA_POLICY_RANGE(NLA_U16, 1, 255), |
8250 | [NL80211_MESHCONF_MAX_PEER_LINKS] = |
8251 | NLA_POLICY_RANGE(NLA_U16, 0, 255), |
8252 | [NL80211_MESHCONF_MAX_RETRIES] = NLA_POLICY_MAX(NLA_U8, 16), |
8253 | [NL80211_MESHCONF_TTL] = NLA_POLICY_MIN(NLA_U8, 1), |
8254 | [NL80211_MESHCONF_ELEMENT_TTL] = NLA_POLICY_MIN(NLA_U8, 1), |
8255 | [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = NLA_POLICY_MAX(NLA_U8, 1), |
8256 | [NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR] = |
8257 | NLA_POLICY_RANGE(NLA_U32, 1, 255), |
8258 | [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 }, |
8259 | [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 }, |
8260 | [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = NLA_POLICY_MIN(NLA_U16, 1), |
8261 | [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 }, |
8262 | [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = |
8263 | NLA_POLICY_MIN(NLA_U16, 1), |
8264 | [NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL] = |
8265 | NLA_POLICY_MIN(NLA_U16, 1), |
8266 | [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = |
8267 | NLA_POLICY_MIN(NLA_U16, 1), |
8268 | [NL80211_MESHCONF_HWMP_ROOTMODE] = NLA_POLICY_MAX(NLA_U8, 4), |
8269 | [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = |
8270 | NLA_POLICY_MIN(NLA_U16, 1), |
8271 | [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = NLA_POLICY_MAX(NLA_U8, 1), |
8272 | [NL80211_MESHCONF_FORWARDING] = NLA_POLICY_MAX(NLA_U8, 1), |
8273 | [NL80211_MESHCONF_RSSI_THRESHOLD] = |
8274 | NLA_POLICY_RANGE(NLA_S32, -255, 0), |
8275 | [NL80211_MESHCONF_HT_OPMODE] = { .type = NLA_U16 }, |
8276 | [NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT] = { .type = NLA_U32 }, |
8277 | [NL80211_MESHCONF_HWMP_ROOT_INTERVAL] = |
8278 | NLA_POLICY_MIN(NLA_U16, 1), |
8279 | [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = |
8280 | NLA_POLICY_MIN(NLA_U16, 1), |
8281 | [NL80211_MESHCONF_POWER_MODE] = |
8282 | NLA_POLICY_RANGE(NLA_U32, |
8283 | NL80211_MESH_POWER_ACTIVE, |
8284 | NL80211_MESH_POWER_MAX), |
8285 | [NL80211_MESHCONF_AWAKE_WINDOW] = { .type = NLA_U16 }, |
8286 | [NL80211_MESHCONF_PLINK_TIMEOUT] = { .type = NLA_U32 }, |
8287 | [NL80211_MESHCONF_CONNECTED_TO_GATE] = NLA_POLICY_RANGE(NLA_U8, 0, 1), |
8288 | [NL80211_MESHCONF_NOLEARN] = NLA_POLICY_RANGE(NLA_U8, 0, 1), |
8289 | [NL80211_MESHCONF_CONNECTED_TO_AS] = NLA_POLICY_RANGE(NLA_U8, 0, 1), |
8290 | }; |
8291 | |
8292 | static const struct nla_policy |
8293 | nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = { |
8294 | [NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC] = { .type = NLA_U8 }, |
8295 | [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 }, |
8296 | [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 }, |
8297 | [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG }, |
8298 | [NL80211_MESH_SETUP_AUTH_PROTOCOL] = { .type = NLA_U8 }, |
8299 | [NL80211_MESH_SETUP_USERSPACE_MPM] = { .type = NLA_FLAG }, |
8300 | [NL80211_MESH_SETUP_IE] = |
8301 | NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_ie_attr, |
8302 | IEEE80211_MAX_DATA_LEN), |
8303 | [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG }, |
8304 | }; |
8305 | |
8306 | static int nl80211_parse_mesh_config(struct genl_info *info, |
8307 | struct mesh_config *cfg, |
8308 | u32 *mask_out) |
8309 | { |
8310 | struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1]; |
8311 | u32 mask = 0; |
8312 | u16 ht_opmode; |
8313 | |
8314 | #define FILL_IN_MESH_PARAM_IF_SET(tb, cfg, param, mask, attr, fn) \ |
8315 | do { \ |
8316 | if (tb[attr]) { \ |
8317 | cfg->param = fn(tb[attr]); \ |
8318 | mask |= BIT((attr) - 1); \ |
8319 | } \ |
8320 | } while (0) |
8321 | |
8322 | if (!info->attrs[NL80211_ATTR_MESH_CONFIG]) |
8323 | return -EINVAL; |
8324 | 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)) |
8325 | return -EINVAL; |
8326 | |
8327 | /* This makes sure that there aren't more than 32 mesh config |
8328 | * parameters (otherwise our bitfield scheme would not work.) */ |
8329 | BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32); |
8330 | |
8331 | /* Fill in the params struct */ |
8332 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, mask, |
8333 | NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16); |
8334 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, mask, |
8335 | NL80211_MESHCONF_CONFIRM_TIMEOUT, |
8336 | nla_get_u16); |
8337 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, mask, |
8338 | NL80211_MESHCONF_HOLDING_TIMEOUT, |
8339 | nla_get_u16); |
8340 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, mask, |
8341 | NL80211_MESHCONF_MAX_PEER_LINKS, |
8342 | nla_get_u16); |
8343 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, mask, |
8344 | NL80211_MESHCONF_MAX_RETRIES, nla_get_u8); |
8345 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, mask, |
8346 | NL80211_MESHCONF_TTL, nla_get_u8); |
8347 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, mask, |
8348 | NL80211_MESHCONF_ELEMENT_TTL, nla_get_u8); |
8349 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, mask, |
8350 | NL80211_MESHCONF_AUTO_OPEN_PLINKS, |
8351 | nla_get_u8); |
8352 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor, |
8353 | mask, |
8354 | NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, |
8355 | nla_get_u32); |
8356 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, mask, |
8357 | NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, |
8358 | nla_get_u8); |
8359 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, mask, |
8360 | NL80211_MESHCONF_PATH_REFRESH_TIME, |
8361 | nla_get_u32); |
8362 | if (mask & BIT(NL80211_MESHCONF_PATH_REFRESH_TIME) && |
8363 | (cfg->path_refresh_time < 1 || cfg->path_refresh_time > 65535)) |
8364 | return -EINVAL; |
8365 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, mask, |
8366 | NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, |
8367 | nla_get_u16); |
8368 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, |
8369 | mask, |
8370 | NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, |
8371 | nla_get_u32); |
8372 | if (mask & BIT(NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT) && |
8373 | (cfg->dot11MeshHWMPactivePathTimeout < 1 || |
8374 | cfg->dot11MeshHWMPactivePathTimeout > 65535)) |
8375 | return -EINVAL; |
8376 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval, mask, |
8377 | NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, |
8378 | nla_get_u16); |
8379 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval, mask, |
8380 | NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL, |
8381 | nla_get_u16); |
8382 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, |
8383 | dot11MeshHWMPnetDiameterTraversalTime, mask, |
8384 | NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, |
8385 | nla_get_u16); |
8386 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, mask, |
8387 | NL80211_MESHCONF_HWMP_ROOTMODE, nla_get_u8); |
8388 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, mask, |
8389 | NL80211_MESHCONF_HWMP_RANN_INTERVAL, |
8390 | nla_get_u16); |
8391 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshGateAnnouncementProtocol, |
8392 | mask, NL80211_MESHCONF_GATE_ANNOUNCEMENTS, |
8393 | nla_get_u8); |
8394 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding, mask, |
8395 | NL80211_MESHCONF_FORWARDING, nla_get_u8); |
8396 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold, mask, |
8397 | NL80211_MESHCONF_RSSI_THRESHOLD, |
8398 | nla_get_s32); |
8399 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConnectedToMeshGate, mask, |
8400 | NL80211_MESHCONF_CONNECTED_TO_GATE, |
8401 | nla_get_u8); |
8402 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConnectedToAuthServer, mask, |
8403 | NL80211_MESHCONF_CONNECTED_TO_AS, |
8404 | nla_get_u8); |
8405 | /* |
8406 | * Check HT operation mode based on |
8407 | * IEEE 802.11-2016 9.4.2.57 HT Operation element. |
8408 | */ |
8409 | if (tb[NL80211_MESHCONF_HT_OPMODE]) { |
8410 | ht_opmode = nla_get_u16(nla: tb[NL80211_MESHCONF_HT_OPMODE]); |
8411 | |
8412 | if (ht_opmode & ~(IEEE80211_HT_OP_MODE_PROTECTION | |
8413 | IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT | |
8414 | IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT)) |
8415 | return -EINVAL; |
8416 | |
8417 | /* NON_HT_STA bit is reserved, but some programs set it */ |
8418 | ht_opmode &= ~IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT; |
8419 | |
8420 | cfg->ht_opmode = ht_opmode; |
8421 | mask |= (1 << (NL80211_MESHCONF_HT_OPMODE - 1)); |
8422 | } |
8423 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, |
8424 | dot11MeshHWMPactivePathToRootTimeout, mask, |
8425 | NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, |
8426 | nla_get_u32); |
8427 | if (mask & BIT(NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT) && |
8428 | (cfg->dot11MeshHWMPactivePathToRootTimeout < 1 || |
8429 | cfg->dot11MeshHWMPactivePathToRootTimeout > 65535)) |
8430 | return -EINVAL; |
8431 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval, mask, |
8432 | NL80211_MESHCONF_HWMP_ROOT_INTERVAL, |
8433 | nla_get_u16); |
8434 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPconfirmationInterval, |
8435 | mask, |
8436 | NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, |
8437 | nla_get_u16); |
8438 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, power_mode, mask, |
8439 | NL80211_MESHCONF_POWER_MODE, nla_get_u32); |
8440 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration, mask, |
8441 | NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16); |
8442 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, mask, |
8443 | NL80211_MESHCONF_PLINK_TIMEOUT, nla_get_u32); |
8444 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNolearn, mask, |
8445 | NL80211_MESHCONF_NOLEARN, nla_get_u8); |
8446 | if (mask_out) |
8447 | *mask_out = mask; |
8448 | |
8449 | return 0; |
8450 | |
8451 | #undef FILL_IN_MESH_PARAM_IF_SET |
8452 | } |
8453 | |
8454 | static int nl80211_parse_mesh_setup(struct genl_info *info, |
8455 | struct mesh_setup *setup) |
8456 | { |
8457 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
8458 | struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1]; |
8459 | |
8460 | if (!info->attrs[NL80211_ATTR_MESH_SETUP]) |
8461 | return -EINVAL; |
8462 | 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)) |
8463 | return -EINVAL; |
8464 | |
8465 | if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC]) |
8466 | setup->sync_method = |
8467 | (nla_get_u8(nla: tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])) ? |
8468 | IEEE80211_SYNC_METHOD_VENDOR : |
8469 | IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET; |
8470 | |
8471 | if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL]) |
8472 | setup->path_sel_proto = |
8473 | (nla_get_u8(nla: tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ? |
8474 | IEEE80211_PATH_PROTOCOL_VENDOR : |
8475 | IEEE80211_PATH_PROTOCOL_HWMP; |
8476 | |
8477 | if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC]) |
8478 | setup->path_metric = |
8479 | (nla_get_u8(nla: tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ? |
8480 | IEEE80211_PATH_METRIC_VENDOR : |
8481 | IEEE80211_PATH_METRIC_AIRTIME; |
8482 | |
8483 | if (tb[NL80211_MESH_SETUP_IE]) { |
8484 | struct nlattr *ieattr = |
8485 | tb[NL80211_MESH_SETUP_IE]; |
8486 | setup->ie = nla_data(nla: ieattr); |
8487 | setup->ie_len = nla_len(nla: ieattr); |
8488 | } |
8489 | if (tb[NL80211_MESH_SETUP_USERSPACE_MPM] && |
8490 | !(rdev->wiphy.features & NL80211_FEATURE_USERSPACE_MPM)) |
8491 | return -EINVAL; |
8492 | setup->user_mpm = nla_get_flag(nla: tb[NL80211_MESH_SETUP_USERSPACE_MPM]); |
8493 | setup->is_authenticated = nla_get_flag(nla: tb[NL80211_MESH_SETUP_USERSPACE_AUTH]); |
8494 | setup->is_secure = nla_get_flag(nla: tb[NL80211_MESH_SETUP_USERSPACE_AMPE]); |
8495 | if (setup->is_secure) |
8496 | setup->user_mpm = true; |
8497 | |
8498 | if (tb[NL80211_MESH_SETUP_AUTH_PROTOCOL]) { |
8499 | if (!setup->user_mpm) |
8500 | return -EINVAL; |
8501 | setup->auth_id = |
8502 | nla_get_u8(nla: tb[NL80211_MESH_SETUP_AUTH_PROTOCOL]); |
8503 | } |
8504 | |
8505 | return 0; |
8506 | } |
8507 | |
8508 | static int nl80211_update_mesh_config(struct sk_buff *skb, |
8509 | struct genl_info *info) |
8510 | { |
8511 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
8512 | struct net_device *dev = info->user_ptr[1]; |
8513 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
8514 | struct mesh_config cfg = {}; |
8515 | u32 mask; |
8516 | int err; |
8517 | |
8518 | if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) |
8519 | return -EOPNOTSUPP; |
8520 | |
8521 | if (!rdev->ops->update_mesh_config) |
8522 | return -EOPNOTSUPP; |
8523 | |
8524 | err = nl80211_parse_mesh_config(info, cfg: &cfg, mask_out: &mask); |
8525 | if (err) |
8526 | return err; |
8527 | |
8528 | if (!wdev->u.mesh.id_len) |
8529 | err = -ENOLINK; |
8530 | |
8531 | if (!err) |
8532 | err = rdev_update_mesh_config(rdev, dev, mask, nconf: &cfg); |
8533 | |
8534 | return err; |
8535 | } |
8536 | |
8537 | static int nl80211_put_regdom(const struct ieee80211_regdomain *regdom, |
8538 | struct sk_buff *msg) |
8539 | { |
8540 | struct nlattr *nl_reg_rules; |
8541 | unsigned int i; |
8542 | |
8543 | if (nla_put_string(skb: msg, attrtype: NL80211_ATTR_REG_ALPHA2, str: regdom->alpha2) || |
8544 | (regdom->dfs_region && |
8545 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_DFS_REGION, value: regdom->dfs_region))) |
8546 | goto nla_put_failure; |
8547 | |
8548 | nl_reg_rules = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_REG_RULES); |
8549 | if (!nl_reg_rules) |
8550 | goto nla_put_failure; |
8551 | |
8552 | for (i = 0; i < regdom->n_reg_rules; i++) { |
8553 | struct nlattr *nl_reg_rule; |
8554 | const struct ieee80211_reg_rule *reg_rule; |
8555 | const struct ieee80211_freq_range *freq_range; |
8556 | const struct ieee80211_power_rule *power_rule; |
8557 | unsigned int max_bandwidth_khz; |
8558 | |
8559 | reg_rule = ®dom->reg_rules[i]; |
8560 | freq_range = ®_rule->freq_range; |
8561 | power_rule = ®_rule->power_rule; |
8562 | |
8563 | nl_reg_rule = nla_nest_start_noflag(skb: msg, attrtype: i); |
8564 | if (!nl_reg_rule) |
8565 | goto nla_put_failure; |
8566 | |
8567 | max_bandwidth_khz = freq_range->max_bandwidth_khz; |
8568 | if (!max_bandwidth_khz) |
8569 | max_bandwidth_khz = reg_get_max_bandwidth(rd: regdom, |
8570 | rule: reg_rule); |
8571 | |
8572 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_REG_RULE_FLAGS, |
8573 | value: reg_rule->flags) || |
8574 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_FREQ_RANGE_START, |
8575 | value: freq_range->start_freq_khz) || |
8576 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_FREQ_RANGE_END, |
8577 | value: freq_range->end_freq_khz) || |
8578 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_FREQ_RANGE_MAX_BW, |
8579 | value: max_bandwidth_khz) || |
8580 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, |
8581 | value: power_rule->max_antenna_gain) || |
8582 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_POWER_RULE_MAX_EIRP, |
8583 | value: power_rule->max_eirp) || |
8584 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_DFS_CAC_TIME, |
8585 | value: reg_rule->dfs_cac_ms)) |
8586 | goto nla_put_failure; |
8587 | |
8588 | if ((reg_rule->flags & NL80211_RRF_PSD) && |
8589 | nla_put_s8(skb: msg, attrtype: NL80211_ATTR_POWER_RULE_PSD, |
8590 | value: reg_rule->psd)) |
8591 | goto nla_put_failure; |
8592 | |
8593 | nla_nest_end(skb: msg, start: nl_reg_rule); |
8594 | } |
8595 | |
8596 | nla_nest_end(skb: msg, start: nl_reg_rules); |
8597 | return 0; |
8598 | |
8599 | nla_put_failure: |
8600 | return -EMSGSIZE; |
8601 | } |
8602 | |
8603 | static int nl80211_get_reg_do(struct sk_buff *skb, struct genl_info *info) |
8604 | { |
8605 | const struct ieee80211_regdomain *regdom = NULL; |
8606 | struct cfg80211_registered_device *rdev; |
8607 | struct wiphy *wiphy = NULL; |
8608 | struct sk_buff *msg; |
8609 | int err = -EMSGSIZE; |
8610 | void *hdr; |
8611 | |
8612 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
8613 | if (!msg) |
8614 | return -ENOBUFS; |
8615 | |
8616 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
8617 | cmd: NL80211_CMD_GET_REG); |
8618 | if (!hdr) |
8619 | goto put_failure; |
8620 | |
8621 | rtnl_lock(); |
8622 | |
8623 | if (info->attrs[NL80211_ATTR_WIPHY]) { |
8624 | bool self_managed; |
8625 | |
8626 | rdev = cfg80211_get_dev_from_info(netns: genl_info_net(info), info); |
8627 | if (IS_ERR(ptr: rdev)) { |
8628 | err = PTR_ERR(ptr: rdev); |
8629 | goto nla_put_failure; |
8630 | } |
8631 | |
8632 | wiphy = &rdev->wiphy; |
8633 | self_managed = wiphy->regulatory_flags & |
8634 | REGULATORY_WIPHY_SELF_MANAGED; |
8635 | |
8636 | rcu_read_lock(); |
8637 | |
8638 | regdom = get_wiphy_regdom(wiphy); |
8639 | |
8640 | /* a self-managed-reg device must have a private regdom */ |
8641 | if (WARN_ON(!regdom && self_managed)) { |
8642 | err = -EINVAL; |
8643 | goto nla_put_failure_rcu; |
8644 | } |
8645 | |
8646 | if (regdom && |
8647 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: get_wiphy_idx(wiphy))) |
8648 | goto nla_put_failure_rcu; |
8649 | } else { |
8650 | rcu_read_lock(); |
8651 | } |
8652 | |
8653 | if (!wiphy && reg_last_request_cell_base() && |
8654 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_USER_REG_HINT_TYPE, |
8655 | value: NL80211_USER_REG_HINT_CELL_BASE)) |
8656 | goto nla_put_failure_rcu; |
8657 | |
8658 | if (!regdom) |
8659 | regdom = rcu_dereference(cfg80211_regdomain); |
8660 | |
8661 | if (nl80211_put_regdom(regdom, msg)) |
8662 | goto nla_put_failure_rcu; |
8663 | |
8664 | rcu_read_unlock(); |
8665 | |
8666 | genlmsg_end(skb: msg, hdr); |
8667 | rtnl_unlock(); |
8668 | return genlmsg_reply(skb: msg, info); |
8669 | |
8670 | nla_put_failure_rcu: |
8671 | rcu_read_unlock(); |
8672 | nla_put_failure: |
8673 | rtnl_unlock(); |
8674 | put_failure: |
8675 | nlmsg_free(skb: msg); |
8676 | return err; |
8677 | } |
8678 | |
8679 | static int nl80211_send_regdom(struct sk_buff *msg, struct netlink_callback *cb, |
8680 | u32 seq, int flags, struct wiphy *wiphy, |
8681 | const struct ieee80211_regdomain *regdom) |
8682 | { |
8683 | void *hdr = nl80211hdr_put(skb: msg, NETLINK_CB(cb->skb).portid, seq, flags, |
8684 | cmd: NL80211_CMD_GET_REG); |
8685 | |
8686 | if (!hdr) |
8687 | return -1; |
8688 | |
8689 | genl_dump_check_consistent(cb, user_hdr: hdr); |
8690 | |
8691 | if (nl80211_put_regdom(regdom, msg)) |
8692 | goto nla_put_failure; |
8693 | |
8694 | if (!wiphy && reg_last_request_cell_base() && |
8695 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_USER_REG_HINT_TYPE, |
8696 | value: NL80211_USER_REG_HINT_CELL_BASE)) |
8697 | goto nla_put_failure; |
8698 | |
8699 | if (wiphy && |
8700 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: get_wiphy_idx(wiphy))) |
8701 | goto nla_put_failure; |
8702 | |
8703 | if (wiphy && wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED && |
8704 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_WIPHY_SELF_MANAGED_REG)) |
8705 | goto nla_put_failure; |
8706 | |
8707 | genlmsg_end(skb: msg, hdr); |
8708 | return 0; |
8709 | |
8710 | nla_put_failure: |
8711 | genlmsg_cancel(skb: msg, hdr); |
8712 | return -EMSGSIZE; |
8713 | } |
8714 | |
8715 | static int nl80211_get_reg_dump(struct sk_buff *skb, |
8716 | struct netlink_callback *cb) |
8717 | { |
8718 | const struct ieee80211_regdomain *regdom = NULL; |
8719 | struct cfg80211_registered_device *rdev; |
8720 | int err, reg_idx, start = cb->args[2]; |
8721 | |
8722 | rcu_read_lock(); |
8723 | |
8724 | if (cfg80211_regdomain && start == 0) { |
8725 | err = nl80211_send_regdom(msg: skb, cb, seq: cb->nlh->nlmsg_seq, |
8726 | NLM_F_MULTI, NULL, |
8727 | rcu_dereference(cfg80211_regdomain)); |
8728 | if (err < 0) |
8729 | goto out_err; |
8730 | } |
8731 | |
8732 | /* the global regdom is idx 0 */ |
8733 | reg_idx = 1; |
8734 | list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { |
8735 | regdom = get_wiphy_regdom(wiphy: &rdev->wiphy); |
8736 | if (!regdom) |
8737 | continue; |
8738 | |
8739 | if (++reg_idx <= start) |
8740 | continue; |
8741 | |
8742 | err = nl80211_send_regdom(msg: skb, cb, seq: cb->nlh->nlmsg_seq, |
8743 | NLM_F_MULTI, wiphy: &rdev->wiphy, regdom); |
8744 | if (err < 0) { |
8745 | reg_idx--; |
8746 | break; |
8747 | } |
8748 | } |
8749 | |
8750 | cb->args[2] = reg_idx; |
8751 | err = skb->len; |
8752 | out_err: |
8753 | rcu_read_unlock(); |
8754 | return err; |
8755 | } |
8756 | |
8757 | #ifdef CONFIG_CFG80211_CRDA_SUPPORT |
8758 | static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { |
8759 | [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, |
8760 | [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, |
8761 | [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, |
8762 | [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 }, |
8763 | [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 }, |
8764 | [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 }, |
8765 | [NL80211_ATTR_DFS_CAC_TIME] = { .type = NLA_U32 }, |
8766 | }; |
8767 | |
8768 | static int parse_reg_rule(struct nlattr *tb[], |
8769 | struct ieee80211_reg_rule *reg_rule) |
8770 | { |
8771 | struct ieee80211_freq_range *freq_range = ®_rule->freq_range; |
8772 | struct ieee80211_power_rule *power_rule = ®_rule->power_rule; |
8773 | |
8774 | if (!tb[NL80211_ATTR_REG_RULE_FLAGS]) |
8775 | return -EINVAL; |
8776 | if (!tb[NL80211_ATTR_FREQ_RANGE_START]) |
8777 | return -EINVAL; |
8778 | if (!tb[NL80211_ATTR_FREQ_RANGE_END]) |
8779 | return -EINVAL; |
8780 | if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) |
8781 | return -EINVAL; |
8782 | if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]) |
8783 | return -EINVAL; |
8784 | |
8785 | reg_rule->flags = nla_get_u32(nla: tb[NL80211_ATTR_REG_RULE_FLAGS]); |
8786 | |
8787 | freq_range->start_freq_khz = |
8788 | nla_get_u32(nla: tb[NL80211_ATTR_FREQ_RANGE_START]); |
8789 | freq_range->end_freq_khz = |
8790 | nla_get_u32(nla: tb[NL80211_ATTR_FREQ_RANGE_END]); |
8791 | freq_range->max_bandwidth_khz = |
8792 | nla_get_u32(nla: tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]); |
8793 | |
8794 | power_rule->max_eirp = |
8795 | nla_get_u32(nla: tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]); |
8796 | |
8797 | if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]) |
8798 | power_rule->max_antenna_gain = |
8799 | nla_get_u32(nla: tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]); |
8800 | |
8801 | if (tb[NL80211_ATTR_DFS_CAC_TIME]) |
8802 | reg_rule->dfs_cac_ms = |
8803 | nla_get_u32(nla: tb[NL80211_ATTR_DFS_CAC_TIME]); |
8804 | |
8805 | return 0; |
8806 | } |
8807 | |
8808 | static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) |
8809 | { |
8810 | struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; |
8811 | struct nlattr *nl_reg_rule; |
8812 | char *alpha2; |
8813 | int rem_reg_rules, r; |
8814 | u32 num_rules = 0, rule_idx = 0; |
8815 | enum nl80211_dfs_regions dfs_region = NL80211_DFS_UNSET; |
8816 | struct ieee80211_regdomain *rd; |
8817 | |
8818 | if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) |
8819 | return -EINVAL; |
8820 | |
8821 | if (!info->attrs[NL80211_ATTR_REG_RULES]) |
8822 | return -EINVAL; |
8823 | |
8824 | alpha2 = nla_data(nla: info->attrs[NL80211_ATTR_REG_ALPHA2]); |
8825 | |
8826 | if (info->attrs[NL80211_ATTR_DFS_REGION]) |
8827 | dfs_region = nla_get_u8(nla: info->attrs[NL80211_ATTR_DFS_REGION]); |
8828 | |
8829 | nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], |
8830 | rem_reg_rules) { |
8831 | num_rules++; |
8832 | if (num_rules > NL80211_MAX_SUPP_REG_RULES) |
8833 | return -EINVAL; |
8834 | } |
8835 | |
8836 | rtnl_lock(); |
8837 | if (!reg_is_valid_request(alpha2)) { |
8838 | r = -EINVAL; |
8839 | goto out; |
8840 | } |
8841 | |
8842 | rd = kzalloc(struct_size(rd, reg_rules, num_rules), GFP_KERNEL); |
8843 | if (!rd) { |
8844 | r = -ENOMEM; |
8845 | goto out; |
8846 | } |
8847 | |
8848 | rd->n_reg_rules = num_rules; |
8849 | rd->alpha2[0] = alpha2[0]; |
8850 | rd->alpha2[1] = alpha2[1]; |
8851 | |
8852 | /* |
8853 | * Disable DFS master mode if the DFS region was |
8854 | * not supported or known on this kernel. |
8855 | */ |
8856 | if (reg_supported_dfs_region(dfs_region)) |
8857 | rd->dfs_region = dfs_region; |
8858 | |
8859 | nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], |
8860 | rem_reg_rules) { |
8861 | r = nla_parse_nested_deprecated(tb, maxtype: NL80211_REG_RULE_ATTR_MAX, |
8862 | nla: nl_reg_rule, policy: reg_rule_policy, |
8863 | extack: info->extack); |
8864 | if (r) |
8865 | goto bad_reg; |
8866 | r = parse_reg_rule(tb, reg_rule: &rd->reg_rules[rule_idx]); |
8867 | if (r) |
8868 | goto bad_reg; |
8869 | |
8870 | rule_idx++; |
8871 | |
8872 | if (rule_idx > NL80211_MAX_SUPP_REG_RULES) { |
8873 | r = -EINVAL; |
8874 | goto bad_reg; |
8875 | } |
8876 | } |
8877 | |
8878 | r = set_regdom(rd, regd_src: REGD_SOURCE_CRDA); |
8879 | /* set_regdom takes ownership of rd */ |
8880 | rd = NULL; |
8881 | bad_reg: |
8882 | kfree(objp: rd); |
8883 | out: |
8884 | rtnl_unlock(); |
8885 | return r; |
8886 | } |
8887 | #endif /* CONFIG_CFG80211_CRDA_SUPPORT */ |
8888 | |
8889 | static int validate_scan_freqs(struct nlattr *freqs) |
8890 | { |
8891 | struct nlattr *attr1, *attr2; |
8892 | int n_channels = 0, tmp1, tmp2; |
8893 | |
8894 | nla_for_each_nested(attr1, freqs, tmp1) |
8895 | if (nla_len(nla: attr1) != sizeof(u32)) |
8896 | return 0; |
8897 | |
8898 | nla_for_each_nested(attr1, freqs, tmp1) { |
8899 | n_channels++; |
8900 | /* |
8901 | * Some hardware has a limited channel list for |
8902 | * scanning, and it is pretty much nonsensical |
8903 | * to scan for a channel twice, so disallow that |
8904 | * and don't require drivers to check that the |
8905 | * channel list they get isn't longer than what |
8906 | * they can scan, as long as they can scan all |
8907 | * the channels they registered at once. |
8908 | */ |
8909 | nla_for_each_nested(attr2, freqs, tmp2) |
8910 | if (attr1 != attr2 && |
8911 | nla_get_u32(nla: attr1) == nla_get_u32(nla: attr2)) |
8912 | return 0; |
8913 | } |
8914 | |
8915 | return n_channels; |
8916 | } |
8917 | |
8918 | static bool is_band_valid(struct wiphy *wiphy, enum nl80211_band b) |
8919 | { |
8920 | return b < NUM_NL80211_BANDS && wiphy->bands[b]; |
8921 | } |
8922 | |
8923 | static int parse_bss_select(struct nlattr *nla, struct wiphy *wiphy, |
8924 | struct cfg80211_bss_selection *bss_select) |
8925 | { |
8926 | struct nlattr *attr[NL80211_BSS_SELECT_ATTR_MAX + 1]; |
8927 | struct nlattr *nest; |
8928 | int err; |
8929 | bool found = false; |
8930 | int i; |
8931 | |
8932 | /* only process one nested attribute */ |
8933 | nest = nla_data(nla); |
8934 | if (!nla_ok(nla: nest, remaining: nla_len(nla: nest))) |
8935 | return -EINVAL; |
8936 | |
8937 | err = nla_parse_nested_deprecated(tb: attr, maxtype: NL80211_BSS_SELECT_ATTR_MAX, |
8938 | nla: nest, policy: nl80211_bss_select_policy, |
8939 | NULL); |
8940 | if (err) |
8941 | return err; |
8942 | |
8943 | /* only one attribute may be given */ |
8944 | for (i = 0; i <= NL80211_BSS_SELECT_ATTR_MAX; i++) { |
8945 | if (attr[i]) { |
8946 | if (found) |
8947 | return -EINVAL; |
8948 | found = true; |
8949 | } |
8950 | } |
8951 | |
8952 | bss_select->behaviour = __NL80211_BSS_SELECT_ATTR_INVALID; |
8953 | |
8954 | if (attr[NL80211_BSS_SELECT_ATTR_RSSI]) |
8955 | bss_select->behaviour = NL80211_BSS_SELECT_ATTR_RSSI; |
8956 | |
8957 | if (attr[NL80211_BSS_SELECT_ATTR_BAND_PREF]) { |
8958 | bss_select->behaviour = NL80211_BSS_SELECT_ATTR_BAND_PREF; |
8959 | bss_select->param.band_pref = |
8960 | nla_get_u32(nla: attr[NL80211_BSS_SELECT_ATTR_BAND_PREF]); |
8961 | if (!is_band_valid(wiphy, b: bss_select->param.band_pref)) |
8962 | return -EINVAL; |
8963 | } |
8964 | |
8965 | if (attr[NL80211_BSS_SELECT_ATTR_RSSI_ADJUST]) { |
8966 | struct nl80211_bss_select_rssi_adjust *adj_param; |
8967 | |
8968 | adj_param = nla_data(nla: attr[NL80211_BSS_SELECT_ATTR_RSSI_ADJUST]); |
8969 | bss_select->behaviour = NL80211_BSS_SELECT_ATTR_RSSI_ADJUST; |
8970 | bss_select->param.adjust.band = adj_param->band; |
8971 | bss_select->param.adjust.delta = adj_param->delta; |
8972 | if (!is_band_valid(wiphy, b: bss_select->param.adjust.band)) |
8973 | return -EINVAL; |
8974 | } |
8975 | |
8976 | /* user-space did not provide behaviour attribute */ |
8977 | if (bss_select->behaviour == __NL80211_BSS_SELECT_ATTR_INVALID) |
8978 | return -EINVAL; |
8979 | |
8980 | if (!(wiphy->bss_select_support & BIT(bss_select->behaviour))) |
8981 | return -EINVAL; |
8982 | |
8983 | return 0; |
8984 | } |
8985 | |
8986 | int nl80211_parse_random_mac(struct nlattr **attrs, |
8987 | u8 *mac_addr, u8 *mac_addr_mask) |
8988 | { |
8989 | int i; |
8990 | |
8991 | if (!attrs[NL80211_ATTR_MAC] && !attrs[NL80211_ATTR_MAC_MASK]) { |
8992 | eth_zero_addr(addr: mac_addr); |
8993 | eth_zero_addr(addr: mac_addr_mask); |
8994 | mac_addr[0] = 0x2; |
8995 | mac_addr_mask[0] = 0x3; |
8996 | |
8997 | return 0; |
8998 | } |
8999 | |
9000 | /* need both or none */ |
9001 | if (!attrs[NL80211_ATTR_MAC] || !attrs[NL80211_ATTR_MAC_MASK]) |
9002 | return -EINVAL; |
9003 | |
9004 | memcpy(mac_addr, nla_data(attrs[NL80211_ATTR_MAC]), ETH_ALEN); |
9005 | memcpy(mac_addr_mask, nla_data(attrs[NL80211_ATTR_MAC_MASK]), ETH_ALEN); |
9006 | |
9007 | /* don't allow or configure an mcast address */ |
9008 | if (!is_multicast_ether_addr(addr: mac_addr_mask) || |
9009 | is_multicast_ether_addr(addr: mac_addr)) |
9010 | return -EINVAL; |
9011 | |
9012 | /* |
9013 | * allow users to pass a MAC address that has bits set outside |
9014 | * of the mask, but don't bother drivers with having to deal |
9015 | * with such bits |
9016 | */ |
9017 | for (i = 0; i < ETH_ALEN; i++) |
9018 | mac_addr[i] &= mac_addr_mask[i]; |
9019 | |
9020 | return 0; |
9021 | } |
9022 | |
9023 | static bool cfg80211_off_channel_oper_allowed(struct wireless_dev *wdev, |
9024 | struct ieee80211_channel *chan) |
9025 | { |
9026 | unsigned int link_id; |
9027 | bool all_ok = true; |
9028 | |
9029 | lockdep_assert_wiphy(wdev->wiphy); |
9030 | |
9031 | if (!cfg80211_beaconing_iface_active(wdev)) |
9032 | return true; |
9033 | |
9034 | /* |
9035 | * FIXME: check if we have a free HW resource/link for chan |
9036 | * |
9037 | * This, as well as the FIXME below, requires knowing the link |
9038 | * capabilities of the hardware. |
9039 | */ |
9040 | |
9041 | /* we cannot leave radar channels */ |
9042 | for_each_valid_link(wdev, link_id) { |
9043 | struct cfg80211_chan_def *chandef; |
9044 | |
9045 | chandef = wdev_chandef(wdev, link_id); |
9046 | if (!chandef || !chandef->chan) |
9047 | continue; |
9048 | |
9049 | /* |
9050 | * FIXME: don't require all_ok, but rather check only the |
9051 | * correct HW resource/link onto which 'chan' falls, |
9052 | * as only that link leaves the channel for doing |
9053 | * the off-channel operation. |
9054 | */ |
9055 | |
9056 | if (chandef->chan->flags & IEEE80211_CHAN_RADAR) |
9057 | all_ok = false; |
9058 | } |
9059 | |
9060 | if (all_ok) |
9061 | return true; |
9062 | |
9063 | return regulatory_pre_cac_allowed(wiphy: wdev->wiphy); |
9064 | } |
9065 | |
9066 | static bool nl80211_check_scan_feat(struct wiphy *wiphy, u32 flags, u32 flag, |
9067 | enum nl80211_ext_feature_index feat) |
9068 | { |
9069 | if (!(flags & flag)) |
9070 | return true; |
9071 | if (wiphy_ext_feature_isset(wiphy, ftidx: feat)) |
9072 | return true; |
9073 | return false; |
9074 | } |
9075 | |
9076 | static int |
9077 | nl80211_check_scan_flags(struct wiphy *wiphy, struct wireless_dev *wdev, |
9078 | void *request, struct nlattr **attrs, |
9079 | bool is_sched_scan) |
9080 | { |
9081 | u8 *mac_addr, *mac_addr_mask; |
9082 | u32 *flags; |
9083 | enum nl80211_feature_flags randomness_flag; |
9084 | |
9085 | if (!attrs[NL80211_ATTR_SCAN_FLAGS]) |
9086 | return 0; |
9087 | |
9088 | if (is_sched_scan) { |
9089 | struct cfg80211_sched_scan_request *req = request; |
9090 | |
9091 | randomness_flag = wdev ? |
9092 | NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR : |
9093 | NL80211_FEATURE_ND_RANDOM_MAC_ADDR; |
9094 | flags = &req->flags; |
9095 | mac_addr = req->mac_addr; |
9096 | mac_addr_mask = req->mac_addr_mask; |
9097 | } else { |
9098 | struct cfg80211_scan_request *req = request; |
9099 | |
9100 | randomness_flag = NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; |
9101 | flags = &req->flags; |
9102 | mac_addr = req->mac_addr; |
9103 | mac_addr_mask = req->mac_addr_mask; |
9104 | } |
9105 | |
9106 | *flags = nla_get_u32(nla: attrs[NL80211_ATTR_SCAN_FLAGS]); |
9107 | |
9108 | if (((*flags & NL80211_SCAN_FLAG_LOW_PRIORITY) && |
9109 | !(wiphy->features & NL80211_FEATURE_LOW_PRIORITY_SCAN)) || |
9110 | !nl80211_check_scan_feat(wiphy, flags: *flags, |
9111 | flag: NL80211_SCAN_FLAG_LOW_SPAN, |
9112 | feat: NL80211_EXT_FEATURE_LOW_SPAN_SCAN) || |
9113 | !nl80211_check_scan_feat(wiphy, flags: *flags, |
9114 | flag: NL80211_SCAN_FLAG_LOW_POWER, |
9115 | feat: NL80211_EXT_FEATURE_LOW_POWER_SCAN) || |
9116 | !nl80211_check_scan_feat(wiphy, flags: *flags, |
9117 | flag: NL80211_SCAN_FLAG_HIGH_ACCURACY, |
9118 | feat: NL80211_EXT_FEATURE_HIGH_ACCURACY_SCAN) || |
9119 | !nl80211_check_scan_feat(wiphy, flags: *flags, |
9120 | flag: NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME, |
9121 | feat: NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME) || |
9122 | !nl80211_check_scan_feat(wiphy, flags: *flags, |
9123 | flag: NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP, |
9124 | feat: NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP) || |
9125 | !nl80211_check_scan_feat(wiphy, flags: *flags, |
9126 | flag: NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION, |
9127 | feat: NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION) || |
9128 | !nl80211_check_scan_feat(wiphy, flags: *flags, |
9129 | flag: NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE, |
9130 | feat: NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE) || |
9131 | !nl80211_check_scan_feat(wiphy, flags: *flags, |
9132 | flag: NL80211_SCAN_FLAG_RANDOM_SN, |
9133 | feat: NL80211_EXT_FEATURE_SCAN_RANDOM_SN) || |
9134 | !nl80211_check_scan_feat(wiphy, flags: *flags, |
9135 | flag: NL80211_SCAN_FLAG_MIN_PREQ_CONTENT, |
9136 | feat: NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT)) |
9137 | return -EOPNOTSUPP; |
9138 | |
9139 | if (*flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { |
9140 | int err; |
9141 | |
9142 | if (!(wiphy->features & randomness_flag) || |
9143 | (wdev && wdev->connected)) |
9144 | return -EOPNOTSUPP; |
9145 | |
9146 | err = nl80211_parse_random_mac(attrs, mac_addr, mac_addr_mask); |
9147 | if (err) |
9148 | return err; |
9149 | } |
9150 | |
9151 | return 0; |
9152 | } |
9153 | |
9154 | static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) |
9155 | { |
9156 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
9157 | struct wireless_dev *wdev = info->user_ptr[1]; |
9158 | struct cfg80211_scan_request *request; |
9159 | struct nlattr *scan_freqs = NULL; |
9160 | bool scan_freqs_khz = false; |
9161 | struct nlattr *attr; |
9162 | struct wiphy *wiphy; |
9163 | int err, tmp, n_ssids = 0, n_channels, i; |
9164 | size_t ie_len, size; |
9165 | |
9166 | wiphy = &rdev->wiphy; |
9167 | |
9168 | if (wdev->iftype == NL80211_IFTYPE_NAN) |
9169 | return -EOPNOTSUPP; |
9170 | |
9171 | if (!rdev->ops->scan) |
9172 | return -EOPNOTSUPP; |
9173 | |
9174 | if (rdev->scan_req || rdev->scan_msg) |
9175 | return -EBUSY; |
9176 | |
9177 | if (info->attrs[NL80211_ATTR_SCAN_FREQ_KHZ]) { |
9178 | if (!wiphy_ext_feature_isset(wiphy, |
9179 | ftidx: NL80211_EXT_FEATURE_SCAN_FREQ_KHZ)) |
9180 | return -EOPNOTSUPP; |
9181 | scan_freqs = info->attrs[NL80211_ATTR_SCAN_FREQ_KHZ]; |
9182 | scan_freqs_khz = true; |
9183 | } else if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) |
9184 | scan_freqs = info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]; |
9185 | |
9186 | if (scan_freqs) { |
9187 | n_channels = validate_scan_freqs(freqs: scan_freqs); |
9188 | if (!n_channels) |
9189 | return -EINVAL; |
9190 | } else { |
9191 | n_channels = ieee80211_get_num_supported_channels(wiphy); |
9192 | } |
9193 | |
9194 | if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) |
9195 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) |
9196 | n_ssids++; |
9197 | |
9198 | if (n_ssids > wiphy->max_scan_ssids) |
9199 | return -EINVAL; |
9200 | |
9201 | if (info->attrs[NL80211_ATTR_IE]) |
9202 | ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); |
9203 | else |
9204 | ie_len = 0; |
9205 | |
9206 | if (ie_len > wiphy->max_scan_ie_len) |
9207 | return -EINVAL; |
9208 | |
9209 | size = struct_size(request, channels, n_channels); |
9210 | size = size_add(addend1: size, array_size(sizeof(*request->ssids), n_ssids)); |
9211 | size = size_add(addend1: size, addend2: ie_len); |
9212 | request = kzalloc(size, GFP_KERNEL); |
9213 | if (!request) |
9214 | return -ENOMEM; |
9215 | |
9216 | if (n_ssids) |
9217 | request->ssids = (void *)&request->channels[n_channels]; |
9218 | request->n_ssids = n_ssids; |
9219 | if (ie_len) { |
9220 | if (n_ssids) |
9221 | request->ie = (void *)(request->ssids + n_ssids); |
9222 | else |
9223 | request->ie = (void *)(request->channels + n_channels); |
9224 | } |
9225 | |
9226 | i = 0; |
9227 | if (scan_freqs) { |
9228 | /* user specified, bail out if channel not found */ |
9229 | nla_for_each_nested(attr, scan_freqs, tmp) { |
9230 | struct ieee80211_channel *chan; |
9231 | int freq = nla_get_u32(nla: attr); |
9232 | |
9233 | if (!scan_freqs_khz) |
9234 | freq = MHZ_TO_KHZ(freq); |
9235 | |
9236 | chan = ieee80211_get_channel_khz(wiphy, freq); |
9237 | if (!chan) { |
9238 | err = -EINVAL; |
9239 | goto out_free; |
9240 | } |
9241 | |
9242 | /* ignore disabled channels */ |
9243 | if (chan->flags & IEEE80211_CHAN_DISABLED) |
9244 | continue; |
9245 | |
9246 | request->channels[i] = chan; |
9247 | i++; |
9248 | } |
9249 | } else { |
9250 | enum nl80211_band band; |
9251 | |
9252 | /* all channels */ |
9253 | for (band = 0; band < NUM_NL80211_BANDS; band++) { |
9254 | int j; |
9255 | |
9256 | if (!wiphy->bands[band]) |
9257 | continue; |
9258 | for (j = 0; j < wiphy->bands[band]->n_channels; j++) { |
9259 | struct ieee80211_channel *chan; |
9260 | |
9261 | chan = &wiphy->bands[band]->channels[j]; |
9262 | |
9263 | if (chan->flags & IEEE80211_CHAN_DISABLED) |
9264 | continue; |
9265 | |
9266 | request->channels[i] = chan; |
9267 | i++; |
9268 | } |
9269 | } |
9270 | } |
9271 | |
9272 | if (!i) { |
9273 | err = -EINVAL; |
9274 | goto out_free; |
9275 | } |
9276 | |
9277 | request->n_channels = i; |
9278 | |
9279 | for (i = 0; i < request->n_channels; i++) { |
9280 | struct ieee80211_channel *chan = request->channels[i]; |
9281 | |
9282 | /* if we can go off-channel to the target channel we're good */ |
9283 | if (cfg80211_off_channel_oper_allowed(wdev, chan)) |
9284 | continue; |
9285 | |
9286 | if (!cfg80211_wdev_on_sub_chan(wdev, chan, primary_only: true)) { |
9287 | err = -EBUSY; |
9288 | goto out_free; |
9289 | } |
9290 | } |
9291 | |
9292 | i = 0; |
9293 | if (n_ssids) { |
9294 | nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) { |
9295 | if (nla_len(nla: attr) > IEEE80211_MAX_SSID_LEN) { |
9296 | err = -EINVAL; |
9297 | goto out_free; |
9298 | } |
9299 | request->ssids[i].ssid_len = nla_len(nla: attr); |
9300 | memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr)); |
9301 | i++; |
9302 | } |
9303 | } |
9304 | |
9305 | if (info->attrs[NL80211_ATTR_IE]) { |
9306 | request->ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); |
9307 | memcpy((void *)request->ie, |
9308 | nla_data(info->attrs[NL80211_ATTR_IE]), |
9309 | request->ie_len); |
9310 | } |
9311 | |
9312 | for (i = 0; i < NUM_NL80211_BANDS; i++) |
9313 | if (wiphy->bands[i]) |
9314 | request->rates[i] = |
9315 | (1 << wiphy->bands[i]->n_bitrates) - 1; |
9316 | |
9317 | if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) { |
9318 | nla_for_each_nested(attr, |
9319 | info->attrs[NL80211_ATTR_SCAN_SUPP_RATES], |
9320 | tmp) { |
9321 | enum nl80211_band band = nla_type(nla: attr); |
9322 | |
9323 | if (band < 0 || band >= NUM_NL80211_BANDS) { |
9324 | err = -EINVAL; |
9325 | goto out_free; |
9326 | } |
9327 | |
9328 | if (!wiphy->bands[band]) |
9329 | continue; |
9330 | |
9331 | err = ieee80211_get_ratemask(sband: wiphy->bands[band], |
9332 | rates: nla_data(nla: attr), |
9333 | n_rates: nla_len(nla: attr), |
9334 | mask: &request->rates[band]); |
9335 | if (err) |
9336 | goto out_free; |
9337 | } |
9338 | } |
9339 | |
9340 | if (info->attrs[NL80211_ATTR_MEASUREMENT_DURATION]) { |
9341 | request->duration = |
9342 | nla_get_u16(nla: info->attrs[NL80211_ATTR_MEASUREMENT_DURATION]); |
9343 | request->duration_mandatory = |
9344 | nla_get_flag(nla: info->attrs[NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY]); |
9345 | } |
9346 | |
9347 | err = nl80211_check_scan_flags(wiphy, wdev, request, attrs: info->attrs, |
9348 | is_sched_scan: false); |
9349 | if (err) |
9350 | goto out_free; |
9351 | |
9352 | request->no_cck = |
9353 | nla_get_flag(nla: info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); |
9354 | |
9355 | /* Initial implementation used NL80211_ATTR_MAC to set the specific |
9356 | * BSSID to scan for. This was problematic because that same attribute |
9357 | * was already used for another purpose (local random MAC address). The |
9358 | * NL80211_ATTR_BSSID attribute was added to fix this. For backwards |
9359 | * compatibility with older userspace components, also use the |
9360 | * NL80211_ATTR_MAC value here if it can be determined to be used for |
9361 | * the specific BSSID use case instead of the random MAC address |
9362 | * (NL80211_ATTR_SCAN_FLAGS is used to enable random MAC address use). |
9363 | */ |
9364 | if (info->attrs[NL80211_ATTR_BSSID]) |
9365 | memcpy(request->bssid, |
9366 | nla_data(info->attrs[NL80211_ATTR_BSSID]), ETH_ALEN); |
9367 | else if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) && |
9368 | info->attrs[NL80211_ATTR_MAC]) |
9369 | memcpy(request->bssid, nla_data(info->attrs[NL80211_ATTR_MAC]), |
9370 | ETH_ALEN); |
9371 | else |
9372 | eth_broadcast_addr(addr: request->bssid); |
9373 | |
9374 | request->tsf_report_link_id = nl80211_link_id_or_invalid(attrs: info->attrs); |
9375 | request->wdev = wdev; |
9376 | request->wiphy = &rdev->wiphy; |
9377 | request->scan_start = jiffies; |
9378 | |
9379 | rdev->scan_req = request; |
9380 | err = cfg80211_scan(rdev); |
9381 | |
9382 | if (err) |
9383 | goto out_free; |
9384 | |
9385 | nl80211_send_scan_start(rdev, wdev); |
9386 | dev_hold(dev: wdev->netdev); |
9387 | |
9388 | return 0; |
9389 | |
9390 | out_free: |
9391 | rdev->scan_req = NULL; |
9392 | kfree(objp: request); |
9393 | |
9394 | return err; |
9395 | } |
9396 | |
9397 | static int nl80211_abort_scan(struct sk_buff *skb, struct genl_info *info) |
9398 | { |
9399 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
9400 | struct wireless_dev *wdev = info->user_ptr[1]; |
9401 | |
9402 | if (!rdev->ops->abort_scan) |
9403 | return -EOPNOTSUPP; |
9404 | |
9405 | if (rdev->scan_msg) |
9406 | return 0; |
9407 | |
9408 | if (!rdev->scan_req) |
9409 | return -ENOENT; |
9410 | |
9411 | rdev_abort_scan(rdev, wdev); |
9412 | return 0; |
9413 | } |
9414 | |
9415 | static int |
9416 | nl80211_parse_sched_scan_plans(struct wiphy *wiphy, int n_plans, |
9417 | struct cfg80211_sched_scan_request *request, |
9418 | struct nlattr **attrs) |
9419 | { |
9420 | int tmp, err, i = 0; |
9421 | struct nlattr *attr; |
9422 | |
9423 | if (!attrs[NL80211_ATTR_SCHED_SCAN_PLANS]) { |
9424 | u32 interval; |
9425 | |
9426 | /* |
9427 | * If scan plans are not specified, |
9428 | * %NL80211_ATTR_SCHED_SCAN_INTERVAL will be specified. In this |
9429 | * case one scan plan will be set with the specified scan |
9430 | * interval and infinite number of iterations. |
9431 | */ |
9432 | interval = nla_get_u32(nla: attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]); |
9433 | if (!interval) |
9434 | return -EINVAL; |
9435 | |
9436 | request->scan_plans[0].interval = |
9437 | DIV_ROUND_UP(interval, MSEC_PER_SEC); |
9438 | if (!request->scan_plans[0].interval) |
9439 | return -EINVAL; |
9440 | |
9441 | if (request->scan_plans[0].interval > |
9442 | wiphy->max_sched_scan_plan_interval) |
9443 | request->scan_plans[0].interval = |
9444 | wiphy->max_sched_scan_plan_interval; |
9445 | |
9446 | return 0; |
9447 | } |
9448 | |
9449 | nla_for_each_nested(attr, attrs[NL80211_ATTR_SCHED_SCAN_PLANS], tmp) { |
9450 | struct nlattr *plan[NL80211_SCHED_SCAN_PLAN_MAX + 1]; |
9451 | |
9452 | if (WARN_ON(i >= n_plans)) |
9453 | return -EINVAL; |
9454 | |
9455 | err = nla_parse_nested_deprecated(tb: plan, |
9456 | maxtype: NL80211_SCHED_SCAN_PLAN_MAX, |
9457 | nla: attr, policy: nl80211_plan_policy, |
9458 | NULL); |
9459 | if (err) |
9460 | return err; |
9461 | |
9462 | if (!plan[NL80211_SCHED_SCAN_PLAN_INTERVAL]) |
9463 | return -EINVAL; |
9464 | |
9465 | request->scan_plans[i].interval = |
9466 | nla_get_u32(nla: plan[NL80211_SCHED_SCAN_PLAN_INTERVAL]); |
9467 | if (!request->scan_plans[i].interval || |
9468 | request->scan_plans[i].interval > |
9469 | wiphy->max_sched_scan_plan_interval) |
9470 | return -EINVAL; |
9471 | |
9472 | if (plan[NL80211_SCHED_SCAN_PLAN_ITERATIONS]) { |
9473 | request->scan_plans[i].iterations = |
9474 | nla_get_u32(nla: plan[NL80211_SCHED_SCAN_PLAN_ITERATIONS]); |
9475 | if (!request->scan_plans[i].iterations || |
9476 | (request->scan_plans[i].iterations > |
9477 | wiphy->max_sched_scan_plan_iterations)) |
9478 | return -EINVAL; |
9479 | } else if (i < n_plans - 1) { |
9480 | /* |
9481 | * All scan plans but the last one must specify |
9482 | * a finite number of iterations |
9483 | */ |
9484 | return -EINVAL; |
9485 | } |
9486 | |
9487 | i++; |
9488 | } |
9489 | |
9490 | /* |
9491 | * The last scan plan must not specify the number of |
9492 | * iterations, it is supposed to run infinitely |
9493 | */ |
9494 | if (request->scan_plans[n_plans - 1].iterations) |
9495 | return -EINVAL; |
9496 | |
9497 | return 0; |
9498 | } |
9499 | |
9500 | static struct cfg80211_sched_scan_request * |
9501 | nl80211_parse_sched_scan(struct wiphy *wiphy, struct wireless_dev *wdev, |
9502 | struct nlattr **attrs, int max_match_sets) |
9503 | { |
9504 | struct cfg80211_sched_scan_request *request; |
9505 | struct nlattr *attr; |
9506 | int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i, n_plans = 0; |
9507 | enum nl80211_band band; |
9508 | size_t ie_len, size; |
9509 | struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1]; |
9510 | s32 = NL80211_SCAN_RSSI_THOLD_OFF; |
9511 | |
9512 | if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { |
9513 | n_channels = validate_scan_freqs( |
9514 | freqs: attrs[NL80211_ATTR_SCAN_FREQUENCIES]); |
9515 | if (!n_channels) |
9516 | return ERR_PTR(error: -EINVAL); |
9517 | } else { |
9518 | n_channels = ieee80211_get_num_supported_channels(wiphy); |
9519 | } |
9520 | |
9521 | if (attrs[NL80211_ATTR_SCAN_SSIDS]) |
9522 | nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS], |
9523 | tmp) |
9524 | n_ssids++; |
9525 | |
9526 | if (n_ssids > wiphy->max_sched_scan_ssids) |
9527 | return ERR_PTR(error: -EINVAL); |
9528 | |
9529 | /* |
9530 | * First, count the number of 'real' matchsets. Due to an issue with |
9531 | * the old implementation, matchsets containing only the RSSI attribute |
9532 | * (NL80211_SCHED_SCAN_MATCH_ATTR_RSSI) are considered as the 'default' |
9533 | * RSSI for all matchsets, rather than their own matchset for reporting |
9534 | * all APs with a strong RSSI. This is needed to be compatible with |
9535 | * older userspace that treated a matchset with only the RSSI as the |
9536 | * global RSSI for all other matchsets - if there are other matchsets. |
9537 | */ |
9538 | if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) { |
9539 | nla_for_each_nested(attr, |
9540 | attrs[NL80211_ATTR_SCHED_SCAN_MATCH], |
9541 | tmp) { |
9542 | struct nlattr *; |
9543 | |
9544 | err = nla_parse_nested_deprecated(tb, |
9545 | maxtype: NL80211_SCHED_SCAN_MATCH_ATTR_MAX, |
9546 | nla: attr, |
9547 | policy: nl80211_match_policy, |
9548 | NULL); |
9549 | if (err) |
9550 | return ERR_PTR(error: err); |
9551 | |
9552 | /* SSID and BSSID are mutually exclusive */ |
9553 | if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID] && |
9554 | tb[NL80211_SCHED_SCAN_MATCH_ATTR_BSSID]) |
9555 | return ERR_PTR(error: -EINVAL); |
9556 | |
9557 | /* add other standalone attributes here */ |
9558 | if (tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID] || |
9559 | tb[NL80211_SCHED_SCAN_MATCH_ATTR_BSSID]) { |
9560 | n_match_sets++; |
9561 | continue; |
9562 | } |
9563 | rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI]; |
9564 | if (rssi) |
9565 | default_match_rssi = nla_get_s32(nla: rssi); |
9566 | } |
9567 | } |
9568 | |
9569 | /* However, if there's no other matchset, add the RSSI one */ |
9570 | if (!n_match_sets && default_match_rssi != NL80211_SCAN_RSSI_THOLD_OFF) |
9571 | n_match_sets = 1; |
9572 | |
9573 | if (n_match_sets > max_match_sets) |
9574 | return ERR_PTR(error: -EINVAL); |
9575 | |
9576 | if (attrs[NL80211_ATTR_IE]) |
9577 | ie_len = nla_len(nla: attrs[NL80211_ATTR_IE]); |
9578 | else |
9579 | ie_len = 0; |
9580 | |
9581 | if (ie_len > wiphy->max_sched_scan_ie_len) |
9582 | return ERR_PTR(error: -EINVAL); |
9583 | |
9584 | if (attrs[NL80211_ATTR_SCHED_SCAN_PLANS]) { |
9585 | /* |
9586 | * NL80211_ATTR_SCHED_SCAN_INTERVAL must not be specified since |
9587 | * each scan plan already specifies its own interval |
9588 | */ |
9589 | if (attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]) |
9590 | return ERR_PTR(error: -EINVAL); |
9591 | |
9592 | nla_for_each_nested(attr, |
9593 | attrs[NL80211_ATTR_SCHED_SCAN_PLANS], tmp) |
9594 | n_plans++; |
9595 | } else { |
9596 | /* |
9597 | * The scan interval attribute is kept for backward |
9598 | * compatibility. If no scan plans are specified and sched scan |
9599 | * interval is specified, one scan plan will be set with this |
9600 | * scan interval and infinite number of iterations. |
9601 | */ |
9602 | if (!attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]) |
9603 | return ERR_PTR(error: -EINVAL); |
9604 | |
9605 | n_plans = 1; |
9606 | } |
9607 | |
9608 | if (!n_plans || n_plans > wiphy->max_sched_scan_plans) |
9609 | return ERR_PTR(error: -EINVAL); |
9610 | |
9611 | if (!wiphy_ext_feature_isset( |
9612 | wiphy, ftidx: NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI) && |
9613 | (attrs[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI] || |
9614 | attrs[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST])) |
9615 | return ERR_PTR(error: -EINVAL); |
9616 | |
9617 | size = struct_size(request, channels, n_channels); |
9618 | size = size_add(addend1: size, array_size(sizeof(*request->ssids), n_ssids)); |
9619 | size = size_add(addend1: size, array_size(sizeof(*request->match_sets), |
9620 | n_match_sets)); |
9621 | size = size_add(addend1: size, array_size(sizeof(*request->scan_plans), |
9622 | n_plans)); |
9623 | size = size_add(addend1: size, addend2: ie_len); |
9624 | request = kzalloc(size, GFP_KERNEL); |
9625 | if (!request) |
9626 | return ERR_PTR(error: -ENOMEM); |
9627 | |
9628 | if (n_ssids) |
9629 | request->ssids = (void *)&request->channels[n_channels]; |
9630 | request->n_ssids = n_ssids; |
9631 | if (ie_len) { |
9632 | if (n_ssids) |
9633 | request->ie = (void *)(request->ssids + n_ssids); |
9634 | else |
9635 | request->ie = (void *)(request->channels + n_channels); |
9636 | } |
9637 | |
9638 | if (n_match_sets) { |
9639 | if (request->ie) |
9640 | request->match_sets = (void *)(request->ie + ie_len); |
9641 | else if (n_ssids) |
9642 | request->match_sets = |
9643 | (void *)(request->ssids + n_ssids); |
9644 | else |
9645 | request->match_sets = |
9646 | (void *)(request->channels + n_channels); |
9647 | } |
9648 | request->n_match_sets = n_match_sets; |
9649 | |
9650 | if (n_match_sets) |
9651 | request->scan_plans = (void *)(request->match_sets + |
9652 | n_match_sets); |
9653 | else if (request->ie) |
9654 | request->scan_plans = (void *)(request->ie + ie_len); |
9655 | else if (n_ssids) |
9656 | request->scan_plans = (void *)(request->ssids + n_ssids); |
9657 | else |
9658 | request->scan_plans = (void *)(request->channels + n_channels); |
9659 | |
9660 | request->n_scan_plans = n_plans; |
9661 | |
9662 | i = 0; |
9663 | if (attrs[NL80211_ATTR_SCAN_FREQUENCIES]) { |
9664 | /* user specified, bail out if channel not found */ |
9665 | nla_for_each_nested(attr, |
9666 | attrs[NL80211_ATTR_SCAN_FREQUENCIES], |
9667 | tmp) { |
9668 | struct ieee80211_channel *chan; |
9669 | |
9670 | chan = ieee80211_get_channel(wiphy, freq: nla_get_u32(nla: attr)); |
9671 | |
9672 | if (!chan) { |
9673 | err = -EINVAL; |
9674 | goto out_free; |
9675 | } |
9676 | |
9677 | /* ignore disabled channels */ |
9678 | if (chan->flags & IEEE80211_CHAN_DISABLED) |
9679 | continue; |
9680 | |
9681 | request->channels[i] = chan; |
9682 | i++; |
9683 | } |
9684 | } else { |
9685 | /* all channels */ |
9686 | for (band = 0; band < NUM_NL80211_BANDS; band++) { |
9687 | int j; |
9688 | |
9689 | if (!wiphy->bands[band]) |
9690 | continue; |
9691 | for (j = 0; j < wiphy->bands[band]->n_channels; j++) { |
9692 | struct ieee80211_channel *chan; |
9693 | |
9694 | chan = &wiphy->bands[band]->channels[j]; |
9695 | |
9696 | if (chan->flags & IEEE80211_CHAN_DISABLED) |
9697 | continue; |
9698 | |
9699 | request->channels[i] = chan; |
9700 | i++; |
9701 | } |
9702 | } |
9703 | } |
9704 | |
9705 | if (!i) { |
9706 | err = -EINVAL; |
9707 | goto out_free; |
9708 | } |
9709 | |
9710 | request->n_channels = i; |
9711 | |
9712 | i = 0; |
9713 | if (n_ssids) { |
9714 | nla_for_each_nested(attr, attrs[NL80211_ATTR_SCAN_SSIDS], |
9715 | tmp) { |
9716 | if (nla_len(nla: attr) > IEEE80211_MAX_SSID_LEN) { |
9717 | err = -EINVAL; |
9718 | goto out_free; |
9719 | } |
9720 | request->ssids[i].ssid_len = nla_len(nla: attr); |
9721 | memcpy(request->ssids[i].ssid, nla_data(attr), |
9722 | nla_len(attr)); |
9723 | i++; |
9724 | } |
9725 | } |
9726 | |
9727 | i = 0; |
9728 | if (attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) { |
9729 | nla_for_each_nested(attr, |
9730 | attrs[NL80211_ATTR_SCHED_SCAN_MATCH], |
9731 | tmp) { |
9732 | struct nlattr *ssid, *bssid, *; |
9733 | |
9734 | err = nla_parse_nested_deprecated(tb, |
9735 | maxtype: NL80211_SCHED_SCAN_MATCH_ATTR_MAX, |
9736 | nla: attr, |
9737 | policy: nl80211_match_policy, |
9738 | NULL); |
9739 | if (err) |
9740 | goto out_free; |
9741 | ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID]; |
9742 | bssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_BSSID]; |
9743 | |
9744 | if (!ssid && !bssid) { |
9745 | i++; |
9746 | continue; |
9747 | } |
9748 | |
9749 | if (WARN_ON(i >= n_match_sets)) { |
9750 | /* this indicates a programming error, |
9751 | * the loop above should have verified |
9752 | * things properly |
9753 | */ |
9754 | err = -EINVAL; |
9755 | goto out_free; |
9756 | } |
9757 | |
9758 | if (ssid) { |
9759 | memcpy(request->match_sets[i].ssid.ssid, |
9760 | nla_data(ssid), nla_len(ssid)); |
9761 | request->match_sets[i].ssid.ssid_len = |
9762 | nla_len(nla: ssid); |
9763 | } |
9764 | if (bssid) |
9765 | memcpy(request->match_sets[i].bssid, |
9766 | nla_data(bssid), ETH_ALEN); |
9767 | |
9768 | /* special attribute - old implementation w/a */ |
9769 | request->match_sets[i].rssi_thold = default_match_rssi; |
9770 | rssi = tb[NL80211_SCHED_SCAN_MATCH_ATTR_RSSI]; |
9771 | if (rssi) |
9772 | request->match_sets[i].rssi_thold = |
9773 | nla_get_s32(nla: rssi); |
9774 | i++; |
9775 | } |
9776 | |
9777 | /* there was no other matchset, so the RSSI one is alone */ |
9778 | if (i == 0 && n_match_sets) |
9779 | request->match_sets[0].rssi_thold = default_match_rssi; |
9780 | |
9781 | request->min_rssi_thold = INT_MAX; |
9782 | for (i = 0; i < n_match_sets; i++) |
9783 | request->min_rssi_thold = |
9784 | min(request->match_sets[i].rssi_thold, |
9785 | request->min_rssi_thold); |
9786 | } else { |
9787 | request->min_rssi_thold = NL80211_SCAN_RSSI_THOLD_OFF; |
9788 | } |
9789 | |
9790 | if (ie_len) { |
9791 | request->ie_len = ie_len; |
9792 | memcpy((void *)request->ie, |
9793 | nla_data(attrs[NL80211_ATTR_IE]), |
9794 | request->ie_len); |
9795 | } |
9796 | |
9797 | err = nl80211_check_scan_flags(wiphy, wdev, request, attrs, is_sched_scan: true); |
9798 | if (err) |
9799 | goto out_free; |
9800 | |
9801 | if (attrs[NL80211_ATTR_SCHED_SCAN_DELAY]) |
9802 | request->delay = |
9803 | nla_get_u32(nla: attrs[NL80211_ATTR_SCHED_SCAN_DELAY]); |
9804 | |
9805 | if (attrs[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI]) { |
9806 | request->relative_rssi = nla_get_s8( |
9807 | nla: attrs[NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI]); |
9808 | request->relative_rssi_set = true; |
9809 | } |
9810 | |
9811 | if (request->relative_rssi_set && |
9812 | attrs[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST]) { |
9813 | struct nl80211_bss_select_rssi_adjust *; |
9814 | |
9815 | rssi_adjust = nla_data( |
9816 | nla: attrs[NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST]); |
9817 | request->rssi_adjust.band = rssi_adjust->band; |
9818 | request->rssi_adjust.delta = rssi_adjust->delta; |
9819 | if (!is_band_valid(wiphy, b: request->rssi_adjust.band)) { |
9820 | err = -EINVAL; |
9821 | goto out_free; |
9822 | } |
9823 | } |
9824 | |
9825 | err = nl80211_parse_sched_scan_plans(wiphy, n_plans, request, attrs); |
9826 | if (err) |
9827 | goto out_free; |
9828 | |
9829 | request->scan_start = jiffies; |
9830 | |
9831 | return request; |
9832 | |
9833 | out_free: |
9834 | kfree(objp: request); |
9835 | return ERR_PTR(error: err); |
9836 | } |
9837 | |
9838 | static int nl80211_start_sched_scan(struct sk_buff *skb, |
9839 | struct genl_info *info) |
9840 | { |
9841 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
9842 | struct net_device *dev = info->user_ptr[1]; |
9843 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
9844 | struct cfg80211_sched_scan_request *sched_scan_req; |
9845 | bool want_multi; |
9846 | int err; |
9847 | |
9848 | if (!rdev->wiphy.max_sched_scan_reqs || !rdev->ops->sched_scan_start) |
9849 | return -EOPNOTSUPP; |
9850 | |
9851 | want_multi = info->attrs[NL80211_ATTR_SCHED_SCAN_MULTI]; |
9852 | err = cfg80211_sched_scan_req_possible(rdev, want_multi); |
9853 | if (err) |
9854 | return err; |
9855 | |
9856 | sched_scan_req = nl80211_parse_sched_scan(wiphy: &rdev->wiphy, wdev, |
9857 | attrs: info->attrs, |
9858 | max_match_sets: rdev->wiphy.max_match_sets); |
9859 | |
9860 | err = PTR_ERR_OR_ZERO(ptr: sched_scan_req); |
9861 | if (err) |
9862 | goto out_err; |
9863 | |
9864 | /* leave request id zero for legacy request |
9865 | * or if driver does not support multi-scheduled scan |
9866 | */ |
9867 | if (want_multi && rdev->wiphy.max_sched_scan_reqs > 1) |
9868 | sched_scan_req->reqid = cfg80211_assign_cookie(rdev); |
9869 | |
9870 | err = rdev_sched_scan_start(rdev, dev, request: sched_scan_req); |
9871 | if (err) |
9872 | goto out_free; |
9873 | |
9874 | sched_scan_req->dev = dev; |
9875 | sched_scan_req->wiphy = &rdev->wiphy; |
9876 | |
9877 | if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) |
9878 | sched_scan_req->owner_nlportid = info->snd_portid; |
9879 | |
9880 | cfg80211_add_sched_scan_req(rdev, req: sched_scan_req); |
9881 | |
9882 | nl80211_send_sched_scan(req: sched_scan_req, cmd: NL80211_CMD_START_SCHED_SCAN); |
9883 | return 0; |
9884 | |
9885 | out_free: |
9886 | kfree(objp: sched_scan_req); |
9887 | out_err: |
9888 | return err; |
9889 | } |
9890 | |
9891 | static int nl80211_stop_sched_scan(struct sk_buff *skb, |
9892 | struct genl_info *info) |
9893 | { |
9894 | struct cfg80211_sched_scan_request *req; |
9895 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
9896 | u64 cookie; |
9897 | |
9898 | if (!rdev->wiphy.max_sched_scan_reqs || !rdev->ops->sched_scan_stop) |
9899 | return -EOPNOTSUPP; |
9900 | |
9901 | if (info->attrs[NL80211_ATTR_COOKIE]) { |
9902 | cookie = nla_get_u64(nla: info->attrs[NL80211_ATTR_COOKIE]); |
9903 | return __cfg80211_stop_sched_scan(rdev, reqid: cookie, driver_initiated: false); |
9904 | } |
9905 | |
9906 | req = list_first_or_null_rcu(&rdev->sched_scan_req_list, |
9907 | struct cfg80211_sched_scan_request, |
9908 | list); |
9909 | if (!req || req->reqid || |
9910 | (req->owner_nlportid && |
9911 | req->owner_nlportid != info->snd_portid)) |
9912 | return -ENOENT; |
9913 | |
9914 | return cfg80211_stop_sched_scan_req(rdev, req, driver_initiated: false); |
9915 | } |
9916 | |
9917 | static int nl80211_start_radar_detection(struct sk_buff *skb, |
9918 | struct genl_info *info) |
9919 | { |
9920 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
9921 | struct net_device *dev = info->user_ptr[1]; |
9922 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
9923 | struct wiphy *wiphy = wdev->wiphy; |
9924 | struct cfg80211_chan_def chandef; |
9925 | enum nl80211_dfs_regions dfs_region; |
9926 | unsigned int cac_time_ms; |
9927 | int err = -EINVAL; |
9928 | |
9929 | flush_delayed_work(dwork: &rdev->dfs_update_channels_wk); |
9930 | |
9931 | wiphy_lock(wiphy); |
9932 | |
9933 | dfs_region = reg_get_dfs_region(wiphy); |
9934 | if (dfs_region == NL80211_DFS_UNSET) |
9935 | goto unlock; |
9936 | |
9937 | err = nl80211_parse_chandef(rdev, info, chandef: &chandef); |
9938 | if (err) |
9939 | goto unlock; |
9940 | |
9941 | err = cfg80211_chandef_dfs_required(wiphy, chandef: &chandef, iftype: wdev->iftype); |
9942 | if (err < 0) |
9943 | goto unlock; |
9944 | |
9945 | if (err == 0) { |
9946 | err = -EINVAL; |
9947 | goto unlock; |
9948 | } |
9949 | |
9950 | if (!cfg80211_chandef_dfs_usable(wiphy, chandef: &chandef)) { |
9951 | err = -EINVAL; |
9952 | goto unlock; |
9953 | } |
9954 | |
9955 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_RADAR_BACKGROUND])) { |
9956 | err = cfg80211_start_background_radar_detection(rdev, wdev, |
9957 | chandef: &chandef); |
9958 | goto unlock; |
9959 | } |
9960 | |
9961 | if (netif_carrier_ok(dev)) { |
9962 | err = -EBUSY; |
9963 | goto unlock; |
9964 | } |
9965 | |
9966 | if (wdev->cac_started) { |
9967 | err = -EBUSY; |
9968 | goto unlock; |
9969 | } |
9970 | |
9971 | /* CAC start is offloaded to HW and can't be started manually */ |
9972 | if (wiphy_ext_feature_isset(wiphy, ftidx: NL80211_EXT_FEATURE_DFS_OFFLOAD)) { |
9973 | err = -EOPNOTSUPP; |
9974 | goto unlock; |
9975 | } |
9976 | |
9977 | if (!rdev->ops->start_radar_detection) { |
9978 | err = -EOPNOTSUPP; |
9979 | goto unlock; |
9980 | } |
9981 | |
9982 | cac_time_ms = cfg80211_chandef_dfs_cac_time(wiphy: &rdev->wiphy, chandef: &chandef); |
9983 | if (WARN_ON(!cac_time_ms)) |
9984 | cac_time_ms = IEEE80211_DFS_MIN_CAC_TIME_MS; |
9985 | |
9986 | err = rdev_start_radar_detection(rdev, dev, chandef: &chandef, cac_time_ms); |
9987 | if (!err) { |
9988 | wdev->links[0].ap.chandef = chandef; |
9989 | wdev->cac_started = true; |
9990 | wdev->cac_start_time = jiffies; |
9991 | wdev->cac_time_ms = cac_time_ms; |
9992 | } |
9993 | unlock: |
9994 | wiphy_unlock(wiphy); |
9995 | |
9996 | return err; |
9997 | } |
9998 | |
9999 | static int nl80211_notify_radar_detection(struct sk_buff *skb, |
10000 | struct genl_info *info) |
10001 | { |
10002 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
10003 | struct net_device *dev = info->user_ptr[1]; |
10004 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
10005 | struct wiphy *wiphy = wdev->wiphy; |
10006 | struct cfg80211_chan_def chandef; |
10007 | enum nl80211_dfs_regions dfs_region; |
10008 | int err; |
10009 | |
10010 | dfs_region = reg_get_dfs_region(wiphy); |
10011 | if (dfs_region == NL80211_DFS_UNSET) { |
10012 | GENL_SET_ERR_MSG(info, |
10013 | "DFS Region is not set. Unexpected Radar indication" ); |
10014 | return -EINVAL; |
10015 | } |
10016 | |
10017 | err = nl80211_parse_chandef(rdev, info, chandef: &chandef); |
10018 | if (err) { |
10019 | GENL_SET_ERR_MSG(info, "Unable to extract chandef info" ); |
10020 | return err; |
10021 | } |
10022 | |
10023 | err = cfg80211_chandef_dfs_required(wiphy, chandef: &chandef, iftype: wdev->iftype); |
10024 | if (err < 0) { |
10025 | GENL_SET_ERR_MSG(info, "chandef is invalid" ); |
10026 | return err; |
10027 | } |
10028 | |
10029 | if (err == 0) { |
10030 | GENL_SET_ERR_MSG(info, |
10031 | "Unexpected Radar indication for chandef/iftype" ); |
10032 | return -EINVAL; |
10033 | } |
10034 | |
10035 | /* Do not process this notification if radar is already detected |
10036 | * by kernel on this channel, and return success. |
10037 | */ |
10038 | if (chandef.chan->dfs_state == NL80211_DFS_UNAVAILABLE) |
10039 | return 0; |
10040 | |
10041 | cfg80211_set_dfs_state(wiphy, chandef: &chandef, dfs_state: NL80211_DFS_UNAVAILABLE); |
10042 | |
10043 | cfg80211_sched_dfs_chan_update(rdev); |
10044 | |
10045 | rdev->radar_chandef = chandef; |
10046 | |
10047 | /* Propagate this notification to other radios as well */ |
10048 | queue_work(wq: cfg80211_wq, work: &rdev->propagate_radar_detect_wk); |
10049 | |
10050 | return 0; |
10051 | } |
10052 | |
10053 | static int nl80211_parse_counter_offsets(struct cfg80211_registered_device *rdev, |
10054 | const u8 *data, size_t datalen, |
10055 | int first_count, struct nlattr *attr, |
10056 | const u16 **offsets, unsigned int *n_offsets) |
10057 | { |
10058 | int i; |
10059 | |
10060 | *n_offsets = 0; |
10061 | |
10062 | if (!attr) |
10063 | return 0; |
10064 | |
10065 | if (!nla_len(nla: attr) || (nla_len(nla: attr) % sizeof(u16))) |
10066 | return -EINVAL; |
10067 | |
10068 | *n_offsets = nla_len(nla: attr) / sizeof(u16); |
10069 | if (rdev->wiphy.max_num_csa_counters && |
10070 | (*n_offsets > rdev->wiphy.max_num_csa_counters)) |
10071 | return -EINVAL; |
10072 | |
10073 | *offsets = nla_data(nla: attr); |
10074 | |
10075 | /* sanity checks - counters should fit and be the same */ |
10076 | for (i = 0; i < *n_offsets; i++) { |
10077 | u16 offset = (*offsets)[i]; |
10078 | |
10079 | if (offset >= datalen) |
10080 | return -EINVAL; |
10081 | |
10082 | if (first_count != -1 && data[offset] != first_count) |
10083 | return -EINVAL; |
10084 | } |
10085 | |
10086 | return 0; |
10087 | } |
10088 | |
10089 | static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info) |
10090 | { |
10091 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
10092 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); |
10093 | struct net_device *dev = info->user_ptr[1]; |
10094 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
10095 | struct cfg80211_csa_settings params; |
10096 | struct nlattr **csa_attrs = NULL; |
10097 | int err; |
10098 | bool need_new_beacon = false; |
10099 | bool need_handle_dfs_flag = true; |
10100 | u32 cs_count; |
10101 | |
10102 | if (!rdev->ops->channel_switch || |
10103 | !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH)) |
10104 | return -EOPNOTSUPP; |
10105 | |
10106 | switch (dev->ieee80211_ptr->iftype) { |
10107 | case NL80211_IFTYPE_AP: |
10108 | case NL80211_IFTYPE_P2P_GO: |
10109 | need_new_beacon = true; |
10110 | /* For all modes except AP the handle_dfs flag needs to be |
10111 | * supplied to tell the kernel that userspace will handle radar |
10112 | * events when they happen. Otherwise a switch to a channel |
10113 | * requiring DFS will be rejected. |
10114 | */ |
10115 | need_handle_dfs_flag = false; |
10116 | |
10117 | /* useless if AP is not running */ |
10118 | if (!wdev->links[link_id].ap.beacon_interval) |
10119 | return -ENOTCONN; |
10120 | break; |
10121 | case NL80211_IFTYPE_ADHOC: |
10122 | if (!wdev->u.ibss.ssid_len) |
10123 | return -ENOTCONN; |
10124 | break; |
10125 | case NL80211_IFTYPE_MESH_POINT: |
10126 | if (!wdev->u.mesh.id_len) |
10127 | return -ENOTCONN; |
10128 | break; |
10129 | default: |
10130 | return -EOPNOTSUPP; |
10131 | } |
10132 | |
10133 | memset(¶ms, 0, sizeof(params)); |
10134 | params.beacon_csa.ftm_responder = -1; |
10135 | |
10136 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || |
10137 | !info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]) |
10138 | return -EINVAL; |
10139 | |
10140 | /* only important for AP, IBSS and mesh create IEs internally */ |
10141 | if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES]) |
10142 | return -EINVAL; |
10143 | |
10144 | /* Even though the attribute is u32, the specification says |
10145 | * u8, so let's make sure we don't overflow. |
10146 | */ |
10147 | cs_count = nla_get_u32(nla: info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]); |
10148 | if (cs_count > 255) |
10149 | return -EINVAL; |
10150 | |
10151 | params.count = cs_count; |
10152 | |
10153 | if (!need_new_beacon) |
10154 | goto skip_beacons; |
10155 | |
10156 | err = nl80211_parse_beacon(rdev, attrs: info->attrs, bcn: ¶ms.beacon_after, |
10157 | extack: info->extack); |
10158 | if (err) |
10159 | goto free; |
10160 | |
10161 | csa_attrs = kcalloc(n: NL80211_ATTR_MAX + 1, size: sizeof(*csa_attrs), |
10162 | GFP_KERNEL); |
10163 | if (!csa_attrs) { |
10164 | err = -ENOMEM; |
10165 | goto free; |
10166 | } |
10167 | |
10168 | err = nla_parse_nested_deprecated(tb: csa_attrs, maxtype: NL80211_ATTR_MAX, |
10169 | nla: info->attrs[NL80211_ATTR_CSA_IES], |
10170 | policy: nl80211_policy, extack: info->extack); |
10171 | if (err) |
10172 | goto free; |
10173 | |
10174 | err = nl80211_parse_beacon(rdev, attrs: csa_attrs, bcn: ¶ms.beacon_csa, |
10175 | extack: info->extack); |
10176 | if (err) |
10177 | goto free; |
10178 | |
10179 | if (!csa_attrs[NL80211_ATTR_CNTDWN_OFFS_BEACON]) { |
10180 | err = -EINVAL; |
10181 | goto free; |
10182 | } |
10183 | |
10184 | err = nl80211_parse_counter_offsets(rdev, data: params.beacon_csa.tail, |
10185 | datalen: params.beacon_csa.tail_len, |
10186 | first_count: params.count, |
10187 | attr: csa_attrs[NL80211_ATTR_CNTDWN_OFFS_BEACON], |
10188 | offsets: ¶ms.counter_offsets_beacon, |
10189 | n_offsets: ¶ms.n_counter_offsets_beacon); |
10190 | if (err) |
10191 | goto free; |
10192 | |
10193 | err = nl80211_parse_counter_offsets(rdev, data: params.beacon_csa.probe_resp, |
10194 | datalen: params.beacon_csa.probe_resp_len, |
10195 | first_count: params.count, |
10196 | attr: csa_attrs[NL80211_ATTR_CNTDWN_OFFS_PRESP], |
10197 | offsets: ¶ms.counter_offsets_presp, |
10198 | n_offsets: ¶ms.n_counter_offsets_presp); |
10199 | if (err) |
10200 | goto free; |
10201 | |
10202 | skip_beacons: |
10203 | err = nl80211_parse_chandef(rdev, info, chandef: ¶ms.chandef); |
10204 | if (err) |
10205 | goto free; |
10206 | |
10207 | if (!cfg80211_reg_can_beacon_relax(wiphy: &rdev->wiphy, chandef: ¶ms.chandef, |
10208 | iftype: wdev->iftype)) { |
10209 | err = -EINVAL; |
10210 | goto free; |
10211 | } |
10212 | |
10213 | err = cfg80211_chandef_dfs_required(wiphy: wdev->wiphy, |
10214 | chandef: ¶ms.chandef, |
10215 | iftype: wdev->iftype); |
10216 | if (err < 0) |
10217 | goto free; |
10218 | |
10219 | if (err > 0) { |
10220 | params.radar_required = true; |
10221 | if (need_handle_dfs_flag && |
10222 | !nla_get_flag(nla: info->attrs[NL80211_ATTR_HANDLE_DFS])) { |
10223 | err = -EINVAL; |
10224 | goto free; |
10225 | } |
10226 | } |
10227 | |
10228 | if (info->attrs[NL80211_ATTR_CH_SWITCH_BLOCK_TX]) |
10229 | params.block_tx = true; |
10230 | |
10231 | params.link_id = link_id; |
10232 | err = rdev_channel_switch(rdev, dev, params: ¶ms); |
10233 | |
10234 | free: |
10235 | kfree(objp: params.beacon_after.mbssid_ies); |
10236 | kfree(objp: params.beacon_csa.mbssid_ies); |
10237 | kfree(objp: params.beacon_after.rnr_ies); |
10238 | kfree(objp: params.beacon_csa.rnr_ies); |
10239 | kfree(objp: csa_attrs); |
10240 | return err; |
10241 | } |
10242 | |
10243 | static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb, |
10244 | u32 seq, int flags, |
10245 | struct cfg80211_registered_device *rdev, |
10246 | struct wireless_dev *wdev, |
10247 | struct cfg80211_internal_bss *intbss) |
10248 | { |
10249 | struct cfg80211_bss *res = &intbss->pub; |
10250 | const struct cfg80211_bss_ies *ies; |
10251 | unsigned int link_id; |
10252 | void *hdr; |
10253 | struct nlattr *bss; |
10254 | |
10255 | lockdep_assert_wiphy(wdev->wiphy); |
10256 | |
10257 | hdr = nl80211hdr_put(skb: msg, NETLINK_CB(cb->skb).portid, seq, flags, |
10258 | cmd: NL80211_CMD_NEW_SCAN_RESULTS); |
10259 | if (!hdr) |
10260 | return -1; |
10261 | |
10262 | genl_dump_check_consistent(cb, user_hdr: hdr); |
10263 | |
10264 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_GENERATION, value: rdev->bss_generation)) |
10265 | goto nla_put_failure; |
10266 | if (wdev->netdev && |
10267 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: wdev->netdev->ifindex)) |
10268 | goto nla_put_failure; |
10269 | if (nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), |
10270 | padattr: NL80211_ATTR_PAD)) |
10271 | goto nla_put_failure; |
10272 | |
10273 | bss = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_BSS); |
10274 | if (!bss) |
10275 | goto nla_put_failure; |
10276 | if ((!is_zero_ether_addr(addr: res->bssid) && |
10277 | nla_put(skb: msg, attrtype: NL80211_BSS_BSSID, ETH_ALEN, data: res->bssid))) |
10278 | goto nla_put_failure; |
10279 | |
10280 | rcu_read_lock(); |
10281 | /* indicate whether we have probe response data or not */ |
10282 | if (rcu_access_pointer(res->proberesp_ies) && |
10283 | nla_put_flag(skb: msg, attrtype: NL80211_BSS_PRESP_DATA)) |
10284 | goto fail_unlock_rcu; |
10285 | |
10286 | /* this pointer prefers to be pointed to probe response data |
10287 | * but is always valid |
10288 | */ |
10289 | ies = rcu_dereference(res->ies); |
10290 | if (ies) { |
10291 | if (nla_put_u64_64bit(skb: msg, attrtype: NL80211_BSS_TSF, value: ies->tsf, |
10292 | padattr: NL80211_BSS_PAD)) |
10293 | goto fail_unlock_rcu; |
10294 | if (ies->len && nla_put(skb: msg, attrtype: NL80211_BSS_INFORMATION_ELEMENTS, |
10295 | attrlen: ies->len, data: ies->data)) |
10296 | goto fail_unlock_rcu; |
10297 | } |
10298 | |
10299 | /* and this pointer is always (unless driver didn't know) beacon data */ |
10300 | ies = rcu_dereference(res->beacon_ies); |
10301 | if (ies && ies->from_beacon) { |
10302 | if (nla_put_u64_64bit(skb: msg, attrtype: NL80211_BSS_BEACON_TSF, value: ies->tsf, |
10303 | padattr: NL80211_BSS_PAD)) |
10304 | goto fail_unlock_rcu; |
10305 | if (ies->len && nla_put(skb: msg, attrtype: NL80211_BSS_BEACON_IES, |
10306 | attrlen: ies->len, data: ies->data)) |
10307 | goto fail_unlock_rcu; |
10308 | } |
10309 | rcu_read_unlock(); |
10310 | |
10311 | if (res->beacon_interval && |
10312 | nla_put_u16(skb: msg, attrtype: NL80211_BSS_BEACON_INTERVAL, value: res->beacon_interval)) |
10313 | goto nla_put_failure; |
10314 | if (nla_put_u16(skb: msg, attrtype: NL80211_BSS_CAPABILITY, value: res->capability) || |
10315 | nla_put_u32(skb: msg, attrtype: NL80211_BSS_FREQUENCY, value: res->channel->center_freq) || |
10316 | nla_put_u32(skb: msg, attrtype: NL80211_BSS_FREQUENCY_OFFSET, |
10317 | value: res->channel->freq_offset) || |
10318 | nla_put_u32(skb: msg, attrtype: NL80211_BSS_SEEN_MS_AGO, |
10319 | value: jiffies_to_msecs(j: jiffies - intbss->ts))) |
10320 | goto nla_put_failure; |
10321 | |
10322 | if (intbss->parent_tsf && |
10323 | (nla_put_u64_64bit(skb: msg, attrtype: NL80211_BSS_PARENT_TSF, |
10324 | value: intbss->parent_tsf, padattr: NL80211_BSS_PAD) || |
10325 | nla_put(skb: msg, attrtype: NL80211_BSS_PARENT_BSSID, ETH_ALEN, |
10326 | data: intbss->parent_bssid))) |
10327 | goto nla_put_failure; |
10328 | |
10329 | if (intbss->ts_boottime && |
10330 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_BSS_LAST_SEEN_BOOTTIME, |
10331 | value: intbss->ts_boottime, padattr: NL80211_BSS_PAD)) |
10332 | goto nla_put_failure; |
10333 | |
10334 | if (!nl80211_put_signal(msg, mask: intbss->pub.chains, |
10335 | signal: intbss->pub.chain_signal, |
10336 | id: NL80211_BSS_CHAIN_SIGNAL)) |
10337 | goto nla_put_failure; |
10338 | |
10339 | switch (rdev->wiphy.signal_type) { |
10340 | case CFG80211_SIGNAL_TYPE_MBM: |
10341 | if (nla_put_u32(skb: msg, attrtype: NL80211_BSS_SIGNAL_MBM, value: res->signal)) |
10342 | goto nla_put_failure; |
10343 | break; |
10344 | case CFG80211_SIGNAL_TYPE_UNSPEC: |
10345 | if (nla_put_u8(skb: msg, attrtype: NL80211_BSS_SIGNAL_UNSPEC, value: res->signal)) |
10346 | goto nla_put_failure; |
10347 | break; |
10348 | default: |
10349 | break; |
10350 | } |
10351 | |
10352 | switch (wdev->iftype) { |
10353 | case NL80211_IFTYPE_P2P_CLIENT: |
10354 | case NL80211_IFTYPE_STATION: |
10355 | for_each_valid_link(wdev, link_id) { |
10356 | if (intbss == wdev->links[link_id].client.current_bss && |
10357 | (nla_put_u32(skb: msg, attrtype: NL80211_BSS_STATUS, |
10358 | value: NL80211_BSS_STATUS_ASSOCIATED) || |
10359 | (wdev->valid_links && |
10360 | (nla_put_u8(skb: msg, attrtype: NL80211_BSS_MLO_LINK_ID, |
10361 | value: link_id) || |
10362 | nla_put(skb: msg, attrtype: NL80211_BSS_MLD_ADDR, ETH_ALEN, |
10363 | data: wdev->u.client.connected_addr))))) |
10364 | goto nla_put_failure; |
10365 | } |
10366 | break; |
10367 | case NL80211_IFTYPE_ADHOC: |
10368 | if (intbss == wdev->u.ibss.current_bss && |
10369 | nla_put_u32(skb: msg, attrtype: NL80211_BSS_STATUS, |
10370 | value: NL80211_BSS_STATUS_IBSS_JOINED)) |
10371 | goto nla_put_failure; |
10372 | break; |
10373 | default: |
10374 | break; |
10375 | } |
10376 | |
10377 | if (nla_put_u32(skb: msg, attrtype: NL80211_BSS_USE_FOR, value: res->use_for)) |
10378 | goto nla_put_failure; |
10379 | |
10380 | if (res->cannot_use_reasons && |
10381 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_BSS_CANNOT_USE_REASONS, |
10382 | value: res->cannot_use_reasons, |
10383 | padattr: NL80211_BSS_PAD)) |
10384 | goto nla_put_failure; |
10385 | |
10386 | nla_nest_end(skb: msg, start: bss); |
10387 | |
10388 | genlmsg_end(skb: msg, hdr); |
10389 | return 0; |
10390 | |
10391 | fail_unlock_rcu: |
10392 | rcu_read_unlock(); |
10393 | nla_put_failure: |
10394 | genlmsg_cancel(skb: msg, hdr); |
10395 | return -EMSGSIZE; |
10396 | } |
10397 | |
10398 | static int nl80211_dump_scan(struct sk_buff *skb, struct netlink_callback *cb) |
10399 | { |
10400 | struct cfg80211_registered_device *rdev; |
10401 | struct cfg80211_internal_bss *scan; |
10402 | struct wireless_dev *wdev; |
10403 | struct nlattr **attrbuf; |
10404 | int start = cb->args[2], idx = 0; |
10405 | bool dump_include_use_data; |
10406 | int err; |
10407 | |
10408 | attrbuf = kcalloc(n: NUM_NL80211_ATTR, size: sizeof(*attrbuf), GFP_KERNEL); |
10409 | if (!attrbuf) |
10410 | return -ENOMEM; |
10411 | |
10412 | err = nl80211_prepare_wdev_dump(cb, rdev: &rdev, wdev: &wdev, attrbuf); |
10413 | if (err) { |
10414 | kfree(objp: attrbuf); |
10415 | return err; |
10416 | } |
10417 | /* nl80211_prepare_wdev_dump acquired it in the successful case */ |
10418 | __acquire(&rdev->wiphy.mtx); |
10419 | |
10420 | dump_include_use_data = |
10421 | attrbuf[NL80211_ATTR_BSS_DUMP_INCLUDE_USE_DATA]; |
10422 | kfree(objp: attrbuf); |
10423 | |
10424 | spin_lock_bh(lock: &rdev->bss_lock); |
10425 | |
10426 | /* |
10427 | * dump_scan will be called multiple times to break up the scan results |
10428 | * into multiple messages. It is unlikely that any more bss-es will be |
10429 | * expired after the first call, so only call only call this on the |
10430 | * first dump_scan invocation. |
10431 | */ |
10432 | if (start == 0) |
10433 | cfg80211_bss_expire(rdev); |
10434 | |
10435 | cb->seq = rdev->bss_generation; |
10436 | |
10437 | list_for_each_entry(scan, &rdev->bss_list, list) { |
10438 | if (++idx <= start) |
10439 | continue; |
10440 | if (!dump_include_use_data && |
10441 | !(scan->pub.use_for & NL80211_BSS_USE_FOR_NORMAL)) |
10442 | continue; |
10443 | if (nl80211_send_bss(msg: skb, cb, |
10444 | seq: cb->nlh->nlmsg_seq, NLM_F_MULTI, |
10445 | rdev, wdev, intbss: scan) < 0) { |
10446 | idx--; |
10447 | break; |
10448 | } |
10449 | } |
10450 | |
10451 | spin_unlock_bh(lock: &rdev->bss_lock); |
10452 | |
10453 | cb->args[2] = idx; |
10454 | wiphy_unlock(wiphy: &rdev->wiphy); |
10455 | |
10456 | return skb->len; |
10457 | } |
10458 | |
10459 | static int nl80211_send_survey(struct sk_buff *msg, u32 portid, u32 seq, |
10460 | int flags, struct net_device *dev, |
10461 | bool allow_radio_stats, |
10462 | struct survey_info *survey) |
10463 | { |
10464 | void *hdr; |
10465 | struct nlattr *infoattr; |
10466 | |
10467 | /* skip radio stats if userspace didn't request them */ |
10468 | if (!survey->channel && !allow_radio_stats) |
10469 | return 0; |
10470 | |
10471 | hdr = nl80211hdr_put(skb: msg, portid, seq, flags, |
10472 | cmd: NL80211_CMD_NEW_SURVEY_RESULTS); |
10473 | if (!hdr) |
10474 | return -ENOMEM; |
10475 | |
10476 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex)) |
10477 | goto nla_put_failure; |
10478 | |
10479 | infoattr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_SURVEY_INFO); |
10480 | if (!infoattr) |
10481 | goto nla_put_failure; |
10482 | |
10483 | if (survey->channel && |
10484 | nla_put_u32(skb: msg, attrtype: NL80211_SURVEY_INFO_FREQUENCY, |
10485 | value: survey->channel->center_freq)) |
10486 | goto nla_put_failure; |
10487 | |
10488 | if (survey->channel && survey->channel->freq_offset && |
10489 | nla_put_u32(skb: msg, attrtype: NL80211_SURVEY_INFO_FREQUENCY_OFFSET, |
10490 | value: survey->channel->freq_offset)) |
10491 | goto nla_put_failure; |
10492 | |
10493 | if ((survey->filled & SURVEY_INFO_NOISE_DBM) && |
10494 | nla_put_u8(skb: msg, attrtype: NL80211_SURVEY_INFO_NOISE, value: survey->noise)) |
10495 | goto nla_put_failure; |
10496 | if ((survey->filled & SURVEY_INFO_IN_USE) && |
10497 | nla_put_flag(skb: msg, attrtype: NL80211_SURVEY_INFO_IN_USE)) |
10498 | goto nla_put_failure; |
10499 | if ((survey->filled & SURVEY_INFO_TIME) && |
10500 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_SURVEY_INFO_TIME, |
10501 | value: survey->time, padattr: NL80211_SURVEY_INFO_PAD)) |
10502 | goto nla_put_failure; |
10503 | if ((survey->filled & SURVEY_INFO_TIME_BUSY) && |
10504 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_SURVEY_INFO_TIME_BUSY, |
10505 | value: survey->time_busy, padattr: NL80211_SURVEY_INFO_PAD)) |
10506 | goto nla_put_failure; |
10507 | if ((survey->filled & SURVEY_INFO_TIME_EXT_BUSY) && |
10508 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_SURVEY_INFO_TIME_EXT_BUSY, |
10509 | value: survey->time_ext_busy, padattr: NL80211_SURVEY_INFO_PAD)) |
10510 | goto nla_put_failure; |
10511 | if ((survey->filled & SURVEY_INFO_TIME_RX) && |
10512 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_SURVEY_INFO_TIME_RX, |
10513 | value: survey->time_rx, padattr: NL80211_SURVEY_INFO_PAD)) |
10514 | goto nla_put_failure; |
10515 | if ((survey->filled & SURVEY_INFO_TIME_TX) && |
10516 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_SURVEY_INFO_TIME_TX, |
10517 | value: survey->time_tx, padattr: NL80211_SURVEY_INFO_PAD)) |
10518 | goto nla_put_failure; |
10519 | if ((survey->filled & SURVEY_INFO_TIME_SCAN) && |
10520 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_SURVEY_INFO_TIME_SCAN, |
10521 | value: survey->time_scan, padattr: NL80211_SURVEY_INFO_PAD)) |
10522 | goto nla_put_failure; |
10523 | if ((survey->filled & SURVEY_INFO_TIME_BSS_RX) && |
10524 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_SURVEY_INFO_TIME_BSS_RX, |
10525 | value: survey->time_bss_rx, padattr: NL80211_SURVEY_INFO_PAD)) |
10526 | goto nla_put_failure; |
10527 | |
10528 | nla_nest_end(skb: msg, start: infoattr); |
10529 | |
10530 | genlmsg_end(skb: msg, hdr); |
10531 | return 0; |
10532 | |
10533 | nla_put_failure: |
10534 | genlmsg_cancel(skb: msg, hdr); |
10535 | return -EMSGSIZE; |
10536 | } |
10537 | |
10538 | static int nl80211_dump_survey(struct sk_buff *skb, struct netlink_callback *cb) |
10539 | { |
10540 | struct nlattr **attrbuf; |
10541 | struct survey_info survey; |
10542 | struct cfg80211_registered_device *rdev; |
10543 | struct wireless_dev *wdev; |
10544 | int survey_idx = cb->args[2]; |
10545 | int res; |
10546 | bool radio_stats; |
10547 | |
10548 | attrbuf = kcalloc(n: NUM_NL80211_ATTR, size: sizeof(*attrbuf), GFP_KERNEL); |
10549 | if (!attrbuf) |
10550 | return -ENOMEM; |
10551 | |
10552 | res = nl80211_prepare_wdev_dump(cb, rdev: &rdev, wdev: &wdev, attrbuf); |
10553 | if (res) { |
10554 | kfree(objp: attrbuf); |
10555 | return res; |
10556 | } |
10557 | /* nl80211_prepare_wdev_dump acquired it in the successful case */ |
10558 | __acquire(&rdev->wiphy.mtx); |
10559 | |
10560 | /* prepare_wdev_dump parsed the attributes */ |
10561 | radio_stats = attrbuf[NL80211_ATTR_SURVEY_RADIO_STATS]; |
10562 | |
10563 | if (!wdev->netdev) { |
10564 | res = -EINVAL; |
10565 | goto out_err; |
10566 | } |
10567 | |
10568 | if (!rdev->ops->dump_survey) { |
10569 | res = -EOPNOTSUPP; |
10570 | goto out_err; |
10571 | } |
10572 | |
10573 | while (1) { |
10574 | res = rdev_dump_survey(rdev, netdev: wdev->netdev, idx: survey_idx, info: &survey); |
10575 | if (res == -ENOENT) |
10576 | break; |
10577 | if (res) |
10578 | goto out_err; |
10579 | |
10580 | /* don't send disabled channels, but do send non-channel data */ |
10581 | if (survey.channel && |
10582 | survey.channel->flags & IEEE80211_CHAN_DISABLED) { |
10583 | survey_idx++; |
10584 | continue; |
10585 | } |
10586 | |
10587 | if (nl80211_send_survey(msg: skb, |
10588 | NETLINK_CB(cb->skb).portid, |
10589 | seq: cb->nlh->nlmsg_seq, NLM_F_MULTI, |
10590 | dev: wdev->netdev, allow_radio_stats: radio_stats, survey: &survey) < 0) |
10591 | goto out; |
10592 | survey_idx++; |
10593 | } |
10594 | |
10595 | out: |
10596 | cb->args[2] = survey_idx; |
10597 | res = skb->len; |
10598 | out_err: |
10599 | kfree(objp: attrbuf); |
10600 | wiphy_unlock(wiphy: &rdev->wiphy); |
10601 | return res; |
10602 | } |
10603 | |
10604 | static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) |
10605 | { |
10606 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
10607 | struct net_device *dev = info->user_ptr[1]; |
10608 | struct ieee80211_channel *chan; |
10609 | const u8 *bssid, *ssid; |
10610 | int err, ssid_len; |
10611 | enum nl80211_auth_type auth_type; |
10612 | struct key_parse key; |
10613 | bool local_state_change; |
10614 | struct cfg80211_auth_request req = {}; |
10615 | u32 freq; |
10616 | |
10617 | if (!info->attrs[NL80211_ATTR_MAC]) |
10618 | return -EINVAL; |
10619 | |
10620 | if (!info->attrs[NL80211_ATTR_AUTH_TYPE]) |
10621 | return -EINVAL; |
10622 | |
10623 | if (!info->attrs[NL80211_ATTR_SSID]) |
10624 | return -EINVAL; |
10625 | |
10626 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) |
10627 | return -EINVAL; |
10628 | |
10629 | err = nl80211_parse_key(info, k: &key); |
10630 | if (err) |
10631 | return err; |
10632 | |
10633 | if (key.idx >= 0) { |
10634 | if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP) |
10635 | return -EINVAL; |
10636 | if (!key.p.key || !key.p.key_len) |
10637 | return -EINVAL; |
10638 | if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 || |
10639 | key.p.key_len != WLAN_KEY_LEN_WEP40) && |
10640 | (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 || |
10641 | key.p.key_len != WLAN_KEY_LEN_WEP104)) |
10642 | return -EINVAL; |
10643 | if (key.idx > 3) |
10644 | return -EINVAL; |
10645 | } else { |
10646 | key.p.key_len = 0; |
10647 | key.p.key = NULL; |
10648 | } |
10649 | |
10650 | if (key.idx >= 0) { |
10651 | int i; |
10652 | bool ok = false; |
10653 | |
10654 | for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) { |
10655 | if (key.p.cipher == rdev->wiphy.cipher_suites[i]) { |
10656 | ok = true; |
10657 | break; |
10658 | } |
10659 | } |
10660 | if (!ok) |
10661 | return -EINVAL; |
10662 | } |
10663 | |
10664 | if (!rdev->ops->auth) |
10665 | return -EOPNOTSUPP; |
10666 | |
10667 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && |
10668 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) |
10669 | return -EOPNOTSUPP; |
10670 | |
10671 | bssid = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
10672 | freq = MHZ_TO_KHZ(nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); |
10673 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]) |
10674 | freq += |
10675 | nla_get_u32(nla: info->attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]); |
10676 | |
10677 | chan = nl80211_get_valid_chan(wiphy: &rdev->wiphy, freq); |
10678 | if (!chan) |
10679 | return -EINVAL; |
10680 | |
10681 | ssid = nla_data(nla: info->attrs[NL80211_ATTR_SSID]); |
10682 | ssid_len = nla_len(nla: info->attrs[NL80211_ATTR_SSID]); |
10683 | |
10684 | if (info->attrs[NL80211_ATTR_IE]) { |
10685 | req.ie = nla_data(nla: info->attrs[NL80211_ATTR_IE]); |
10686 | req.ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); |
10687 | } |
10688 | |
10689 | auth_type = nla_get_u32(nla: info->attrs[NL80211_ATTR_AUTH_TYPE]); |
10690 | if (!nl80211_valid_auth_type(rdev, auth_type, NL80211_CMD_AUTHENTICATE)) |
10691 | return -EINVAL; |
10692 | |
10693 | if ((auth_type == NL80211_AUTHTYPE_SAE || |
10694 | auth_type == NL80211_AUTHTYPE_FILS_SK || |
10695 | auth_type == NL80211_AUTHTYPE_FILS_SK_PFS || |
10696 | auth_type == NL80211_AUTHTYPE_FILS_PK) && |
10697 | !info->attrs[NL80211_ATTR_AUTH_DATA]) |
10698 | return -EINVAL; |
10699 | |
10700 | if (info->attrs[NL80211_ATTR_AUTH_DATA]) { |
10701 | if (auth_type != NL80211_AUTHTYPE_SAE && |
10702 | auth_type != NL80211_AUTHTYPE_FILS_SK && |
10703 | auth_type != NL80211_AUTHTYPE_FILS_SK_PFS && |
10704 | auth_type != NL80211_AUTHTYPE_FILS_PK) |
10705 | return -EINVAL; |
10706 | req.auth_data = nla_data(nla: info->attrs[NL80211_ATTR_AUTH_DATA]); |
10707 | req.auth_data_len = nla_len(nla: info->attrs[NL80211_ATTR_AUTH_DATA]); |
10708 | } |
10709 | |
10710 | local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; |
10711 | |
10712 | /* |
10713 | * Since we no longer track auth state, ignore |
10714 | * requests to only change local state. |
10715 | */ |
10716 | if (local_state_change) |
10717 | return 0; |
10718 | |
10719 | req.auth_type = auth_type; |
10720 | req.key = key.p.key; |
10721 | req.key_len = key.p.key_len; |
10722 | req.key_idx = key.idx; |
10723 | req.link_id = nl80211_link_id_or_invalid(attrs: info->attrs); |
10724 | if (req.link_id >= 0) { |
10725 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_MLO)) |
10726 | return -EINVAL; |
10727 | if (!info->attrs[NL80211_ATTR_MLD_ADDR]) |
10728 | return -EINVAL; |
10729 | req.ap_mld_addr = nla_data(nla: info->attrs[NL80211_ATTR_MLD_ADDR]); |
10730 | if (!is_valid_ether_addr(addr: req.ap_mld_addr)) |
10731 | return -EINVAL; |
10732 | } |
10733 | |
10734 | req.bss = cfg80211_get_bss(wiphy: &rdev->wiphy, channel: chan, bssid, ssid, ssid_len, |
10735 | bss_type: IEEE80211_BSS_TYPE_ESS, |
10736 | privacy: IEEE80211_PRIVACY_ANY); |
10737 | if (!req.bss) |
10738 | return -ENOENT; |
10739 | |
10740 | err = cfg80211_mlme_auth(rdev, dev, req: &req); |
10741 | |
10742 | cfg80211_put_bss(wiphy: &rdev->wiphy, bss: req.bss); |
10743 | |
10744 | return err; |
10745 | } |
10746 | |
10747 | static int validate_pae_over_nl80211(struct cfg80211_registered_device *rdev, |
10748 | struct genl_info *info) |
10749 | { |
10750 | if (!info->attrs[NL80211_ATTR_SOCKET_OWNER]) { |
10751 | GENL_SET_ERR_MSG(info, "SOCKET_OWNER not set" ); |
10752 | return -EINVAL; |
10753 | } |
10754 | |
10755 | if (!rdev->ops->tx_control_port || |
10756 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
10757 | ftidx: NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211)) |
10758 | return -EOPNOTSUPP; |
10759 | |
10760 | return 0; |
10761 | } |
10762 | |
10763 | static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev, |
10764 | struct genl_info *info, |
10765 | struct cfg80211_crypto_settings *settings, |
10766 | int cipher_limit) |
10767 | { |
10768 | memset(settings, 0, sizeof(*settings)); |
10769 | |
10770 | settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT]; |
10771 | |
10772 | if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) { |
10773 | u16 proto; |
10774 | |
10775 | proto = nla_get_u16( |
10776 | nla: info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]); |
10777 | settings->control_port_ethertype = cpu_to_be16(proto); |
10778 | if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) && |
10779 | proto != ETH_P_PAE) |
10780 | return -EINVAL; |
10781 | if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT]) |
10782 | settings->control_port_no_encrypt = true; |
10783 | } else |
10784 | settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE); |
10785 | |
10786 | if (info->attrs[NL80211_ATTR_CONTROL_PORT_OVER_NL80211]) { |
10787 | int r = validate_pae_over_nl80211(rdev, info); |
10788 | |
10789 | if (r < 0) |
10790 | return r; |
10791 | |
10792 | settings->control_port_over_nl80211 = true; |
10793 | |
10794 | if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_PREAUTH]) |
10795 | settings->control_port_no_preauth = true; |
10796 | } |
10797 | |
10798 | if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) { |
10799 | void *data; |
10800 | int len, i; |
10801 | |
10802 | data = nla_data(nla: info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); |
10803 | len = nla_len(nla: info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); |
10804 | settings->n_ciphers_pairwise = len / sizeof(u32); |
10805 | |
10806 | if (len % sizeof(u32)) |
10807 | return -EINVAL; |
10808 | |
10809 | if (settings->n_ciphers_pairwise > cipher_limit) |
10810 | return -EINVAL; |
10811 | |
10812 | memcpy(settings->ciphers_pairwise, data, len); |
10813 | |
10814 | for (i = 0; i < settings->n_ciphers_pairwise; i++) |
10815 | if (!cfg80211_supported_cipher_suite( |
10816 | wiphy: &rdev->wiphy, |
10817 | cipher: settings->ciphers_pairwise[i])) |
10818 | return -EINVAL; |
10819 | } |
10820 | |
10821 | if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) { |
10822 | settings->cipher_group = |
10823 | nla_get_u32(nla: info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]); |
10824 | if (!cfg80211_supported_cipher_suite(wiphy: &rdev->wiphy, |
10825 | cipher: settings->cipher_group)) |
10826 | return -EINVAL; |
10827 | } |
10828 | |
10829 | if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) |
10830 | settings->wpa_versions = |
10831 | nla_get_u32(nla: info->attrs[NL80211_ATTR_WPA_VERSIONS]); |
10832 | |
10833 | if (info->attrs[NL80211_ATTR_AKM_SUITES]) { |
10834 | void *data; |
10835 | int len; |
10836 | |
10837 | data = nla_data(nla: info->attrs[NL80211_ATTR_AKM_SUITES]); |
10838 | len = nla_len(nla: info->attrs[NL80211_ATTR_AKM_SUITES]); |
10839 | settings->n_akm_suites = len / sizeof(u32); |
10840 | |
10841 | if (len % sizeof(u32)) |
10842 | return -EINVAL; |
10843 | |
10844 | if (settings->n_akm_suites > rdev->wiphy.max_num_akm_suites) |
10845 | return -EINVAL; |
10846 | |
10847 | memcpy(settings->akm_suites, data, len); |
10848 | } |
10849 | |
10850 | if (info->attrs[NL80211_ATTR_PMK]) { |
10851 | if (nla_len(nla: info->attrs[NL80211_ATTR_PMK]) != WLAN_PMK_LEN) |
10852 | return -EINVAL; |
10853 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
10854 | ftidx: NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK) && |
10855 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
10856 | ftidx: NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK)) |
10857 | return -EINVAL; |
10858 | settings->psk = nla_data(nla: info->attrs[NL80211_ATTR_PMK]); |
10859 | } |
10860 | |
10861 | if (info->attrs[NL80211_ATTR_SAE_PASSWORD]) { |
10862 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
10863 | ftidx: NL80211_EXT_FEATURE_SAE_OFFLOAD) && |
10864 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
10865 | ftidx: NL80211_EXT_FEATURE_SAE_OFFLOAD_AP)) |
10866 | return -EINVAL; |
10867 | settings->sae_pwd = |
10868 | nla_data(nla: info->attrs[NL80211_ATTR_SAE_PASSWORD]); |
10869 | settings->sae_pwd_len = |
10870 | nla_len(nla: info->attrs[NL80211_ATTR_SAE_PASSWORD]); |
10871 | } |
10872 | |
10873 | if (info->attrs[NL80211_ATTR_SAE_PWE]) |
10874 | settings->sae_pwe = |
10875 | nla_get_u8(nla: info->attrs[NL80211_ATTR_SAE_PWE]); |
10876 | else |
10877 | settings->sae_pwe = NL80211_SAE_PWE_UNSPECIFIED; |
10878 | |
10879 | return 0; |
10880 | } |
10881 | |
10882 | static struct cfg80211_bss *nl80211_assoc_bss(struct cfg80211_registered_device *rdev, |
10883 | const u8 *ssid, int ssid_len, |
10884 | struct nlattr **attrs, |
10885 | int assoc_link_id, int link_id) |
10886 | { |
10887 | struct ieee80211_channel *chan; |
10888 | struct cfg80211_bss *bss; |
10889 | const u8 *bssid; |
10890 | u32 freq, use_for = 0; |
10891 | |
10892 | if (!attrs[NL80211_ATTR_MAC] || !attrs[NL80211_ATTR_WIPHY_FREQ]) |
10893 | return ERR_PTR(error: -EINVAL); |
10894 | |
10895 | bssid = nla_data(nla: attrs[NL80211_ATTR_MAC]); |
10896 | |
10897 | freq = MHZ_TO_KHZ(nla_get_u32(attrs[NL80211_ATTR_WIPHY_FREQ])); |
10898 | if (attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]) |
10899 | freq += nla_get_u32(nla: attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]); |
10900 | |
10901 | chan = nl80211_get_valid_chan(wiphy: &rdev->wiphy, freq); |
10902 | if (!chan) |
10903 | return ERR_PTR(error: -EINVAL); |
10904 | |
10905 | if (assoc_link_id >= 0) |
10906 | use_for = NL80211_BSS_USE_FOR_MLD_LINK; |
10907 | if (assoc_link_id == link_id) |
10908 | use_for |= NL80211_BSS_USE_FOR_NORMAL; |
10909 | |
10910 | bss = __cfg80211_get_bss(wiphy: &rdev->wiphy, channel: chan, bssid, |
10911 | ssid, ssid_len, |
10912 | bss_type: IEEE80211_BSS_TYPE_ESS, |
10913 | privacy: IEEE80211_PRIVACY_ANY, |
10914 | use_for); |
10915 | if (!bss) |
10916 | return ERR_PTR(error: -ENOENT); |
10917 | |
10918 | return bss; |
10919 | } |
10920 | |
10921 | static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) |
10922 | { |
10923 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
10924 | struct net_device *dev = info->user_ptr[1]; |
10925 | struct cfg80211_assoc_request req = {}; |
10926 | struct nlattr **attrs = NULL; |
10927 | const u8 *ap_addr, *ssid; |
10928 | unsigned int link_id; |
10929 | int err, ssid_len; |
10930 | |
10931 | if (dev->ieee80211_ptr->conn_owner_nlportid && |
10932 | dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid) |
10933 | return -EPERM; |
10934 | |
10935 | if (!info->attrs[NL80211_ATTR_SSID]) |
10936 | return -EINVAL; |
10937 | |
10938 | if (!rdev->ops->assoc) |
10939 | return -EOPNOTSUPP; |
10940 | |
10941 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && |
10942 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) |
10943 | return -EOPNOTSUPP; |
10944 | |
10945 | ssid = nla_data(nla: info->attrs[NL80211_ATTR_SSID]); |
10946 | ssid_len = nla_len(nla: info->attrs[NL80211_ATTR_SSID]); |
10947 | |
10948 | if (info->attrs[NL80211_ATTR_IE]) { |
10949 | req.ie = nla_data(nla: info->attrs[NL80211_ATTR_IE]); |
10950 | req.ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); |
10951 | |
10952 | if (cfg80211_find_ext_elem(ext_eid: WLAN_EID_EXT_NON_INHERITANCE, |
10953 | ies: req.ie, len: req.ie_len)) { |
10954 | NL_SET_ERR_MSG_ATTR(info->extack, |
10955 | info->attrs[NL80211_ATTR_IE], |
10956 | "non-inheritance makes no sense" ); |
10957 | return -EINVAL; |
10958 | } |
10959 | } |
10960 | |
10961 | if (info->attrs[NL80211_ATTR_USE_MFP]) { |
10962 | enum nl80211_mfp mfp = |
10963 | nla_get_u32(nla: info->attrs[NL80211_ATTR_USE_MFP]); |
10964 | if (mfp == NL80211_MFP_REQUIRED) |
10965 | req.use_mfp = true; |
10966 | else if (mfp != NL80211_MFP_NO) |
10967 | return -EINVAL; |
10968 | } |
10969 | |
10970 | if (info->attrs[NL80211_ATTR_PREV_BSSID]) |
10971 | req.prev_bssid = nla_data(nla: info->attrs[NL80211_ATTR_PREV_BSSID]); |
10972 | |
10973 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_DISABLE_HT])) |
10974 | req.flags |= ASSOC_REQ_DISABLE_HT; |
10975 | |
10976 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) |
10977 | memcpy(&req.ht_capa_mask, |
10978 | nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]), |
10979 | sizeof(req.ht_capa_mask)); |
10980 | |
10981 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) { |
10982 | if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) |
10983 | return -EINVAL; |
10984 | memcpy(&req.ht_capa, |
10985 | nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]), |
10986 | sizeof(req.ht_capa)); |
10987 | } |
10988 | |
10989 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_DISABLE_VHT])) |
10990 | req.flags |= ASSOC_REQ_DISABLE_VHT; |
10991 | |
10992 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_DISABLE_HE])) |
10993 | req.flags |= ASSOC_REQ_DISABLE_HE; |
10994 | |
10995 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_DISABLE_EHT])) |
10996 | req.flags |= ASSOC_REQ_DISABLE_EHT; |
10997 | |
10998 | if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) |
10999 | memcpy(&req.vht_capa_mask, |
11000 | nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]), |
11001 | sizeof(req.vht_capa_mask)); |
11002 | |
11003 | if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) { |
11004 | if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) |
11005 | return -EINVAL; |
11006 | memcpy(&req.vht_capa, |
11007 | nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]), |
11008 | sizeof(req.vht_capa)); |
11009 | } |
11010 | |
11011 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_USE_RRM])) { |
11012 | if (!((rdev->wiphy.features & |
11013 | NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) && |
11014 | (rdev->wiphy.features & NL80211_FEATURE_QUIET)) && |
11015 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
11016 | ftidx: NL80211_EXT_FEATURE_RRM)) |
11017 | return -EINVAL; |
11018 | req.flags |= ASSOC_REQ_USE_RRM; |
11019 | } |
11020 | |
11021 | if (info->attrs[NL80211_ATTR_FILS_KEK]) { |
11022 | req.fils_kek = nla_data(nla: info->attrs[NL80211_ATTR_FILS_KEK]); |
11023 | req.fils_kek_len = nla_len(nla: info->attrs[NL80211_ATTR_FILS_KEK]); |
11024 | if (!info->attrs[NL80211_ATTR_FILS_NONCES]) |
11025 | return -EINVAL; |
11026 | req.fils_nonces = |
11027 | nla_data(nla: info->attrs[NL80211_ATTR_FILS_NONCES]); |
11028 | } |
11029 | |
11030 | if (info->attrs[NL80211_ATTR_S1G_CAPABILITY_MASK]) { |
11031 | if (!info->attrs[NL80211_ATTR_S1G_CAPABILITY]) |
11032 | return -EINVAL; |
11033 | memcpy(&req.s1g_capa_mask, |
11034 | nla_data(info->attrs[NL80211_ATTR_S1G_CAPABILITY_MASK]), |
11035 | sizeof(req.s1g_capa_mask)); |
11036 | } |
11037 | |
11038 | if (info->attrs[NL80211_ATTR_S1G_CAPABILITY]) { |
11039 | if (!info->attrs[NL80211_ATTR_S1G_CAPABILITY_MASK]) |
11040 | return -EINVAL; |
11041 | memcpy(&req.s1g_capa, |
11042 | nla_data(info->attrs[NL80211_ATTR_S1G_CAPABILITY]), |
11043 | sizeof(req.s1g_capa)); |
11044 | } |
11045 | |
11046 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_ASSOC_SPP_AMSDU])) { |
11047 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
11048 | ftidx: NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT)) { |
11049 | GENL_SET_ERR_MSG(info, "SPP A-MSDUs not supported" ); |
11050 | return -EINVAL; |
11051 | } |
11052 | req.flags |= ASSOC_REQ_SPP_AMSDU; |
11053 | } |
11054 | |
11055 | req.link_id = nl80211_link_id_or_invalid(attrs: info->attrs); |
11056 | |
11057 | if (info->attrs[NL80211_ATTR_MLO_LINKS]) { |
11058 | unsigned int attrsize = NUM_NL80211_ATTR * sizeof(*attrs); |
11059 | struct nlattr *link; |
11060 | int rem = 0; |
11061 | |
11062 | if (req.link_id < 0) |
11063 | return -EINVAL; |
11064 | |
11065 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_MLO)) |
11066 | return -EINVAL; |
11067 | |
11068 | if (info->attrs[NL80211_ATTR_MAC] || |
11069 | info->attrs[NL80211_ATTR_WIPHY_FREQ] || |
11070 | !info->attrs[NL80211_ATTR_MLD_ADDR]) |
11071 | return -EINVAL; |
11072 | |
11073 | req.ap_mld_addr = nla_data(nla: info->attrs[NL80211_ATTR_MLD_ADDR]); |
11074 | ap_addr = req.ap_mld_addr; |
11075 | |
11076 | attrs = kzalloc(size: attrsize, GFP_KERNEL); |
11077 | if (!attrs) |
11078 | return -ENOMEM; |
11079 | |
11080 | nla_for_each_nested(link, |
11081 | info->attrs[NL80211_ATTR_MLO_LINKS], |
11082 | rem) { |
11083 | memset(attrs, 0, attrsize); |
11084 | |
11085 | nla_parse_nested(tb: attrs, maxtype: NL80211_ATTR_MAX, |
11086 | nla: link, NULL, NULL); |
11087 | |
11088 | if (!attrs[NL80211_ATTR_MLO_LINK_ID]) { |
11089 | err = -EINVAL; |
11090 | NL_SET_BAD_ATTR(info->extack, link); |
11091 | goto free; |
11092 | } |
11093 | |
11094 | link_id = nla_get_u8(nla: attrs[NL80211_ATTR_MLO_LINK_ID]); |
11095 | /* cannot use the same link ID again */ |
11096 | if (req.links[link_id].bss) { |
11097 | err = -EINVAL; |
11098 | NL_SET_BAD_ATTR(info->extack, link); |
11099 | goto free; |
11100 | } |
11101 | req.links[link_id].bss = |
11102 | nl80211_assoc_bss(rdev, ssid, ssid_len, attrs, |
11103 | assoc_link_id: req.link_id, link_id); |
11104 | if (IS_ERR(ptr: req.links[link_id].bss)) { |
11105 | err = PTR_ERR(ptr: req.links[link_id].bss); |
11106 | req.links[link_id].bss = NULL; |
11107 | NL_SET_ERR_MSG_ATTR(info->extack, |
11108 | link, "Error fetching BSS for link" ); |
11109 | goto free; |
11110 | } |
11111 | |
11112 | if (attrs[NL80211_ATTR_IE]) { |
11113 | req.links[link_id].elems = |
11114 | nla_data(nla: attrs[NL80211_ATTR_IE]); |
11115 | req.links[link_id].elems_len = |
11116 | nla_len(nla: attrs[NL80211_ATTR_IE]); |
11117 | |
11118 | if (cfg80211_find_elem(eid: WLAN_EID_FRAGMENT, |
11119 | ies: req.links[link_id].elems, |
11120 | len: req.links[link_id].elems_len)) { |
11121 | NL_SET_ERR_MSG_ATTR(info->extack, |
11122 | attrs[NL80211_ATTR_IE], |
11123 | "cannot deal with fragmentation" ); |
11124 | err = -EINVAL; |
11125 | goto free; |
11126 | } |
11127 | |
11128 | if (cfg80211_find_ext_elem(ext_eid: WLAN_EID_EXT_NON_INHERITANCE, |
11129 | ies: req.links[link_id].elems, |
11130 | len: req.links[link_id].elems_len)) { |
11131 | NL_SET_ERR_MSG_ATTR(info->extack, |
11132 | attrs[NL80211_ATTR_IE], |
11133 | "cannot deal with non-inheritance" ); |
11134 | err = -EINVAL; |
11135 | goto free; |
11136 | } |
11137 | } |
11138 | |
11139 | req.links[link_id].disabled = |
11140 | nla_get_flag(nla: attrs[NL80211_ATTR_MLO_LINK_DISABLED]); |
11141 | } |
11142 | |
11143 | if (!req.links[req.link_id].bss) { |
11144 | err = -EINVAL; |
11145 | goto free; |
11146 | } |
11147 | |
11148 | if (req.links[req.link_id].elems_len) { |
11149 | GENL_SET_ERR_MSG(info, |
11150 | "cannot have per-link elems on assoc link" ); |
11151 | err = -EINVAL; |
11152 | goto free; |
11153 | } |
11154 | |
11155 | if (req.links[req.link_id].disabled) { |
11156 | GENL_SET_ERR_MSG(info, |
11157 | "cannot have assoc link disabled" ); |
11158 | err = -EINVAL; |
11159 | goto free; |
11160 | } |
11161 | |
11162 | kfree(objp: attrs); |
11163 | attrs = NULL; |
11164 | } else { |
11165 | if (req.link_id >= 0) |
11166 | return -EINVAL; |
11167 | |
11168 | req.bss = nl80211_assoc_bss(rdev, ssid, ssid_len, attrs: info->attrs, |
11169 | assoc_link_id: -1, link_id: -1); |
11170 | if (IS_ERR(ptr: req.bss)) |
11171 | return PTR_ERR(ptr: req.bss); |
11172 | ap_addr = req.bss->bssid; |
11173 | } |
11174 | |
11175 | err = nl80211_crypto_settings(rdev, info, settings: &req.crypto, cipher_limit: 1); |
11176 | if (!err) { |
11177 | struct nlattr *link; |
11178 | int rem = 0; |
11179 | |
11180 | err = cfg80211_mlme_assoc(rdev, dev, req: &req, |
11181 | extack: info->extack); |
11182 | |
11183 | if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) { |
11184 | dev->ieee80211_ptr->conn_owner_nlportid = |
11185 | info->snd_portid; |
11186 | memcpy(dev->ieee80211_ptr->disconnect_bssid, |
11187 | ap_addr, ETH_ALEN); |
11188 | } |
11189 | |
11190 | /* Report error from first problematic link */ |
11191 | if (info->attrs[NL80211_ATTR_MLO_LINKS]) { |
11192 | nla_for_each_nested(link, |
11193 | info->attrs[NL80211_ATTR_MLO_LINKS], |
11194 | rem) { |
11195 | struct nlattr *link_id_attr = |
11196 | nla_find_nested(nla: link, attrtype: NL80211_ATTR_MLO_LINK_ID); |
11197 | |
11198 | if (!link_id_attr) |
11199 | continue; |
11200 | |
11201 | link_id = nla_get_u8(nla: link_id_attr); |
11202 | |
11203 | if (link_id == req.link_id) |
11204 | continue; |
11205 | |
11206 | if (!req.links[link_id].error || |
11207 | WARN_ON(req.links[link_id].error > 0)) |
11208 | continue; |
11209 | |
11210 | WARN_ON(err >= 0); |
11211 | |
11212 | NL_SET_BAD_ATTR(info->extack, link); |
11213 | err = req.links[link_id].error; |
11214 | break; |
11215 | } |
11216 | } |
11217 | } |
11218 | |
11219 | free: |
11220 | for (link_id = 0; link_id < ARRAY_SIZE(req.links); link_id++) |
11221 | cfg80211_put_bss(wiphy: &rdev->wiphy, bss: req.links[link_id].bss); |
11222 | cfg80211_put_bss(wiphy: &rdev->wiphy, bss: req.bss); |
11223 | kfree(objp: attrs); |
11224 | |
11225 | return err; |
11226 | } |
11227 | |
11228 | static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) |
11229 | { |
11230 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
11231 | struct net_device *dev = info->user_ptr[1]; |
11232 | const u8 *ie = NULL, *bssid; |
11233 | int ie_len = 0; |
11234 | u16 reason_code; |
11235 | bool local_state_change; |
11236 | |
11237 | if (dev->ieee80211_ptr->conn_owner_nlportid && |
11238 | dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid) |
11239 | return -EPERM; |
11240 | |
11241 | if (!info->attrs[NL80211_ATTR_MAC]) |
11242 | return -EINVAL; |
11243 | |
11244 | if (!info->attrs[NL80211_ATTR_REASON_CODE]) |
11245 | return -EINVAL; |
11246 | |
11247 | if (!rdev->ops->deauth) |
11248 | return -EOPNOTSUPP; |
11249 | |
11250 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && |
11251 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) |
11252 | return -EOPNOTSUPP; |
11253 | |
11254 | bssid = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
11255 | |
11256 | reason_code = nla_get_u16(nla: info->attrs[NL80211_ATTR_REASON_CODE]); |
11257 | if (reason_code == 0) { |
11258 | /* Reason Code 0 is reserved */ |
11259 | return -EINVAL; |
11260 | } |
11261 | |
11262 | if (info->attrs[NL80211_ATTR_IE]) { |
11263 | ie = nla_data(nla: info->attrs[NL80211_ATTR_IE]); |
11264 | ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); |
11265 | } |
11266 | |
11267 | local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; |
11268 | |
11269 | return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason: reason_code, |
11270 | local_state_change); |
11271 | } |
11272 | |
11273 | static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) |
11274 | { |
11275 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
11276 | struct net_device *dev = info->user_ptr[1]; |
11277 | const u8 *ie = NULL, *bssid; |
11278 | int ie_len = 0; |
11279 | u16 reason_code; |
11280 | bool local_state_change; |
11281 | |
11282 | if (dev->ieee80211_ptr->conn_owner_nlportid && |
11283 | dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid) |
11284 | return -EPERM; |
11285 | |
11286 | if (!info->attrs[NL80211_ATTR_MAC]) |
11287 | return -EINVAL; |
11288 | |
11289 | if (!info->attrs[NL80211_ATTR_REASON_CODE]) |
11290 | return -EINVAL; |
11291 | |
11292 | if (!rdev->ops->disassoc) |
11293 | return -EOPNOTSUPP; |
11294 | |
11295 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && |
11296 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) |
11297 | return -EOPNOTSUPP; |
11298 | |
11299 | bssid = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
11300 | |
11301 | reason_code = nla_get_u16(nla: info->attrs[NL80211_ATTR_REASON_CODE]); |
11302 | if (reason_code == 0) { |
11303 | /* Reason Code 0 is reserved */ |
11304 | return -EINVAL; |
11305 | } |
11306 | |
11307 | if (info->attrs[NL80211_ATTR_IE]) { |
11308 | ie = nla_data(nla: info->attrs[NL80211_ATTR_IE]); |
11309 | ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); |
11310 | } |
11311 | |
11312 | local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; |
11313 | |
11314 | return cfg80211_mlme_disassoc(rdev, dev, ap_addr: bssid, ie, ie_len, reason: reason_code, |
11315 | local_state_change); |
11316 | } |
11317 | |
11318 | static bool |
11319 | nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev, |
11320 | int mcast_rate[NUM_NL80211_BANDS], |
11321 | int rateval) |
11322 | { |
11323 | struct wiphy *wiphy = &rdev->wiphy; |
11324 | bool found = false; |
11325 | int band, i; |
11326 | |
11327 | for (band = 0; band < NUM_NL80211_BANDS; band++) { |
11328 | struct ieee80211_supported_band *sband; |
11329 | |
11330 | sband = wiphy->bands[band]; |
11331 | if (!sband) |
11332 | continue; |
11333 | |
11334 | for (i = 0; i < sband->n_bitrates; i++) { |
11335 | if (sband->bitrates[i].bitrate == rateval) { |
11336 | mcast_rate[band] = i + 1; |
11337 | found = true; |
11338 | break; |
11339 | } |
11340 | } |
11341 | } |
11342 | |
11343 | return found; |
11344 | } |
11345 | |
11346 | static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) |
11347 | { |
11348 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
11349 | struct net_device *dev = info->user_ptr[1]; |
11350 | struct cfg80211_ibss_params ibss; |
11351 | struct wiphy *wiphy; |
11352 | struct cfg80211_cached_keys *connkeys = NULL; |
11353 | int err; |
11354 | |
11355 | memset(&ibss, 0, sizeof(ibss)); |
11356 | |
11357 | if (!info->attrs[NL80211_ATTR_SSID] || |
11358 | !nla_len(nla: info->attrs[NL80211_ATTR_SSID])) |
11359 | return -EINVAL; |
11360 | |
11361 | ibss.beacon_interval = 100; |
11362 | |
11363 | if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) |
11364 | ibss.beacon_interval = |
11365 | nla_get_u32(nla: info->attrs[NL80211_ATTR_BEACON_INTERVAL]); |
11366 | |
11367 | err = cfg80211_validate_beacon_int(rdev, iftype: NL80211_IFTYPE_ADHOC, |
11368 | beacon_int: ibss.beacon_interval); |
11369 | if (err) |
11370 | return err; |
11371 | |
11372 | if (!rdev->ops->join_ibss) |
11373 | return -EOPNOTSUPP; |
11374 | |
11375 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) |
11376 | return -EOPNOTSUPP; |
11377 | |
11378 | wiphy = &rdev->wiphy; |
11379 | |
11380 | if (info->attrs[NL80211_ATTR_MAC]) { |
11381 | ibss.bssid = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
11382 | |
11383 | if (!is_valid_ether_addr(addr: ibss.bssid)) |
11384 | return -EINVAL; |
11385 | } |
11386 | ibss.ssid = nla_data(nla: info->attrs[NL80211_ATTR_SSID]); |
11387 | ibss.ssid_len = nla_len(nla: info->attrs[NL80211_ATTR_SSID]); |
11388 | |
11389 | if (info->attrs[NL80211_ATTR_IE]) { |
11390 | ibss.ie = nla_data(nla: info->attrs[NL80211_ATTR_IE]); |
11391 | ibss.ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); |
11392 | } |
11393 | |
11394 | err = nl80211_parse_chandef(rdev, info, chandef: &ibss.chandef); |
11395 | if (err) |
11396 | return err; |
11397 | |
11398 | if (!cfg80211_reg_can_beacon(wiphy: &rdev->wiphy, chandef: &ibss.chandef, |
11399 | iftype: NL80211_IFTYPE_ADHOC)) |
11400 | return -EINVAL; |
11401 | |
11402 | switch (ibss.chandef.width) { |
11403 | case NL80211_CHAN_WIDTH_5: |
11404 | case NL80211_CHAN_WIDTH_10: |
11405 | case NL80211_CHAN_WIDTH_20_NOHT: |
11406 | break; |
11407 | case NL80211_CHAN_WIDTH_20: |
11408 | case NL80211_CHAN_WIDTH_40: |
11409 | if (!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)) |
11410 | return -EINVAL; |
11411 | break; |
11412 | case NL80211_CHAN_WIDTH_80: |
11413 | case NL80211_CHAN_WIDTH_80P80: |
11414 | case NL80211_CHAN_WIDTH_160: |
11415 | if (!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)) |
11416 | return -EINVAL; |
11417 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
11418 | ftidx: NL80211_EXT_FEATURE_VHT_IBSS)) |
11419 | return -EINVAL; |
11420 | break; |
11421 | case NL80211_CHAN_WIDTH_320: |
11422 | return -EINVAL; |
11423 | default: |
11424 | return -EINVAL; |
11425 | } |
11426 | |
11427 | ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; |
11428 | ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; |
11429 | |
11430 | if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { |
11431 | u8 *rates = |
11432 | nla_data(nla: info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); |
11433 | int n_rates = |
11434 | nla_len(nla: info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); |
11435 | struct ieee80211_supported_band *sband = |
11436 | wiphy->bands[ibss.chandef.chan->band]; |
11437 | |
11438 | err = ieee80211_get_ratemask(sband, rates, n_rates, |
11439 | mask: &ibss.basic_rates); |
11440 | if (err) |
11441 | return err; |
11442 | } |
11443 | |
11444 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) |
11445 | memcpy(&ibss.ht_capa_mask, |
11446 | nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]), |
11447 | sizeof(ibss.ht_capa_mask)); |
11448 | |
11449 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) { |
11450 | if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) |
11451 | return -EINVAL; |
11452 | memcpy(&ibss.ht_capa, |
11453 | nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]), |
11454 | sizeof(ibss.ht_capa)); |
11455 | } |
11456 | |
11457 | if (info->attrs[NL80211_ATTR_MCAST_RATE] && |
11458 | !nl80211_parse_mcast_rate(rdev, mcast_rate: ibss.mcast_rate, |
11459 | rateval: nla_get_u32(nla: info->attrs[NL80211_ATTR_MCAST_RATE]))) |
11460 | return -EINVAL; |
11461 | |
11462 | if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) { |
11463 | bool no_ht = false; |
11464 | |
11465 | connkeys = nl80211_parse_connkeys(rdev, info, no_ht: &no_ht); |
11466 | if (IS_ERR(ptr: connkeys)) |
11467 | return PTR_ERR(ptr: connkeys); |
11468 | |
11469 | if ((ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) && |
11470 | no_ht) { |
11471 | kfree_sensitive(objp: connkeys); |
11472 | return -EINVAL; |
11473 | } |
11474 | } |
11475 | |
11476 | ibss.control_port = |
11477 | nla_get_flag(nla: info->attrs[NL80211_ATTR_CONTROL_PORT]); |
11478 | |
11479 | if (info->attrs[NL80211_ATTR_CONTROL_PORT_OVER_NL80211]) { |
11480 | int r = validate_pae_over_nl80211(rdev, info); |
11481 | |
11482 | if (r < 0) { |
11483 | kfree_sensitive(objp: connkeys); |
11484 | return r; |
11485 | } |
11486 | |
11487 | ibss.control_port_over_nl80211 = true; |
11488 | } |
11489 | |
11490 | ibss.userspace_handles_dfs = |
11491 | nla_get_flag(nla: info->attrs[NL80211_ATTR_HANDLE_DFS]); |
11492 | |
11493 | err = __cfg80211_join_ibss(rdev, dev, params: &ibss, connkeys); |
11494 | if (err) |
11495 | kfree_sensitive(objp: connkeys); |
11496 | else if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) |
11497 | dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid; |
11498 | |
11499 | return err; |
11500 | } |
11501 | |
11502 | static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info) |
11503 | { |
11504 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
11505 | struct net_device *dev = info->user_ptr[1]; |
11506 | |
11507 | if (!rdev->ops->leave_ibss) |
11508 | return -EOPNOTSUPP; |
11509 | |
11510 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) |
11511 | return -EOPNOTSUPP; |
11512 | |
11513 | return cfg80211_leave_ibss(rdev, dev, nowext: false); |
11514 | } |
11515 | |
11516 | static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info) |
11517 | { |
11518 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
11519 | struct net_device *dev = info->user_ptr[1]; |
11520 | int mcast_rate[NUM_NL80211_BANDS]; |
11521 | u32 nla_rate; |
11522 | |
11523 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && |
11524 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && |
11525 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_OCB) |
11526 | return -EOPNOTSUPP; |
11527 | |
11528 | if (!rdev->ops->set_mcast_rate) |
11529 | return -EOPNOTSUPP; |
11530 | |
11531 | memset(mcast_rate, 0, sizeof(mcast_rate)); |
11532 | |
11533 | if (!info->attrs[NL80211_ATTR_MCAST_RATE]) |
11534 | return -EINVAL; |
11535 | |
11536 | nla_rate = nla_get_u32(nla: info->attrs[NL80211_ATTR_MCAST_RATE]); |
11537 | if (!nl80211_parse_mcast_rate(rdev, mcast_rate, rateval: nla_rate)) |
11538 | return -EINVAL; |
11539 | |
11540 | return rdev_set_mcast_rate(rdev, dev, mcast_rate); |
11541 | } |
11542 | |
11543 | static struct sk_buff * |
11544 | __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev, |
11545 | struct wireless_dev *wdev, int approxlen, |
11546 | u32 portid, u32 seq, enum nl80211_commands cmd, |
11547 | enum nl80211_attrs attr, |
11548 | const struct nl80211_vendor_cmd_info *info, |
11549 | gfp_t gfp) |
11550 | { |
11551 | struct sk_buff *skb; |
11552 | void *hdr; |
11553 | struct nlattr *data; |
11554 | |
11555 | skb = nlmsg_new(payload: approxlen + 100, flags: gfp); |
11556 | if (!skb) |
11557 | return NULL; |
11558 | |
11559 | hdr = nl80211hdr_put(skb, portid, seq, flags: 0, cmd); |
11560 | if (!hdr) { |
11561 | kfree_skb(skb); |
11562 | return NULL; |
11563 | } |
11564 | |
11565 | if (nla_put_u32(skb, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx)) |
11566 | goto nla_put_failure; |
11567 | |
11568 | if (info) { |
11569 | if (nla_put_u32(skb, attrtype: NL80211_ATTR_VENDOR_ID, |
11570 | value: info->vendor_id)) |
11571 | goto nla_put_failure; |
11572 | if (nla_put_u32(skb, attrtype: NL80211_ATTR_VENDOR_SUBCMD, |
11573 | value: info->subcmd)) |
11574 | goto nla_put_failure; |
11575 | } |
11576 | |
11577 | if (wdev) { |
11578 | if (nla_put_u64_64bit(skb, attrtype: NL80211_ATTR_WDEV, |
11579 | value: wdev_id(wdev), padattr: NL80211_ATTR_PAD)) |
11580 | goto nla_put_failure; |
11581 | if (wdev->netdev && |
11582 | nla_put_u32(skb, attrtype: NL80211_ATTR_IFINDEX, |
11583 | value: wdev->netdev->ifindex)) |
11584 | goto nla_put_failure; |
11585 | } |
11586 | |
11587 | data = nla_nest_start_noflag(skb, attrtype: attr); |
11588 | if (!data) |
11589 | goto nla_put_failure; |
11590 | |
11591 | ((void **)skb->cb)[0] = rdev; |
11592 | ((void **)skb->cb)[1] = hdr; |
11593 | ((void **)skb->cb)[2] = data; |
11594 | |
11595 | return skb; |
11596 | |
11597 | nla_put_failure: |
11598 | kfree_skb(skb); |
11599 | return NULL; |
11600 | } |
11601 | |
11602 | struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, |
11603 | struct wireless_dev *wdev, |
11604 | enum nl80211_commands cmd, |
11605 | enum nl80211_attrs attr, |
11606 | unsigned int portid, |
11607 | int vendor_event_idx, |
11608 | int approxlen, gfp_t gfp) |
11609 | { |
11610 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
11611 | const struct nl80211_vendor_cmd_info *info; |
11612 | |
11613 | switch (cmd) { |
11614 | case NL80211_CMD_TESTMODE: |
11615 | if (WARN_ON(vendor_event_idx != -1)) |
11616 | return NULL; |
11617 | info = NULL; |
11618 | break; |
11619 | case NL80211_CMD_VENDOR: |
11620 | if (WARN_ON(vendor_event_idx < 0 || |
11621 | vendor_event_idx >= wiphy->n_vendor_events)) |
11622 | return NULL; |
11623 | info = &wiphy->vendor_events[vendor_event_idx]; |
11624 | break; |
11625 | default: |
11626 | WARN_ON(1); |
11627 | return NULL; |
11628 | } |
11629 | |
11630 | return __cfg80211_alloc_vendor_skb(rdev, wdev, approxlen, portid, seq: 0, |
11631 | cmd, attr, info, gfp); |
11632 | } |
11633 | EXPORT_SYMBOL(__cfg80211_alloc_event_skb); |
11634 | |
11635 | void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp) |
11636 | { |
11637 | struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; |
11638 | void *hdr = ((void **)skb->cb)[1]; |
11639 | struct nlmsghdr *nlhdr = nlmsg_hdr(skb); |
11640 | struct nlattr *data = ((void **)skb->cb)[2]; |
11641 | enum nl80211_multicast_groups mcgrp = NL80211_MCGRP_TESTMODE; |
11642 | |
11643 | /* clear CB data for netlink core to own from now on */ |
11644 | memset(skb->cb, 0, sizeof(skb->cb)); |
11645 | |
11646 | nla_nest_end(skb, start: data); |
11647 | genlmsg_end(skb, hdr); |
11648 | |
11649 | if (nlhdr->nlmsg_pid) { |
11650 | genlmsg_unicast(net: wiphy_net(wiphy: &rdev->wiphy), skb, |
11651 | portid: nlhdr->nlmsg_pid); |
11652 | } else { |
11653 | if (data->nla_type == NL80211_ATTR_VENDOR_DATA) |
11654 | mcgrp = NL80211_MCGRP_VENDOR; |
11655 | |
11656 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), |
11657 | skb, portid: 0, group: mcgrp, flags: gfp); |
11658 | } |
11659 | } |
11660 | EXPORT_SYMBOL(__cfg80211_send_event_skb); |
11661 | |
11662 | #ifdef CONFIG_NL80211_TESTMODE |
11663 | static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info) |
11664 | { |
11665 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
11666 | struct wireless_dev *wdev; |
11667 | int err; |
11668 | |
11669 | lockdep_assert_held(&rdev->wiphy.mtx); |
11670 | |
11671 | wdev = __cfg80211_wdev_from_attrs(rdev, netns: genl_info_net(info), |
11672 | attrs: info->attrs); |
11673 | |
11674 | if (!rdev->ops->testmode_cmd) |
11675 | return -EOPNOTSUPP; |
11676 | |
11677 | if (IS_ERR(ptr: wdev)) { |
11678 | err = PTR_ERR(ptr: wdev); |
11679 | if (err != -EINVAL) |
11680 | return err; |
11681 | wdev = NULL; |
11682 | } else if (wdev->wiphy != &rdev->wiphy) { |
11683 | return -EINVAL; |
11684 | } |
11685 | |
11686 | if (!info->attrs[NL80211_ATTR_TESTDATA]) |
11687 | return -EINVAL; |
11688 | |
11689 | rdev->cur_cmd_info = info; |
11690 | err = rdev_testmode_cmd(rdev, wdev, |
11691 | data: nla_data(nla: info->attrs[NL80211_ATTR_TESTDATA]), |
11692 | len: nla_len(nla: info->attrs[NL80211_ATTR_TESTDATA])); |
11693 | rdev->cur_cmd_info = NULL; |
11694 | |
11695 | return err; |
11696 | } |
11697 | |
11698 | static int nl80211_testmode_dump(struct sk_buff *skb, |
11699 | struct netlink_callback *cb) |
11700 | { |
11701 | struct cfg80211_registered_device *rdev; |
11702 | struct nlattr **attrbuf = NULL; |
11703 | int err; |
11704 | long phy_idx; |
11705 | void *data = NULL; |
11706 | int data_len = 0; |
11707 | |
11708 | rtnl_lock(); |
11709 | |
11710 | if (cb->args[0]) { |
11711 | /* |
11712 | * 0 is a valid index, but not valid for args[0], |
11713 | * so we need to offset by 1. |
11714 | */ |
11715 | phy_idx = cb->args[0] - 1; |
11716 | |
11717 | rdev = cfg80211_rdev_by_wiphy_idx(wiphy_idx: phy_idx); |
11718 | if (!rdev) { |
11719 | err = -ENOENT; |
11720 | goto out_err; |
11721 | } |
11722 | } else { |
11723 | attrbuf = kcalloc(n: NUM_NL80211_ATTR, size: sizeof(*attrbuf), |
11724 | GFP_KERNEL); |
11725 | if (!attrbuf) { |
11726 | err = -ENOMEM; |
11727 | goto out_err; |
11728 | } |
11729 | |
11730 | err = nlmsg_parse_deprecated(nlh: cb->nlh, |
11731 | GENL_HDRLEN + nl80211_fam.hdrsize, |
11732 | tb: attrbuf, maxtype: nl80211_fam.maxattr, |
11733 | policy: nl80211_policy, NULL); |
11734 | if (err) |
11735 | goto out_err; |
11736 | |
11737 | rdev = __cfg80211_rdev_from_attrs(netns: sock_net(sk: skb->sk), attrs: attrbuf); |
11738 | if (IS_ERR(ptr: rdev)) { |
11739 | err = PTR_ERR(ptr: rdev); |
11740 | goto out_err; |
11741 | } |
11742 | phy_idx = rdev->wiphy_idx; |
11743 | |
11744 | if (attrbuf[NL80211_ATTR_TESTDATA]) |
11745 | cb->args[1] = (long)attrbuf[NL80211_ATTR_TESTDATA]; |
11746 | } |
11747 | |
11748 | if (cb->args[1]) { |
11749 | data = nla_data(nla: (void *)cb->args[1]); |
11750 | data_len = nla_len(nla: (void *)cb->args[1]); |
11751 | } |
11752 | |
11753 | if (!rdev->ops->testmode_dump) { |
11754 | err = -EOPNOTSUPP; |
11755 | goto out_err; |
11756 | } |
11757 | |
11758 | while (1) { |
11759 | void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).portid, |
11760 | seq: cb->nlh->nlmsg_seq, NLM_F_MULTI, |
11761 | cmd: NL80211_CMD_TESTMODE); |
11762 | struct nlattr *tmdata; |
11763 | |
11764 | if (!hdr) |
11765 | break; |
11766 | |
11767 | if (nla_put_u32(skb, attrtype: NL80211_ATTR_WIPHY, value: phy_idx)) { |
11768 | genlmsg_cancel(skb, hdr); |
11769 | break; |
11770 | } |
11771 | |
11772 | tmdata = nla_nest_start_noflag(skb, attrtype: NL80211_ATTR_TESTDATA); |
11773 | if (!tmdata) { |
11774 | genlmsg_cancel(skb, hdr); |
11775 | break; |
11776 | } |
11777 | err = rdev_testmode_dump(rdev, skb, cb, data, len: data_len); |
11778 | nla_nest_end(skb, start: tmdata); |
11779 | |
11780 | if (err == -ENOBUFS || err == -ENOENT) { |
11781 | genlmsg_cancel(skb, hdr); |
11782 | break; |
11783 | } else if (err) { |
11784 | genlmsg_cancel(skb, hdr); |
11785 | goto out_err; |
11786 | } |
11787 | |
11788 | genlmsg_end(skb, hdr); |
11789 | } |
11790 | |
11791 | err = skb->len; |
11792 | /* see above */ |
11793 | cb->args[0] = phy_idx + 1; |
11794 | out_err: |
11795 | kfree(objp: attrbuf); |
11796 | rtnl_unlock(); |
11797 | return err; |
11798 | } |
11799 | #endif |
11800 | |
11801 | static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) |
11802 | { |
11803 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
11804 | struct net_device *dev = info->user_ptr[1]; |
11805 | struct cfg80211_connect_params connect; |
11806 | struct wiphy *wiphy; |
11807 | struct cfg80211_cached_keys *connkeys = NULL; |
11808 | u32 freq = 0; |
11809 | int err; |
11810 | |
11811 | memset(&connect, 0, sizeof(connect)); |
11812 | |
11813 | if (!info->attrs[NL80211_ATTR_SSID] || |
11814 | !nla_len(nla: info->attrs[NL80211_ATTR_SSID])) |
11815 | return -EINVAL; |
11816 | |
11817 | if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { |
11818 | connect.auth_type = |
11819 | nla_get_u32(nla: info->attrs[NL80211_ATTR_AUTH_TYPE]); |
11820 | if (!nl80211_valid_auth_type(rdev, auth_type: connect.auth_type, |
11821 | NL80211_CMD_CONNECT)) |
11822 | return -EINVAL; |
11823 | } else |
11824 | connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; |
11825 | |
11826 | connect.privacy = info->attrs[NL80211_ATTR_PRIVACY]; |
11827 | |
11828 | if (info->attrs[NL80211_ATTR_WANT_1X_4WAY_HS] && |
11829 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
11830 | ftidx: NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X)) |
11831 | return -EINVAL; |
11832 | connect.want_1x = info->attrs[NL80211_ATTR_WANT_1X_4WAY_HS]; |
11833 | |
11834 | err = nl80211_crypto_settings(rdev, info, settings: &connect.crypto, |
11835 | NL80211_MAX_NR_CIPHER_SUITES); |
11836 | if (err) |
11837 | return err; |
11838 | |
11839 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && |
11840 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) |
11841 | return -EOPNOTSUPP; |
11842 | |
11843 | wiphy = &rdev->wiphy; |
11844 | |
11845 | connect.bg_scan_period = -1; |
11846 | if (info->attrs[NL80211_ATTR_BG_SCAN_PERIOD] && |
11847 | (wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)) { |
11848 | connect.bg_scan_period = |
11849 | nla_get_u16(nla: info->attrs[NL80211_ATTR_BG_SCAN_PERIOD]); |
11850 | } |
11851 | |
11852 | if (info->attrs[NL80211_ATTR_MAC]) |
11853 | connect.bssid = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
11854 | else if (info->attrs[NL80211_ATTR_MAC_HINT]) |
11855 | connect.bssid_hint = |
11856 | nla_data(nla: info->attrs[NL80211_ATTR_MAC_HINT]); |
11857 | connect.ssid = nla_data(nla: info->attrs[NL80211_ATTR_SSID]); |
11858 | connect.ssid_len = nla_len(nla: info->attrs[NL80211_ATTR_SSID]); |
11859 | |
11860 | if (info->attrs[NL80211_ATTR_IE]) { |
11861 | connect.ie = nla_data(nla: info->attrs[NL80211_ATTR_IE]); |
11862 | connect.ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); |
11863 | } |
11864 | |
11865 | if (info->attrs[NL80211_ATTR_USE_MFP]) { |
11866 | connect.mfp = nla_get_u32(nla: info->attrs[NL80211_ATTR_USE_MFP]); |
11867 | if (connect.mfp == NL80211_MFP_OPTIONAL && |
11868 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
11869 | ftidx: NL80211_EXT_FEATURE_MFP_OPTIONAL)) |
11870 | return -EOPNOTSUPP; |
11871 | } else { |
11872 | connect.mfp = NL80211_MFP_NO; |
11873 | } |
11874 | |
11875 | if (info->attrs[NL80211_ATTR_PREV_BSSID]) |
11876 | connect.prev_bssid = |
11877 | nla_data(nla: info->attrs[NL80211_ATTR_PREV_BSSID]); |
11878 | |
11879 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) |
11880 | freq = MHZ_TO_KHZ(nla_get_u32( |
11881 | info->attrs[NL80211_ATTR_WIPHY_FREQ])); |
11882 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]) |
11883 | freq += |
11884 | nla_get_u32(nla: info->attrs[NL80211_ATTR_WIPHY_FREQ_OFFSET]); |
11885 | |
11886 | if (freq) { |
11887 | connect.channel = nl80211_get_valid_chan(wiphy, freq); |
11888 | if (!connect.channel) |
11889 | return -EINVAL; |
11890 | } else if (info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]) { |
11891 | freq = nla_get_u32(nla: info->attrs[NL80211_ATTR_WIPHY_FREQ_HINT]); |
11892 | freq = MHZ_TO_KHZ(freq); |
11893 | connect.channel_hint = nl80211_get_valid_chan(wiphy, freq); |
11894 | if (!connect.channel_hint) |
11895 | return -EINVAL; |
11896 | } |
11897 | |
11898 | if (info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNELS]) { |
11899 | connect.edmg.channels = |
11900 | nla_get_u8(nla: info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNELS]); |
11901 | |
11902 | if (info->attrs[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG]) |
11903 | connect.edmg.bw_config = |
11904 | nla_get_u8(nla: info->attrs[NL80211_ATTR_WIPHY_EDMG_BW_CONFIG]); |
11905 | } |
11906 | |
11907 | if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) { |
11908 | connkeys = nl80211_parse_connkeys(rdev, info, NULL); |
11909 | if (IS_ERR(ptr: connkeys)) |
11910 | return PTR_ERR(ptr: connkeys); |
11911 | } |
11912 | |
11913 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_DISABLE_HT])) |
11914 | connect.flags |= ASSOC_REQ_DISABLE_HT; |
11915 | |
11916 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) |
11917 | memcpy(&connect.ht_capa_mask, |
11918 | nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]), |
11919 | sizeof(connect.ht_capa_mask)); |
11920 | |
11921 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) { |
11922 | if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]) { |
11923 | kfree_sensitive(objp: connkeys); |
11924 | return -EINVAL; |
11925 | } |
11926 | memcpy(&connect.ht_capa, |
11927 | nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]), |
11928 | sizeof(connect.ht_capa)); |
11929 | } |
11930 | |
11931 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_DISABLE_VHT])) |
11932 | connect.flags |= ASSOC_REQ_DISABLE_VHT; |
11933 | |
11934 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_DISABLE_HE])) |
11935 | connect.flags |= ASSOC_REQ_DISABLE_HE; |
11936 | |
11937 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_DISABLE_EHT])) |
11938 | connect.flags |= ASSOC_REQ_DISABLE_EHT; |
11939 | |
11940 | if (info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) |
11941 | memcpy(&connect.vht_capa_mask, |
11942 | nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]), |
11943 | sizeof(connect.vht_capa_mask)); |
11944 | |
11945 | if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) { |
11946 | if (!info->attrs[NL80211_ATTR_VHT_CAPABILITY_MASK]) { |
11947 | kfree_sensitive(objp: connkeys); |
11948 | return -EINVAL; |
11949 | } |
11950 | memcpy(&connect.vht_capa, |
11951 | nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]), |
11952 | sizeof(connect.vht_capa)); |
11953 | } |
11954 | |
11955 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_USE_RRM])) { |
11956 | if (!((rdev->wiphy.features & |
11957 | NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) && |
11958 | (rdev->wiphy.features & NL80211_FEATURE_QUIET)) && |
11959 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
11960 | ftidx: NL80211_EXT_FEATURE_RRM)) { |
11961 | kfree_sensitive(objp: connkeys); |
11962 | return -EINVAL; |
11963 | } |
11964 | connect.flags |= ASSOC_REQ_USE_RRM; |
11965 | } |
11966 | |
11967 | connect.pbss = nla_get_flag(nla: info->attrs[NL80211_ATTR_PBSS]); |
11968 | if (connect.pbss && !rdev->wiphy.bands[NL80211_BAND_60GHZ]) { |
11969 | kfree_sensitive(objp: connkeys); |
11970 | return -EOPNOTSUPP; |
11971 | } |
11972 | |
11973 | if (info->attrs[NL80211_ATTR_BSS_SELECT]) { |
11974 | /* bss selection makes no sense if bssid is set */ |
11975 | if (connect.bssid) { |
11976 | kfree_sensitive(objp: connkeys); |
11977 | return -EINVAL; |
11978 | } |
11979 | |
11980 | err = parse_bss_select(nla: info->attrs[NL80211_ATTR_BSS_SELECT], |
11981 | wiphy, bss_select: &connect.bss_select); |
11982 | if (err) { |
11983 | kfree_sensitive(objp: connkeys); |
11984 | return err; |
11985 | } |
11986 | } |
11987 | |
11988 | if (wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
11989 | ftidx: NL80211_EXT_FEATURE_FILS_SK_OFFLOAD) && |
11990 | info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] && |
11991 | info->attrs[NL80211_ATTR_FILS_ERP_REALM] && |
11992 | info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] && |
11993 | info->attrs[NL80211_ATTR_FILS_ERP_RRK]) { |
11994 | connect.fils_erp_username = |
11995 | nla_data(nla: info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]); |
11996 | connect.fils_erp_username_len = |
11997 | nla_len(nla: info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]); |
11998 | connect.fils_erp_realm = |
11999 | nla_data(nla: info->attrs[NL80211_ATTR_FILS_ERP_REALM]); |
12000 | connect.fils_erp_realm_len = |
12001 | nla_len(nla: info->attrs[NL80211_ATTR_FILS_ERP_REALM]); |
12002 | connect.fils_erp_next_seq_num = |
12003 | nla_get_u16( |
12004 | nla: info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM]); |
12005 | connect.fils_erp_rrk = |
12006 | nla_data(nla: info->attrs[NL80211_ATTR_FILS_ERP_RRK]); |
12007 | connect.fils_erp_rrk_len = |
12008 | nla_len(nla: info->attrs[NL80211_ATTR_FILS_ERP_RRK]); |
12009 | } else if (info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] || |
12010 | info->attrs[NL80211_ATTR_FILS_ERP_REALM] || |
12011 | info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] || |
12012 | info->attrs[NL80211_ATTR_FILS_ERP_RRK]) { |
12013 | kfree_sensitive(objp: connkeys); |
12014 | return -EINVAL; |
12015 | } |
12016 | |
12017 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_EXTERNAL_AUTH_SUPPORT])) { |
12018 | if (!info->attrs[NL80211_ATTR_SOCKET_OWNER]) { |
12019 | kfree_sensitive(objp: connkeys); |
12020 | GENL_SET_ERR_MSG(info, |
12021 | "external auth requires connection ownership" ); |
12022 | return -EINVAL; |
12023 | } |
12024 | connect.flags |= CONNECT_REQ_EXTERNAL_AUTH_SUPPORT; |
12025 | } |
12026 | |
12027 | if (nla_get_flag(nla: info->attrs[NL80211_ATTR_MLO_SUPPORT])) |
12028 | connect.flags |= CONNECT_REQ_MLO_SUPPORT; |
12029 | |
12030 | err = cfg80211_connect(rdev, dev, connect: &connect, connkeys, |
12031 | prev_bssid: connect.prev_bssid); |
12032 | if (err) |
12033 | kfree_sensitive(objp: connkeys); |
12034 | |
12035 | if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) { |
12036 | dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid; |
12037 | if (connect.bssid) |
12038 | memcpy(dev->ieee80211_ptr->disconnect_bssid, |
12039 | connect.bssid, ETH_ALEN); |
12040 | else |
12041 | eth_zero_addr(addr: dev->ieee80211_ptr->disconnect_bssid); |
12042 | } |
12043 | |
12044 | return err; |
12045 | } |
12046 | |
12047 | static int nl80211_update_connect_params(struct sk_buff *skb, |
12048 | struct genl_info *info) |
12049 | { |
12050 | struct cfg80211_connect_params connect = {}; |
12051 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
12052 | struct net_device *dev = info->user_ptr[1]; |
12053 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
12054 | bool fils_sk_offload; |
12055 | u32 auth_type; |
12056 | u32 changed = 0; |
12057 | |
12058 | if (!rdev->ops->update_connect_params) |
12059 | return -EOPNOTSUPP; |
12060 | |
12061 | if (info->attrs[NL80211_ATTR_IE]) { |
12062 | connect.ie = nla_data(nla: info->attrs[NL80211_ATTR_IE]); |
12063 | connect.ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); |
12064 | changed |= UPDATE_ASSOC_IES; |
12065 | } |
12066 | |
12067 | fils_sk_offload = wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
12068 | ftidx: NL80211_EXT_FEATURE_FILS_SK_OFFLOAD); |
12069 | |
12070 | /* |
12071 | * when driver supports fils-sk offload all attributes must be |
12072 | * provided. So the else covers "fils-sk-not-all" and |
12073 | * "no-fils-sk-any". |
12074 | */ |
12075 | if (fils_sk_offload && |
12076 | info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] && |
12077 | info->attrs[NL80211_ATTR_FILS_ERP_REALM] && |
12078 | info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] && |
12079 | info->attrs[NL80211_ATTR_FILS_ERP_RRK]) { |
12080 | connect.fils_erp_username = |
12081 | nla_data(nla: info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]); |
12082 | connect.fils_erp_username_len = |
12083 | nla_len(nla: info->attrs[NL80211_ATTR_FILS_ERP_USERNAME]); |
12084 | connect.fils_erp_realm = |
12085 | nla_data(nla: info->attrs[NL80211_ATTR_FILS_ERP_REALM]); |
12086 | connect.fils_erp_realm_len = |
12087 | nla_len(nla: info->attrs[NL80211_ATTR_FILS_ERP_REALM]); |
12088 | connect.fils_erp_next_seq_num = |
12089 | nla_get_u16( |
12090 | nla: info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM]); |
12091 | connect.fils_erp_rrk = |
12092 | nla_data(nla: info->attrs[NL80211_ATTR_FILS_ERP_RRK]); |
12093 | connect.fils_erp_rrk_len = |
12094 | nla_len(nla: info->attrs[NL80211_ATTR_FILS_ERP_RRK]); |
12095 | changed |= UPDATE_FILS_ERP_INFO; |
12096 | } else if (info->attrs[NL80211_ATTR_FILS_ERP_USERNAME] || |
12097 | info->attrs[NL80211_ATTR_FILS_ERP_REALM] || |
12098 | info->attrs[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM] || |
12099 | info->attrs[NL80211_ATTR_FILS_ERP_RRK]) { |
12100 | return -EINVAL; |
12101 | } |
12102 | |
12103 | if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { |
12104 | auth_type = nla_get_u32(nla: info->attrs[NL80211_ATTR_AUTH_TYPE]); |
12105 | if (!nl80211_valid_auth_type(rdev, auth_type, |
12106 | NL80211_CMD_CONNECT)) |
12107 | return -EINVAL; |
12108 | |
12109 | if (auth_type == NL80211_AUTHTYPE_FILS_SK && |
12110 | fils_sk_offload && !(changed & UPDATE_FILS_ERP_INFO)) |
12111 | return -EINVAL; |
12112 | |
12113 | connect.auth_type = auth_type; |
12114 | changed |= UPDATE_AUTH_TYPE; |
12115 | } |
12116 | |
12117 | if (!wdev->connected) |
12118 | return -ENOLINK; |
12119 | |
12120 | return rdev_update_connect_params(rdev, dev, sme: &connect, changed); |
12121 | } |
12122 | |
12123 | static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) |
12124 | { |
12125 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
12126 | struct net_device *dev = info->user_ptr[1]; |
12127 | u16 reason; |
12128 | |
12129 | if (dev->ieee80211_ptr->conn_owner_nlportid && |
12130 | dev->ieee80211_ptr->conn_owner_nlportid != info->snd_portid) |
12131 | return -EPERM; |
12132 | |
12133 | if (!info->attrs[NL80211_ATTR_REASON_CODE]) |
12134 | reason = WLAN_REASON_DEAUTH_LEAVING; |
12135 | else |
12136 | reason = nla_get_u16(nla: info->attrs[NL80211_ATTR_REASON_CODE]); |
12137 | |
12138 | if (reason == 0) |
12139 | return -EINVAL; |
12140 | |
12141 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && |
12142 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) |
12143 | return -EOPNOTSUPP; |
12144 | |
12145 | return cfg80211_disconnect(rdev, dev, reason, wextev: true); |
12146 | } |
12147 | |
12148 | static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info) |
12149 | { |
12150 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
12151 | struct net *net; |
12152 | int err; |
12153 | |
12154 | if (info->attrs[NL80211_ATTR_PID]) { |
12155 | u32 pid = nla_get_u32(nla: info->attrs[NL80211_ATTR_PID]); |
12156 | |
12157 | net = get_net_ns_by_pid(pid); |
12158 | } else if (info->attrs[NL80211_ATTR_NETNS_FD]) { |
12159 | u32 fd = nla_get_u32(nla: info->attrs[NL80211_ATTR_NETNS_FD]); |
12160 | |
12161 | net = get_net_ns_by_fd(fd); |
12162 | } else { |
12163 | return -EINVAL; |
12164 | } |
12165 | |
12166 | if (IS_ERR(ptr: net)) |
12167 | return PTR_ERR(ptr: net); |
12168 | |
12169 | err = 0; |
12170 | |
12171 | /* check if anything to do */ |
12172 | if (!net_eq(net1: wiphy_net(wiphy: &rdev->wiphy), net2: net)) |
12173 | err = cfg80211_switch_netns(rdev, net); |
12174 | |
12175 | put_net(net); |
12176 | return err; |
12177 | } |
12178 | |
12179 | static int nl80211_set_pmksa(struct sk_buff *skb, struct genl_info *info) |
12180 | { |
12181 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
12182 | struct net_device *dev = info->user_ptr[1]; |
12183 | struct cfg80211_pmksa pmksa; |
12184 | bool ap_pmksa_caching_support = false; |
12185 | |
12186 | memset(&pmksa, 0, sizeof(struct cfg80211_pmksa)); |
12187 | |
12188 | ap_pmksa_caching_support = wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
12189 | ftidx: NL80211_EXT_FEATURE_AP_PMKSA_CACHING); |
12190 | |
12191 | if (!info->attrs[NL80211_ATTR_PMKID]) |
12192 | return -EINVAL; |
12193 | |
12194 | pmksa.pmkid = nla_data(nla: info->attrs[NL80211_ATTR_PMKID]); |
12195 | |
12196 | if (info->attrs[NL80211_ATTR_MAC]) { |
12197 | pmksa.bssid = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
12198 | } else if (info->attrs[NL80211_ATTR_SSID] && |
12199 | info->attrs[NL80211_ATTR_FILS_CACHE_ID] && |
12200 | info->attrs[NL80211_ATTR_PMK]) { |
12201 | pmksa.ssid = nla_data(nla: info->attrs[NL80211_ATTR_SSID]); |
12202 | pmksa.ssid_len = nla_len(nla: info->attrs[NL80211_ATTR_SSID]); |
12203 | pmksa.cache_id = nla_data(nla: info->attrs[NL80211_ATTR_FILS_CACHE_ID]); |
12204 | } else { |
12205 | return -EINVAL; |
12206 | } |
12207 | |
12208 | if (info->attrs[NL80211_ATTR_PMK]) { |
12209 | pmksa.pmk = nla_data(nla: info->attrs[NL80211_ATTR_PMK]); |
12210 | pmksa.pmk_len = nla_len(nla: info->attrs[NL80211_ATTR_PMK]); |
12211 | } |
12212 | |
12213 | if (info->attrs[NL80211_ATTR_PMK_LIFETIME]) |
12214 | pmksa.pmk_lifetime = |
12215 | nla_get_u32(nla: info->attrs[NL80211_ATTR_PMK_LIFETIME]); |
12216 | |
12217 | if (info->attrs[NL80211_ATTR_PMK_REAUTH_THRESHOLD]) |
12218 | pmksa.pmk_reauth_threshold = |
12219 | nla_get_u8(nla: info->attrs[NL80211_ATTR_PMK_REAUTH_THRESHOLD]); |
12220 | |
12221 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && |
12222 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && |
12223 | !((dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP || |
12224 | dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) && |
12225 | ap_pmksa_caching_support)) |
12226 | return -EOPNOTSUPP; |
12227 | |
12228 | if (!rdev->ops->set_pmksa) |
12229 | return -EOPNOTSUPP; |
12230 | |
12231 | return rdev_set_pmksa(rdev, netdev: dev, pmksa: &pmksa); |
12232 | } |
12233 | |
12234 | static int nl80211_del_pmksa(struct sk_buff *skb, struct genl_info *info) |
12235 | { |
12236 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
12237 | struct net_device *dev = info->user_ptr[1]; |
12238 | struct cfg80211_pmksa pmksa; |
12239 | bool sae_offload_support = false; |
12240 | bool owe_offload_support = false; |
12241 | bool ap_pmksa_caching_support = false; |
12242 | |
12243 | memset(&pmksa, 0, sizeof(struct cfg80211_pmksa)); |
12244 | |
12245 | sae_offload_support = wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
12246 | ftidx: NL80211_EXT_FEATURE_SAE_OFFLOAD); |
12247 | owe_offload_support = wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
12248 | ftidx: NL80211_EXT_FEATURE_OWE_OFFLOAD); |
12249 | ap_pmksa_caching_support = wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
12250 | ftidx: NL80211_EXT_FEATURE_AP_PMKSA_CACHING); |
12251 | |
12252 | if (info->attrs[NL80211_ATTR_PMKID]) |
12253 | pmksa.pmkid = nla_data(nla: info->attrs[NL80211_ATTR_PMKID]); |
12254 | |
12255 | if (info->attrs[NL80211_ATTR_MAC]) { |
12256 | pmksa.bssid = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
12257 | } else if (info->attrs[NL80211_ATTR_SSID]) { |
12258 | /* SSID based pmksa flush suppported only for FILS, |
12259 | * OWE/SAE OFFLOAD cases |
12260 | */ |
12261 | if (info->attrs[NL80211_ATTR_FILS_CACHE_ID] && |
12262 | info->attrs[NL80211_ATTR_PMK]) { |
12263 | pmksa.cache_id = nla_data(nla: info->attrs[NL80211_ATTR_FILS_CACHE_ID]); |
12264 | } else if (!sae_offload_support && !owe_offload_support) { |
12265 | return -EINVAL; |
12266 | } |
12267 | pmksa.ssid = nla_data(nla: info->attrs[NL80211_ATTR_SSID]); |
12268 | pmksa.ssid_len = nla_len(nla: info->attrs[NL80211_ATTR_SSID]); |
12269 | } else { |
12270 | return -EINVAL; |
12271 | } |
12272 | |
12273 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && |
12274 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && |
12275 | !((dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP || |
12276 | dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) && |
12277 | ap_pmksa_caching_support)) |
12278 | return -EOPNOTSUPP; |
12279 | |
12280 | if (!rdev->ops->del_pmksa) |
12281 | return -EOPNOTSUPP; |
12282 | |
12283 | return rdev_del_pmksa(rdev, netdev: dev, pmksa: &pmksa); |
12284 | } |
12285 | |
12286 | static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info) |
12287 | { |
12288 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
12289 | struct net_device *dev = info->user_ptr[1]; |
12290 | |
12291 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && |
12292 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT) |
12293 | return -EOPNOTSUPP; |
12294 | |
12295 | if (!rdev->ops->flush_pmksa) |
12296 | return -EOPNOTSUPP; |
12297 | |
12298 | return rdev_flush_pmksa(rdev, netdev: dev); |
12299 | } |
12300 | |
12301 | static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info) |
12302 | { |
12303 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
12304 | struct net_device *dev = info->user_ptr[1]; |
12305 | u8 action_code, dialog_token; |
12306 | u32 peer_capability = 0; |
12307 | u16 status_code; |
12308 | u8 *peer; |
12309 | int link_id; |
12310 | bool initiator; |
12311 | |
12312 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || |
12313 | !rdev->ops->tdls_mgmt) |
12314 | return -EOPNOTSUPP; |
12315 | |
12316 | if (!info->attrs[NL80211_ATTR_TDLS_ACTION] || |
12317 | !info->attrs[NL80211_ATTR_STATUS_CODE] || |
12318 | !info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] || |
12319 | !info->attrs[NL80211_ATTR_IE] || |
12320 | !info->attrs[NL80211_ATTR_MAC]) |
12321 | return -EINVAL; |
12322 | |
12323 | peer = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
12324 | action_code = nla_get_u8(nla: info->attrs[NL80211_ATTR_TDLS_ACTION]); |
12325 | status_code = nla_get_u16(nla: info->attrs[NL80211_ATTR_STATUS_CODE]); |
12326 | dialog_token = nla_get_u8(nla: info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]); |
12327 | initiator = nla_get_flag(nla: info->attrs[NL80211_ATTR_TDLS_INITIATOR]); |
12328 | if (info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]) |
12329 | peer_capability = |
12330 | nla_get_u32(nla: info->attrs[NL80211_ATTR_TDLS_PEER_CAPABILITY]); |
12331 | link_id = nl80211_link_id_or_invalid(attrs: info->attrs); |
12332 | |
12333 | return rdev_tdls_mgmt(rdev, dev, peer, link_id, action_code, |
12334 | dialog_token, status_code, peer_capability, |
12335 | initiator, |
12336 | buf: nla_data(nla: info->attrs[NL80211_ATTR_IE]), |
12337 | len: nla_len(nla: info->attrs[NL80211_ATTR_IE])); |
12338 | } |
12339 | |
12340 | static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info) |
12341 | { |
12342 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
12343 | struct net_device *dev = info->user_ptr[1]; |
12344 | enum nl80211_tdls_operation operation; |
12345 | u8 *peer; |
12346 | |
12347 | if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) || |
12348 | !rdev->ops->tdls_oper) |
12349 | return -EOPNOTSUPP; |
12350 | |
12351 | if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] || |
12352 | !info->attrs[NL80211_ATTR_MAC]) |
12353 | return -EINVAL; |
12354 | |
12355 | operation = nla_get_u8(nla: info->attrs[NL80211_ATTR_TDLS_OPERATION]); |
12356 | peer = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
12357 | |
12358 | return rdev_tdls_oper(rdev, dev, peer, oper: operation); |
12359 | } |
12360 | |
12361 | static int nl80211_remain_on_channel(struct sk_buff *skb, |
12362 | struct genl_info *info) |
12363 | { |
12364 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
12365 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); |
12366 | struct wireless_dev *wdev = info->user_ptr[1]; |
12367 | struct cfg80211_chan_def chandef; |
12368 | struct sk_buff *msg; |
12369 | void *hdr; |
12370 | u64 cookie; |
12371 | u32 duration; |
12372 | int err; |
12373 | |
12374 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || |
12375 | !info->attrs[NL80211_ATTR_DURATION]) |
12376 | return -EINVAL; |
12377 | |
12378 | duration = nla_get_u32(nla: info->attrs[NL80211_ATTR_DURATION]); |
12379 | |
12380 | if (!rdev->ops->remain_on_channel || |
12381 | !(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)) |
12382 | return -EOPNOTSUPP; |
12383 | |
12384 | /* |
12385 | * We should be on that channel for at least a minimum amount of |
12386 | * time (10ms) but no longer than the driver supports. |
12387 | */ |
12388 | if (duration < NL80211_MIN_REMAIN_ON_CHANNEL_TIME || |
12389 | duration > rdev->wiphy.max_remain_on_channel_duration) |
12390 | return -EINVAL; |
12391 | |
12392 | err = nl80211_parse_chandef(rdev, info, chandef: &chandef); |
12393 | if (err) |
12394 | return err; |
12395 | |
12396 | if (!cfg80211_off_channel_oper_allowed(wdev, chan: chandef.chan)) { |
12397 | const struct cfg80211_chan_def *oper_chandef, *compat_chandef; |
12398 | |
12399 | oper_chandef = wdev_chandef(wdev, link_id); |
12400 | |
12401 | if (WARN_ON(!oper_chandef)) { |
12402 | /* cannot happen since we must beacon to get here */ |
12403 | WARN_ON(1); |
12404 | return -EBUSY; |
12405 | } |
12406 | |
12407 | /* note: returns first one if identical chandefs */ |
12408 | compat_chandef = cfg80211_chandef_compatible(chandef1: &chandef, |
12409 | chandef2: oper_chandef); |
12410 | |
12411 | if (compat_chandef != &chandef) |
12412 | return -EBUSY; |
12413 | } |
12414 | |
12415 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
12416 | if (!msg) |
12417 | return -ENOMEM; |
12418 | |
12419 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
12420 | cmd: NL80211_CMD_REMAIN_ON_CHANNEL); |
12421 | if (!hdr) { |
12422 | err = -ENOBUFS; |
12423 | goto free_msg; |
12424 | } |
12425 | |
12426 | err = rdev_remain_on_channel(rdev, wdev, chan: chandef.chan, |
12427 | duration, cookie: &cookie); |
12428 | |
12429 | if (err) |
12430 | goto free_msg; |
12431 | |
12432 | if (nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: cookie, |
12433 | padattr: NL80211_ATTR_PAD)) |
12434 | goto nla_put_failure; |
12435 | |
12436 | genlmsg_end(skb: msg, hdr); |
12437 | |
12438 | return genlmsg_reply(skb: msg, info); |
12439 | |
12440 | nla_put_failure: |
12441 | err = -ENOBUFS; |
12442 | free_msg: |
12443 | nlmsg_free(skb: msg); |
12444 | return err; |
12445 | } |
12446 | |
12447 | static int nl80211_cancel_remain_on_channel(struct sk_buff *skb, |
12448 | struct genl_info *info) |
12449 | { |
12450 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
12451 | struct wireless_dev *wdev = info->user_ptr[1]; |
12452 | u64 cookie; |
12453 | |
12454 | if (!info->attrs[NL80211_ATTR_COOKIE]) |
12455 | return -EINVAL; |
12456 | |
12457 | if (!rdev->ops->cancel_remain_on_channel) |
12458 | return -EOPNOTSUPP; |
12459 | |
12460 | cookie = nla_get_u64(nla: info->attrs[NL80211_ATTR_COOKIE]); |
12461 | |
12462 | return rdev_cancel_remain_on_channel(rdev, wdev, cookie); |
12463 | } |
12464 | |
12465 | static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, |
12466 | struct genl_info *info) |
12467 | { |
12468 | struct cfg80211_bitrate_mask mask; |
12469 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); |
12470 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
12471 | struct net_device *dev = info->user_ptr[1]; |
12472 | int err; |
12473 | |
12474 | if (!rdev->ops->set_bitrate_mask) |
12475 | return -EOPNOTSUPP; |
12476 | |
12477 | err = nl80211_parse_tx_bitrate_mask(info, attrs: info->attrs, |
12478 | attr: NL80211_ATTR_TX_RATES, mask: &mask, |
12479 | dev, default_all_enabled: true, link_id); |
12480 | if (err) |
12481 | return err; |
12482 | |
12483 | return rdev_set_bitrate_mask(rdev, dev, link_id, NULL, mask: &mask); |
12484 | } |
12485 | |
12486 | static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) |
12487 | { |
12488 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
12489 | struct wireless_dev *wdev = info->user_ptr[1]; |
12490 | u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION; |
12491 | |
12492 | if (!info->attrs[NL80211_ATTR_FRAME_MATCH]) |
12493 | return -EINVAL; |
12494 | |
12495 | if (info->attrs[NL80211_ATTR_FRAME_TYPE]) |
12496 | frame_type = nla_get_u16(nla: info->attrs[NL80211_ATTR_FRAME_TYPE]); |
12497 | |
12498 | switch (wdev->iftype) { |
12499 | case NL80211_IFTYPE_STATION: |
12500 | case NL80211_IFTYPE_ADHOC: |
12501 | case NL80211_IFTYPE_P2P_CLIENT: |
12502 | case NL80211_IFTYPE_AP: |
12503 | case NL80211_IFTYPE_AP_VLAN: |
12504 | case NL80211_IFTYPE_MESH_POINT: |
12505 | case NL80211_IFTYPE_P2P_GO: |
12506 | case NL80211_IFTYPE_P2P_DEVICE: |
12507 | break; |
12508 | case NL80211_IFTYPE_NAN: |
12509 | if (!wiphy_ext_feature_isset(wiphy: wdev->wiphy, |
12510 | ftidx: NL80211_EXT_FEATURE_SECURE_NAN)) |
12511 | return -EOPNOTSUPP; |
12512 | break; |
12513 | default: |
12514 | return -EOPNOTSUPP; |
12515 | } |
12516 | |
12517 | /* not much point in registering if we can't reply */ |
12518 | if (!rdev->ops->mgmt_tx) |
12519 | return -EOPNOTSUPP; |
12520 | |
12521 | if (info->attrs[NL80211_ATTR_RECEIVE_MULTICAST] && |
12522 | !wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
12523 | ftidx: NL80211_EXT_FEATURE_MULTICAST_REGISTRATIONS)) { |
12524 | GENL_SET_ERR_MSG(info, |
12525 | "multicast RX registrations are not supported" ); |
12526 | return -EOPNOTSUPP; |
12527 | } |
12528 | |
12529 | return cfg80211_mlme_register_mgmt(wdev, snd_pid: info->snd_portid, frame_type, |
12530 | match_data: nla_data(nla: info->attrs[NL80211_ATTR_FRAME_MATCH]), |
12531 | match_len: nla_len(nla: info->attrs[NL80211_ATTR_FRAME_MATCH]), |
12532 | multicast_rx: info->attrs[NL80211_ATTR_RECEIVE_MULTICAST], |
12533 | extack: info->extack); |
12534 | } |
12535 | |
12536 | static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) |
12537 | { |
12538 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
12539 | struct wireless_dev *wdev = info->user_ptr[1]; |
12540 | struct cfg80211_chan_def chandef; |
12541 | int err; |
12542 | void *hdr = NULL; |
12543 | u64 cookie; |
12544 | struct sk_buff *msg = NULL; |
12545 | struct cfg80211_mgmt_tx_params params = { |
12546 | .dont_wait_for_ack = |
12547 | info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK], |
12548 | }; |
12549 | |
12550 | if (!info->attrs[NL80211_ATTR_FRAME]) |
12551 | return -EINVAL; |
12552 | |
12553 | if (!rdev->ops->mgmt_tx) |
12554 | return -EOPNOTSUPP; |
12555 | |
12556 | switch (wdev->iftype) { |
12557 | case NL80211_IFTYPE_P2P_DEVICE: |
12558 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) |
12559 | return -EINVAL; |
12560 | break; |
12561 | case NL80211_IFTYPE_STATION: |
12562 | case NL80211_IFTYPE_ADHOC: |
12563 | case NL80211_IFTYPE_P2P_CLIENT: |
12564 | case NL80211_IFTYPE_AP: |
12565 | case NL80211_IFTYPE_AP_VLAN: |
12566 | case NL80211_IFTYPE_MESH_POINT: |
12567 | case NL80211_IFTYPE_P2P_GO: |
12568 | break; |
12569 | case NL80211_IFTYPE_NAN: |
12570 | if (!wiphy_ext_feature_isset(wiphy: wdev->wiphy, |
12571 | ftidx: NL80211_EXT_FEATURE_SECURE_NAN)) |
12572 | return -EOPNOTSUPP; |
12573 | break; |
12574 | default: |
12575 | return -EOPNOTSUPP; |
12576 | } |
12577 | |
12578 | if (info->attrs[NL80211_ATTR_DURATION]) { |
12579 | if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) |
12580 | return -EINVAL; |
12581 | params.wait = nla_get_u32(nla: info->attrs[NL80211_ATTR_DURATION]); |
12582 | |
12583 | /* |
12584 | * We should wait on the channel for at least a minimum amount |
12585 | * of time (10ms) but no longer than the driver supports. |
12586 | */ |
12587 | if (params.wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME || |
12588 | params.wait > rdev->wiphy.max_remain_on_channel_duration) |
12589 | return -EINVAL; |
12590 | } |
12591 | |
12592 | params.offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK]; |
12593 | |
12594 | if (params.offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) |
12595 | return -EINVAL; |
12596 | |
12597 | params.no_cck = nla_get_flag(nla: info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); |
12598 | |
12599 | /* get the channel if any has been specified, otherwise pass NULL to |
12600 | * the driver. The latter will use the current one |
12601 | */ |
12602 | chandef.chan = NULL; |
12603 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { |
12604 | err = nl80211_parse_chandef(rdev, info, chandef: &chandef); |
12605 | if (err) |
12606 | return err; |
12607 | } |
12608 | |
12609 | if (!chandef.chan && params.offchan) |
12610 | return -EINVAL; |
12611 | |
12612 | if (params.offchan && |
12613 | !cfg80211_off_channel_oper_allowed(wdev, chan: chandef.chan)) |
12614 | return -EBUSY; |
12615 | |
12616 | params.link_id = nl80211_link_id_or_invalid(attrs: info->attrs); |
12617 | /* |
12618 | * This now races due to the unlock, but we cannot check |
12619 | * the valid links for the _station_ anyway, so that's up |
12620 | * to the driver. |
12621 | */ |
12622 | if (params.link_id >= 0 && |
12623 | !(wdev->valid_links & BIT(params.link_id))) |
12624 | return -EINVAL; |
12625 | |
12626 | params.buf = nla_data(nla: info->attrs[NL80211_ATTR_FRAME]); |
12627 | params.len = nla_len(nla: info->attrs[NL80211_ATTR_FRAME]); |
12628 | |
12629 | err = nl80211_parse_counter_offsets(rdev, NULL, datalen: params.len, first_count: -1, |
12630 | attr: info->attrs[NL80211_ATTR_CSA_C_OFFSETS_TX], |
12631 | offsets: ¶ms.csa_offsets, |
12632 | n_offsets: ¶ms.n_csa_offsets); |
12633 | if (err) |
12634 | return err; |
12635 | |
12636 | if (!params.dont_wait_for_ack) { |
12637 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
12638 | if (!msg) |
12639 | return -ENOMEM; |
12640 | |
12641 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
12642 | cmd: NL80211_CMD_FRAME); |
12643 | if (!hdr) { |
12644 | err = -ENOBUFS; |
12645 | goto free_msg; |
12646 | } |
12647 | } |
12648 | |
12649 | params.chan = chandef.chan; |
12650 | err = cfg80211_mlme_mgmt_tx(rdev, wdev, params: ¶ms, cookie: &cookie); |
12651 | if (err) |
12652 | goto free_msg; |
12653 | |
12654 | if (msg) { |
12655 | if (nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: cookie, |
12656 | padattr: NL80211_ATTR_PAD)) |
12657 | goto nla_put_failure; |
12658 | |
12659 | genlmsg_end(skb: msg, hdr); |
12660 | return genlmsg_reply(skb: msg, info); |
12661 | } |
12662 | |
12663 | return 0; |
12664 | |
12665 | nla_put_failure: |
12666 | err = -ENOBUFS; |
12667 | free_msg: |
12668 | nlmsg_free(skb: msg); |
12669 | return err; |
12670 | } |
12671 | |
12672 | static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info) |
12673 | { |
12674 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
12675 | struct wireless_dev *wdev = info->user_ptr[1]; |
12676 | u64 cookie; |
12677 | |
12678 | if (!info->attrs[NL80211_ATTR_COOKIE]) |
12679 | return -EINVAL; |
12680 | |
12681 | if (!rdev->ops->mgmt_tx_cancel_wait) |
12682 | return -EOPNOTSUPP; |
12683 | |
12684 | switch (wdev->iftype) { |
12685 | case NL80211_IFTYPE_STATION: |
12686 | case NL80211_IFTYPE_ADHOC: |
12687 | case NL80211_IFTYPE_P2P_CLIENT: |
12688 | case NL80211_IFTYPE_AP: |
12689 | case NL80211_IFTYPE_AP_VLAN: |
12690 | case NL80211_IFTYPE_P2P_GO: |
12691 | case NL80211_IFTYPE_P2P_DEVICE: |
12692 | break; |
12693 | case NL80211_IFTYPE_NAN: |
12694 | if (!wiphy_ext_feature_isset(wiphy: wdev->wiphy, |
12695 | ftidx: NL80211_EXT_FEATURE_SECURE_NAN)) |
12696 | return -EOPNOTSUPP; |
12697 | break; |
12698 | default: |
12699 | return -EOPNOTSUPP; |
12700 | } |
12701 | |
12702 | cookie = nla_get_u64(nla: info->attrs[NL80211_ATTR_COOKIE]); |
12703 | |
12704 | return rdev_mgmt_tx_cancel_wait(rdev, wdev, cookie); |
12705 | } |
12706 | |
12707 | static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) |
12708 | { |
12709 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
12710 | struct wireless_dev *wdev; |
12711 | struct net_device *dev = info->user_ptr[1]; |
12712 | u8 ps_state; |
12713 | bool state; |
12714 | int err; |
12715 | |
12716 | if (!info->attrs[NL80211_ATTR_PS_STATE]) |
12717 | return -EINVAL; |
12718 | |
12719 | ps_state = nla_get_u32(nla: info->attrs[NL80211_ATTR_PS_STATE]); |
12720 | |
12721 | wdev = dev->ieee80211_ptr; |
12722 | |
12723 | if (!rdev->ops->set_power_mgmt) |
12724 | return -EOPNOTSUPP; |
12725 | |
12726 | state = (ps_state == NL80211_PS_ENABLED) ? true : false; |
12727 | |
12728 | if (state == wdev->ps) |
12729 | return 0; |
12730 | |
12731 | err = rdev_set_power_mgmt(rdev, dev, enabled: state, timeout: wdev->ps_timeout); |
12732 | if (!err) |
12733 | wdev->ps = state; |
12734 | return err; |
12735 | } |
12736 | |
12737 | static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info) |
12738 | { |
12739 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
12740 | enum nl80211_ps_state ps_state; |
12741 | struct wireless_dev *wdev; |
12742 | struct net_device *dev = info->user_ptr[1]; |
12743 | struct sk_buff *msg; |
12744 | void *hdr; |
12745 | int err; |
12746 | |
12747 | wdev = dev->ieee80211_ptr; |
12748 | |
12749 | if (!rdev->ops->set_power_mgmt) |
12750 | return -EOPNOTSUPP; |
12751 | |
12752 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
12753 | if (!msg) |
12754 | return -ENOMEM; |
12755 | |
12756 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
12757 | cmd: NL80211_CMD_GET_POWER_SAVE); |
12758 | if (!hdr) { |
12759 | err = -ENOBUFS; |
12760 | goto free_msg; |
12761 | } |
12762 | |
12763 | if (wdev->ps) |
12764 | ps_state = NL80211_PS_ENABLED; |
12765 | else |
12766 | ps_state = NL80211_PS_DISABLED; |
12767 | |
12768 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_PS_STATE, value: ps_state)) |
12769 | goto nla_put_failure; |
12770 | |
12771 | genlmsg_end(skb: msg, hdr); |
12772 | return genlmsg_reply(skb: msg, info); |
12773 | |
12774 | nla_put_failure: |
12775 | err = -ENOBUFS; |
12776 | free_msg: |
12777 | nlmsg_free(skb: msg); |
12778 | return err; |
12779 | } |
12780 | |
12781 | static const struct nla_policy |
12782 | nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] = { |
12783 | [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_BINARY }, |
12784 | [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 }, |
12785 | [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, |
12786 | [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 }, |
12787 | [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 }, |
12788 | [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 }, |
12789 | [NL80211_ATTR_CQM_RSSI_LEVEL] = { .type = NLA_S32 }, |
12790 | }; |
12791 | |
12792 | static int nl80211_set_cqm_txe(struct genl_info *info, |
12793 | u32 rate, u32 pkts, u32 intvl) |
12794 | { |
12795 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
12796 | struct net_device *dev = info->user_ptr[1]; |
12797 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
12798 | |
12799 | if (rate > 100 || intvl > NL80211_CQM_TXE_MAX_INTVL) |
12800 | return -EINVAL; |
12801 | |
12802 | if (!rdev->ops->set_cqm_txe_config) |
12803 | return -EOPNOTSUPP; |
12804 | |
12805 | if (wdev->iftype != NL80211_IFTYPE_STATION && |
12806 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) |
12807 | return -EOPNOTSUPP; |
12808 | |
12809 | return rdev_set_cqm_txe_config(rdev, dev, rate, pkts, intvl); |
12810 | } |
12811 | |
12812 | static int (struct cfg80211_registered_device *rdev, |
12813 | struct net_device *dev, |
12814 | struct cfg80211_cqm_config *cqm_config) |
12815 | { |
12816 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
12817 | s32 last, low, high; |
12818 | u32 hyst; |
12819 | int i, n, low_index; |
12820 | int err; |
12821 | |
12822 | /* |
12823 | * Obtain current RSSI value if possible, if not and no RSSI threshold |
12824 | * event has been received yet, we should receive an event after a |
12825 | * connection is established and enough beacons received to calculate |
12826 | * the average. |
12827 | */ |
12828 | if (!cqm_config->last_rssi_event_value && |
12829 | wdev->links[0].client.current_bss && |
12830 | rdev->ops->get_station) { |
12831 | struct station_info sinfo = {}; |
12832 | u8 *mac_addr; |
12833 | |
12834 | mac_addr = wdev->links[0].client.current_bss->pub.bssid; |
12835 | |
12836 | err = rdev_get_station(rdev, dev, mac: mac_addr, sinfo: &sinfo); |
12837 | if (err) |
12838 | return err; |
12839 | |
12840 | cfg80211_sinfo_release_content(sinfo: &sinfo); |
12841 | if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_BEACON_SIGNAL_AVG)) |
12842 | cqm_config->last_rssi_event_value = |
12843 | (s8) sinfo.rx_beacon_signal_avg; |
12844 | } |
12845 | |
12846 | last = cqm_config->last_rssi_event_value; |
12847 | hyst = cqm_config->rssi_hyst; |
12848 | n = cqm_config->n_rssi_thresholds; |
12849 | |
12850 | for (i = 0; i < n; i++) { |
12851 | i = array_index_nospec(i, n); |
12852 | if (last < cqm_config->rssi_thresholds[i]) |
12853 | break; |
12854 | } |
12855 | |
12856 | low_index = i - 1; |
12857 | if (low_index >= 0) { |
12858 | low_index = array_index_nospec(low_index, n); |
12859 | low = cqm_config->rssi_thresholds[low_index] - hyst; |
12860 | } else { |
12861 | low = S32_MIN; |
12862 | } |
12863 | if (i < n) { |
12864 | i = array_index_nospec(i, n); |
12865 | high = cqm_config->rssi_thresholds[i] + hyst - 1; |
12866 | } else { |
12867 | high = S32_MAX; |
12868 | } |
12869 | |
12870 | return rdev_set_cqm_rssi_range_config(rdev, dev, low, high); |
12871 | } |
12872 | |
12873 | static int (struct genl_info *info, |
12874 | const s32 *thresholds, int n_thresholds, |
12875 | u32 hysteresis) |
12876 | { |
12877 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
12878 | struct cfg80211_cqm_config *cqm_config = NULL, *old; |
12879 | struct net_device *dev = info->user_ptr[1]; |
12880 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
12881 | s32 prev = S32_MIN; |
12882 | int i, err; |
12883 | |
12884 | /* Check all values negative and sorted */ |
12885 | for (i = 0; i < n_thresholds; i++) { |
12886 | if (thresholds[i] > 0 || thresholds[i] <= prev) |
12887 | return -EINVAL; |
12888 | |
12889 | prev = thresholds[i]; |
12890 | } |
12891 | |
12892 | if (wdev->iftype != NL80211_IFTYPE_STATION && |
12893 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) |
12894 | return -EOPNOTSUPP; |
12895 | |
12896 | if (n_thresholds == 1 && thresholds[0] == 0) /* Disabling */ |
12897 | n_thresholds = 0; |
12898 | |
12899 | old = wiphy_dereference(wdev->wiphy, wdev->cqm_config); |
12900 | |
12901 | /* if already disabled just succeed */ |
12902 | if (!n_thresholds && !old) |
12903 | return 0; |
12904 | |
12905 | if (n_thresholds > 1) { |
12906 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
12907 | ftidx: NL80211_EXT_FEATURE_CQM_RSSI_LIST) || |
12908 | !rdev->ops->set_cqm_rssi_range_config) |
12909 | return -EOPNOTSUPP; |
12910 | } else { |
12911 | if (!rdev->ops->set_cqm_rssi_config) |
12912 | return -EOPNOTSUPP; |
12913 | } |
12914 | |
12915 | if (n_thresholds) { |
12916 | cqm_config = kzalloc(struct_size(cqm_config, rssi_thresholds, |
12917 | n_thresholds), |
12918 | GFP_KERNEL); |
12919 | if (!cqm_config) |
12920 | return -ENOMEM; |
12921 | |
12922 | cqm_config->rssi_hyst = hysteresis; |
12923 | cqm_config->n_rssi_thresholds = n_thresholds; |
12924 | memcpy(cqm_config->rssi_thresholds, thresholds, |
12925 | flex_array_size(cqm_config, rssi_thresholds, |
12926 | n_thresholds)); |
12927 | cqm_config->use_range_api = n_thresholds > 1 || |
12928 | !rdev->ops->set_cqm_rssi_config; |
12929 | |
12930 | rcu_assign_pointer(wdev->cqm_config, cqm_config); |
12931 | |
12932 | if (cqm_config->use_range_api) |
12933 | err = cfg80211_cqm_rssi_update(rdev, dev, cqm_config); |
12934 | else |
12935 | err = rdev_set_cqm_rssi_config(rdev, dev, |
12936 | rssi_thold: thresholds[0], |
12937 | rssi_hyst: hysteresis); |
12938 | } else { |
12939 | RCU_INIT_POINTER(wdev->cqm_config, NULL); |
12940 | /* if enabled as range also disable via range */ |
12941 | if (old->use_range_api) |
12942 | err = rdev_set_cqm_rssi_range_config(rdev, dev, low: 0, high: 0); |
12943 | else |
12944 | err = rdev_set_cqm_rssi_config(rdev, dev, rssi_thold: 0, rssi_hyst: 0); |
12945 | } |
12946 | |
12947 | if (err) { |
12948 | rcu_assign_pointer(wdev->cqm_config, old); |
12949 | kfree_rcu(cqm_config, rcu_head); |
12950 | } else { |
12951 | kfree_rcu(old, rcu_head); |
12952 | } |
12953 | |
12954 | return err; |
12955 | } |
12956 | |
12957 | static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) |
12958 | { |
12959 | struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1]; |
12960 | struct nlattr *cqm; |
12961 | int err; |
12962 | |
12963 | cqm = info->attrs[NL80211_ATTR_CQM]; |
12964 | if (!cqm) |
12965 | return -EINVAL; |
12966 | |
12967 | err = nla_parse_nested_deprecated(tb: attrs, maxtype: NL80211_ATTR_CQM_MAX, nla: cqm, |
12968 | policy: nl80211_attr_cqm_policy, |
12969 | extack: info->extack); |
12970 | if (err) |
12971 | return err; |
12972 | |
12973 | if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] && |
12974 | attrs[NL80211_ATTR_CQM_RSSI_HYST]) { |
12975 | const s32 *thresholds = |
12976 | nla_data(nla: attrs[NL80211_ATTR_CQM_RSSI_THOLD]); |
12977 | int len = nla_len(nla: attrs[NL80211_ATTR_CQM_RSSI_THOLD]); |
12978 | u32 hysteresis = nla_get_u32(nla: attrs[NL80211_ATTR_CQM_RSSI_HYST]); |
12979 | |
12980 | if (len % 4) |
12981 | return -EINVAL; |
12982 | |
12983 | return nl80211_set_cqm_rssi(info, thresholds, n_thresholds: len / 4, |
12984 | hysteresis); |
12985 | } |
12986 | |
12987 | if (attrs[NL80211_ATTR_CQM_TXE_RATE] && |
12988 | attrs[NL80211_ATTR_CQM_TXE_PKTS] && |
12989 | attrs[NL80211_ATTR_CQM_TXE_INTVL]) { |
12990 | u32 rate = nla_get_u32(nla: attrs[NL80211_ATTR_CQM_TXE_RATE]); |
12991 | u32 pkts = nla_get_u32(nla: attrs[NL80211_ATTR_CQM_TXE_PKTS]); |
12992 | u32 intvl = nla_get_u32(nla: attrs[NL80211_ATTR_CQM_TXE_INTVL]); |
12993 | |
12994 | return nl80211_set_cqm_txe(info, rate, pkts, intvl); |
12995 | } |
12996 | |
12997 | return -EINVAL; |
12998 | } |
12999 | |
13000 | static int nl80211_join_ocb(struct sk_buff *skb, struct genl_info *info) |
13001 | { |
13002 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
13003 | struct net_device *dev = info->user_ptr[1]; |
13004 | struct ocb_setup setup = {}; |
13005 | int err; |
13006 | |
13007 | err = nl80211_parse_chandef(rdev, info, chandef: &setup.chandef); |
13008 | if (err) |
13009 | return err; |
13010 | |
13011 | return cfg80211_join_ocb(rdev, dev, setup: &setup); |
13012 | } |
13013 | |
13014 | static int nl80211_leave_ocb(struct sk_buff *skb, struct genl_info *info) |
13015 | { |
13016 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
13017 | struct net_device *dev = info->user_ptr[1]; |
13018 | |
13019 | return cfg80211_leave_ocb(rdev, dev); |
13020 | } |
13021 | |
13022 | static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) |
13023 | { |
13024 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
13025 | struct net_device *dev = info->user_ptr[1]; |
13026 | struct mesh_config cfg; |
13027 | struct mesh_setup setup; |
13028 | int err; |
13029 | |
13030 | /* start with default */ |
13031 | memcpy(&cfg, &default_mesh_config, sizeof(cfg)); |
13032 | memcpy(&setup, &default_mesh_setup, sizeof(setup)); |
13033 | |
13034 | if (info->attrs[NL80211_ATTR_MESH_CONFIG]) { |
13035 | /* and parse parameters if given */ |
13036 | err = nl80211_parse_mesh_config(info, cfg: &cfg, NULL); |
13037 | if (err) |
13038 | return err; |
13039 | } |
13040 | |
13041 | if (!info->attrs[NL80211_ATTR_MESH_ID] || |
13042 | !nla_len(nla: info->attrs[NL80211_ATTR_MESH_ID])) |
13043 | return -EINVAL; |
13044 | |
13045 | setup.mesh_id = nla_data(nla: info->attrs[NL80211_ATTR_MESH_ID]); |
13046 | setup.mesh_id_len = nla_len(nla: info->attrs[NL80211_ATTR_MESH_ID]); |
13047 | |
13048 | if (info->attrs[NL80211_ATTR_MCAST_RATE] && |
13049 | !nl80211_parse_mcast_rate(rdev, mcast_rate: setup.mcast_rate, |
13050 | rateval: nla_get_u32(nla: info->attrs[NL80211_ATTR_MCAST_RATE]))) |
13051 | return -EINVAL; |
13052 | |
13053 | if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) { |
13054 | setup.beacon_interval = |
13055 | nla_get_u32(nla: info->attrs[NL80211_ATTR_BEACON_INTERVAL]); |
13056 | |
13057 | err = cfg80211_validate_beacon_int(rdev, |
13058 | iftype: NL80211_IFTYPE_MESH_POINT, |
13059 | beacon_int: setup.beacon_interval); |
13060 | if (err) |
13061 | return err; |
13062 | } |
13063 | |
13064 | if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) { |
13065 | setup.dtim_period = |
13066 | nla_get_u32(nla: info->attrs[NL80211_ATTR_DTIM_PERIOD]); |
13067 | if (setup.dtim_period < 1 || setup.dtim_period > 100) |
13068 | return -EINVAL; |
13069 | } |
13070 | |
13071 | if (info->attrs[NL80211_ATTR_MESH_SETUP]) { |
13072 | /* parse additional setup parameters if given */ |
13073 | err = nl80211_parse_mesh_setup(info, setup: &setup); |
13074 | if (err) |
13075 | return err; |
13076 | } |
13077 | |
13078 | if (setup.user_mpm) |
13079 | cfg.auto_open_plinks = false; |
13080 | |
13081 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { |
13082 | err = nl80211_parse_chandef(rdev, info, chandef: &setup.chandef); |
13083 | if (err) |
13084 | return err; |
13085 | } else { |
13086 | /* __cfg80211_join_mesh() will sort it out */ |
13087 | setup.chandef.chan = NULL; |
13088 | } |
13089 | |
13090 | if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { |
13091 | u8 *rates = nla_data(nla: info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); |
13092 | int n_rates = |
13093 | nla_len(nla: info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); |
13094 | struct ieee80211_supported_band *sband; |
13095 | |
13096 | if (!setup.chandef.chan) |
13097 | return -EINVAL; |
13098 | |
13099 | sband = rdev->wiphy.bands[setup.chandef.chan->band]; |
13100 | |
13101 | err = ieee80211_get_ratemask(sband, rates, n_rates, |
13102 | mask: &setup.basic_rates); |
13103 | if (err) |
13104 | return err; |
13105 | } |
13106 | |
13107 | if (info->attrs[NL80211_ATTR_TX_RATES]) { |
13108 | err = nl80211_parse_tx_bitrate_mask(info, attrs: info->attrs, |
13109 | attr: NL80211_ATTR_TX_RATES, |
13110 | mask: &setup.beacon_rate, |
13111 | dev, default_all_enabled: false, link_id: 0); |
13112 | if (err) |
13113 | return err; |
13114 | |
13115 | if (!setup.chandef.chan) |
13116 | return -EINVAL; |
13117 | |
13118 | err = validate_beacon_tx_rate(rdev, band: setup.chandef.chan->band, |
13119 | beacon_rate: &setup.beacon_rate); |
13120 | if (err) |
13121 | return err; |
13122 | } |
13123 | |
13124 | setup.userspace_handles_dfs = |
13125 | nla_get_flag(nla: info->attrs[NL80211_ATTR_HANDLE_DFS]); |
13126 | |
13127 | if (info->attrs[NL80211_ATTR_CONTROL_PORT_OVER_NL80211]) { |
13128 | int r = validate_pae_over_nl80211(rdev, info); |
13129 | |
13130 | if (r < 0) |
13131 | return r; |
13132 | |
13133 | setup.control_port_over_nl80211 = true; |
13134 | } |
13135 | |
13136 | err = __cfg80211_join_mesh(rdev, dev, setup: &setup, conf: &cfg); |
13137 | if (!err && info->attrs[NL80211_ATTR_SOCKET_OWNER]) |
13138 | dev->ieee80211_ptr->conn_owner_nlportid = info->snd_portid; |
13139 | |
13140 | return err; |
13141 | } |
13142 | |
13143 | static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info) |
13144 | { |
13145 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
13146 | struct net_device *dev = info->user_ptr[1]; |
13147 | |
13148 | return cfg80211_leave_mesh(rdev, dev); |
13149 | } |
13150 | |
13151 | #ifdef CONFIG_PM |
13152 | static int nl80211_send_wowlan_patterns(struct sk_buff *msg, |
13153 | struct cfg80211_registered_device *rdev) |
13154 | { |
13155 | struct cfg80211_wowlan *wowlan = rdev->wiphy.wowlan_config; |
13156 | struct nlattr *nl_pats, *nl_pat; |
13157 | int i, pat_len; |
13158 | |
13159 | if (!wowlan->n_patterns) |
13160 | return 0; |
13161 | |
13162 | nl_pats = nla_nest_start_noflag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_PKT_PATTERN); |
13163 | if (!nl_pats) |
13164 | return -ENOBUFS; |
13165 | |
13166 | for (i = 0; i < wowlan->n_patterns; i++) { |
13167 | nl_pat = nla_nest_start_noflag(skb: msg, attrtype: i + 1); |
13168 | if (!nl_pat) |
13169 | return -ENOBUFS; |
13170 | pat_len = wowlan->patterns[i].pattern_len; |
13171 | if (nla_put(skb: msg, attrtype: NL80211_PKTPAT_MASK, DIV_ROUND_UP(pat_len, 8), |
13172 | data: wowlan->patterns[i].mask) || |
13173 | nla_put(skb: msg, attrtype: NL80211_PKTPAT_PATTERN, attrlen: pat_len, |
13174 | data: wowlan->patterns[i].pattern) || |
13175 | nla_put_u32(skb: msg, attrtype: NL80211_PKTPAT_OFFSET, |
13176 | value: wowlan->patterns[i].pkt_offset)) |
13177 | return -ENOBUFS; |
13178 | nla_nest_end(skb: msg, start: nl_pat); |
13179 | } |
13180 | nla_nest_end(skb: msg, start: nl_pats); |
13181 | |
13182 | return 0; |
13183 | } |
13184 | |
13185 | static int nl80211_send_wowlan_tcp(struct sk_buff *msg, |
13186 | struct cfg80211_wowlan_tcp *tcp) |
13187 | { |
13188 | struct nlattr *nl_tcp; |
13189 | |
13190 | if (!tcp) |
13191 | return 0; |
13192 | |
13193 | nl_tcp = nla_nest_start_noflag(skb: msg, |
13194 | attrtype: NL80211_WOWLAN_TRIG_TCP_CONNECTION); |
13195 | if (!nl_tcp) |
13196 | return -ENOBUFS; |
13197 | |
13198 | if (nla_put_in_addr(skb: msg, attrtype: NL80211_WOWLAN_TCP_SRC_IPV4, addr: tcp->src) || |
13199 | nla_put_in_addr(skb: msg, attrtype: NL80211_WOWLAN_TCP_DST_IPV4, addr: tcp->dst) || |
13200 | nla_put(skb: msg, attrtype: NL80211_WOWLAN_TCP_DST_MAC, ETH_ALEN, data: tcp->dst_mac) || |
13201 | nla_put_u16(skb: msg, attrtype: NL80211_WOWLAN_TCP_SRC_PORT, value: tcp->src_port) || |
13202 | nla_put_u16(skb: msg, attrtype: NL80211_WOWLAN_TCP_DST_PORT, value: tcp->dst_port) || |
13203 | nla_put(skb: msg, attrtype: NL80211_WOWLAN_TCP_DATA_PAYLOAD, |
13204 | attrlen: tcp->payload_len, data: tcp->payload) || |
13205 | nla_put_u32(skb: msg, attrtype: NL80211_WOWLAN_TCP_DATA_INTERVAL, |
13206 | value: tcp->data_interval) || |
13207 | nla_put(skb: msg, attrtype: NL80211_WOWLAN_TCP_WAKE_PAYLOAD, |
13208 | attrlen: tcp->wake_len, data: tcp->wake_data) || |
13209 | nla_put(skb: msg, attrtype: NL80211_WOWLAN_TCP_WAKE_MASK, |
13210 | DIV_ROUND_UP(tcp->wake_len, 8), data: tcp->wake_mask)) |
13211 | return -ENOBUFS; |
13212 | |
13213 | if (tcp->payload_seq.len && |
13214 | nla_put(skb: msg, attrtype: NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ, |
13215 | attrlen: sizeof(tcp->payload_seq), data: &tcp->payload_seq)) |
13216 | return -ENOBUFS; |
13217 | |
13218 | if (tcp->payload_tok.len && |
13219 | nla_put(skb: msg, attrtype: NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, |
13220 | attrlen: sizeof(tcp->payload_tok) + tcp->tokens_size, |
13221 | data: &tcp->payload_tok)) |
13222 | return -ENOBUFS; |
13223 | |
13224 | nla_nest_end(skb: msg, start: nl_tcp); |
13225 | |
13226 | return 0; |
13227 | } |
13228 | |
13229 | static int nl80211_send_wowlan_nd(struct sk_buff *msg, |
13230 | struct cfg80211_sched_scan_request *req) |
13231 | { |
13232 | struct nlattr *nd, *freqs, *matches, *match, *scan_plans, *scan_plan; |
13233 | int i; |
13234 | |
13235 | if (!req) |
13236 | return 0; |
13237 | |
13238 | nd = nla_nest_start_noflag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_NET_DETECT); |
13239 | if (!nd) |
13240 | return -ENOBUFS; |
13241 | |
13242 | if (req->n_scan_plans == 1 && |
13243 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_SCHED_SCAN_INTERVAL, |
13244 | value: req->scan_plans[0].interval * 1000)) |
13245 | return -ENOBUFS; |
13246 | |
13247 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_SCHED_SCAN_DELAY, value: req->delay)) |
13248 | return -ENOBUFS; |
13249 | |
13250 | if (req->relative_rssi_set) { |
13251 | struct nl80211_bss_select_rssi_adjust ; |
13252 | |
13253 | if (nla_put_s8(skb: msg, attrtype: NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI, |
13254 | value: req->relative_rssi)) |
13255 | return -ENOBUFS; |
13256 | |
13257 | rssi_adjust.band = req->rssi_adjust.band; |
13258 | rssi_adjust.delta = req->rssi_adjust.delta; |
13259 | if (nla_put(skb: msg, attrtype: NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST, |
13260 | attrlen: sizeof(rssi_adjust), data: &rssi_adjust)) |
13261 | return -ENOBUFS; |
13262 | } |
13263 | |
13264 | freqs = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_SCAN_FREQUENCIES); |
13265 | if (!freqs) |
13266 | return -ENOBUFS; |
13267 | |
13268 | for (i = 0; i < req->n_channels; i++) { |
13269 | if (nla_put_u32(skb: msg, attrtype: i, value: req->channels[i]->center_freq)) |
13270 | return -ENOBUFS; |
13271 | } |
13272 | |
13273 | nla_nest_end(skb: msg, start: freqs); |
13274 | |
13275 | if (req->n_match_sets) { |
13276 | matches = nla_nest_start_noflag(skb: msg, |
13277 | attrtype: NL80211_ATTR_SCHED_SCAN_MATCH); |
13278 | if (!matches) |
13279 | return -ENOBUFS; |
13280 | |
13281 | for (i = 0; i < req->n_match_sets; i++) { |
13282 | match = nla_nest_start_noflag(skb: msg, attrtype: i); |
13283 | if (!match) |
13284 | return -ENOBUFS; |
13285 | |
13286 | if (nla_put(skb: msg, attrtype: NL80211_SCHED_SCAN_MATCH_ATTR_SSID, |
13287 | attrlen: req->match_sets[i].ssid.ssid_len, |
13288 | data: req->match_sets[i].ssid.ssid)) |
13289 | return -ENOBUFS; |
13290 | nla_nest_end(skb: msg, start: match); |
13291 | } |
13292 | nla_nest_end(skb: msg, start: matches); |
13293 | } |
13294 | |
13295 | scan_plans = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_SCHED_SCAN_PLANS); |
13296 | if (!scan_plans) |
13297 | return -ENOBUFS; |
13298 | |
13299 | for (i = 0; i < req->n_scan_plans; i++) { |
13300 | scan_plan = nla_nest_start_noflag(skb: msg, attrtype: i + 1); |
13301 | if (!scan_plan) |
13302 | return -ENOBUFS; |
13303 | |
13304 | if (nla_put_u32(skb: msg, attrtype: NL80211_SCHED_SCAN_PLAN_INTERVAL, |
13305 | value: req->scan_plans[i].interval) || |
13306 | (req->scan_plans[i].iterations && |
13307 | nla_put_u32(skb: msg, attrtype: NL80211_SCHED_SCAN_PLAN_ITERATIONS, |
13308 | value: req->scan_plans[i].iterations))) |
13309 | return -ENOBUFS; |
13310 | nla_nest_end(skb: msg, start: scan_plan); |
13311 | } |
13312 | nla_nest_end(skb: msg, start: scan_plans); |
13313 | |
13314 | nla_nest_end(skb: msg, start: nd); |
13315 | |
13316 | return 0; |
13317 | } |
13318 | |
13319 | static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info) |
13320 | { |
13321 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
13322 | struct sk_buff *msg; |
13323 | void *hdr; |
13324 | u32 size = NLMSG_DEFAULT_SIZE; |
13325 | |
13326 | if (!rdev->wiphy.wowlan) |
13327 | return -EOPNOTSUPP; |
13328 | |
13329 | if (rdev->wiphy.wowlan_config && rdev->wiphy.wowlan_config->tcp) { |
13330 | /* adjust size to have room for all the data */ |
13331 | size += rdev->wiphy.wowlan_config->tcp->tokens_size + |
13332 | rdev->wiphy.wowlan_config->tcp->payload_len + |
13333 | rdev->wiphy.wowlan_config->tcp->wake_len + |
13334 | rdev->wiphy.wowlan_config->tcp->wake_len / 8; |
13335 | } |
13336 | |
13337 | msg = nlmsg_new(payload: size, GFP_KERNEL); |
13338 | if (!msg) |
13339 | return -ENOMEM; |
13340 | |
13341 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
13342 | cmd: NL80211_CMD_GET_WOWLAN); |
13343 | if (!hdr) |
13344 | goto nla_put_failure; |
13345 | |
13346 | if (rdev->wiphy.wowlan_config) { |
13347 | struct nlattr *nl_wowlan; |
13348 | |
13349 | nl_wowlan = nla_nest_start_noflag(skb: msg, |
13350 | attrtype: NL80211_ATTR_WOWLAN_TRIGGERS); |
13351 | if (!nl_wowlan) |
13352 | goto nla_put_failure; |
13353 | |
13354 | if ((rdev->wiphy.wowlan_config->any && |
13355 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_ANY)) || |
13356 | (rdev->wiphy.wowlan_config->disconnect && |
13357 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_DISCONNECT)) || |
13358 | (rdev->wiphy.wowlan_config->magic_pkt && |
13359 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_MAGIC_PKT)) || |
13360 | (rdev->wiphy.wowlan_config->gtk_rekey_failure && |
13361 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) || |
13362 | (rdev->wiphy.wowlan_config->eap_identity_req && |
13363 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) || |
13364 | (rdev->wiphy.wowlan_config->four_way_handshake && |
13365 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) || |
13366 | (rdev->wiphy.wowlan_config->rfkill_release && |
13367 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) |
13368 | goto nla_put_failure; |
13369 | |
13370 | if (nl80211_send_wowlan_patterns(msg, rdev)) |
13371 | goto nla_put_failure; |
13372 | |
13373 | if (nl80211_send_wowlan_tcp(msg, |
13374 | tcp: rdev->wiphy.wowlan_config->tcp)) |
13375 | goto nla_put_failure; |
13376 | |
13377 | if (nl80211_send_wowlan_nd( |
13378 | msg, |
13379 | req: rdev->wiphy.wowlan_config->nd_config)) |
13380 | goto nla_put_failure; |
13381 | |
13382 | nla_nest_end(skb: msg, start: nl_wowlan); |
13383 | } |
13384 | |
13385 | genlmsg_end(skb: msg, hdr); |
13386 | return genlmsg_reply(skb: msg, info); |
13387 | |
13388 | nla_put_failure: |
13389 | nlmsg_free(skb: msg); |
13390 | return -ENOBUFS; |
13391 | } |
13392 | |
13393 | static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev, |
13394 | struct nlattr *attr, |
13395 | struct cfg80211_wowlan *trig) |
13396 | { |
13397 | struct nlattr *tb[NUM_NL80211_WOWLAN_TCP]; |
13398 | struct cfg80211_wowlan_tcp *cfg; |
13399 | struct nl80211_wowlan_tcp_data_token *tok = NULL; |
13400 | struct nl80211_wowlan_tcp_data_seq *seq = NULL; |
13401 | u32 size; |
13402 | u32 data_size, wake_size, tokens_size = 0, wake_mask_size; |
13403 | int err, port; |
13404 | |
13405 | if (!rdev->wiphy.wowlan->tcp) |
13406 | return -EINVAL; |
13407 | |
13408 | err = nla_parse_nested_deprecated(tb, maxtype: MAX_NL80211_WOWLAN_TCP, nla: attr, |
13409 | policy: nl80211_wowlan_tcp_policy, NULL); |
13410 | if (err) |
13411 | return err; |
13412 | |
13413 | if (!tb[NL80211_WOWLAN_TCP_SRC_IPV4] || |
13414 | !tb[NL80211_WOWLAN_TCP_DST_IPV4] || |
13415 | !tb[NL80211_WOWLAN_TCP_DST_MAC] || |
13416 | !tb[NL80211_WOWLAN_TCP_DST_PORT] || |
13417 | !tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD] || |
13418 | !tb[NL80211_WOWLAN_TCP_DATA_INTERVAL] || |
13419 | !tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD] || |
13420 | !tb[NL80211_WOWLAN_TCP_WAKE_MASK]) |
13421 | return -EINVAL; |
13422 | |
13423 | data_size = nla_len(nla: tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]); |
13424 | if (data_size > rdev->wiphy.wowlan->tcp->data_payload_max) |
13425 | return -EINVAL; |
13426 | |
13427 | if (nla_get_u32(nla: tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) > |
13428 | rdev->wiphy.wowlan->tcp->data_interval_max || |
13429 | nla_get_u32(nla: tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) == 0) |
13430 | return -EINVAL; |
13431 | |
13432 | wake_size = nla_len(nla: tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]); |
13433 | if (wake_size > rdev->wiphy.wowlan->tcp->wake_payload_max) |
13434 | return -EINVAL; |
13435 | |
13436 | wake_mask_size = nla_len(nla: tb[NL80211_WOWLAN_TCP_WAKE_MASK]); |
13437 | if (wake_mask_size != DIV_ROUND_UP(wake_size, 8)) |
13438 | return -EINVAL; |
13439 | |
13440 | if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]) { |
13441 | u32 tokln = nla_len(nla: tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]); |
13442 | |
13443 | tok = nla_data(nla: tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]); |
13444 | tokens_size = tokln - sizeof(*tok); |
13445 | |
13446 | if (!tok->len || tokens_size % tok->len) |
13447 | return -EINVAL; |
13448 | if (!rdev->wiphy.wowlan->tcp->tok) |
13449 | return -EINVAL; |
13450 | if (tok->len > rdev->wiphy.wowlan->tcp->tok->max_len) |
13451 | return -EINVAL; |
13452 | if (tok->len < rdev->wiphy.wowlan->tcp->tok->min_len) |
13453 | return -EINVAL; |
13454 | if (tokens_size > rdev->wiphy.wowlan->tcp->tok->bufsize) |
13455 | return -EINVAL; |
13456 | if (tok->offset + tok->len > data_size) |
13457 | return -EINVAL; |
13458 | } |
13459 | |
13460 | if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]) { |
13461 | seq = nla_data(nla: tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]); |
13462 | if (!rdev->wiphy.wowlan->tcp->seq) |
13463 | return -EINVAL; |
13464 | if (seq->len == 0 || seq->len > 4) |
13465 | return -EINVAL; |
13466 | if (seq->len + seq->offset > data_size) |
13467 | return -EINVAL; |
13468 | } |
13469 | |
13470 | size = sizeof(*cfg); |
13471 | size += data_size; |
13472 | size += wake_size + wake_mask_size; |
13473 | size += tokens_size; |
13474 | |
13475 | cfg = kzalloc(size, GFP_KERNEL); |
13476 | if (!cfg) |
13477 | return -ENOMEM; |
13478 | cfg->src = nla_get_in_addr(nla: tb[NL80211_WOWLAN_TCP_SRC_IPV4]); |
13479 | cfg->dst = nla_get_in_addr(nla: tb[NL80211_WOWLAN_TCP_DST_IPV4]); |
13480 | memcpy(cfg->dst_mac, nla_data(tb[NL80211_WOWLAN_TCP_DST_MAC]), |
13481 | ETH_ALEN); |
13482 | if (tb[NL80211_WOWLAN_TCP_SRC_PORT]) |
13483 | port = nla_get_u16(nla: tb[NL80211_WOWLAN_TCP_SRC_PORT]); |
13484 | else |
13485 | port = 0; |
13486 | #ifdef CONFIG_INET |
13487 | /* allocate a socket and port for it and use it */ |
13488 | err = __sock_create(net: wiphy_net(wiphy: &rdev->wiphy), PF_INET, type: SOCK_STREAM, |
13489 | IPPROTO_TCP, res: &cfg->sock, kern: 1); |
13490 | if (err) { |
13491 | kfree(objp: cfg); |
13492 | return err; |
13493 | } |
13494 | if (inet_csk_get_port(sk: cfg->sock->sk, snum: port)) { |
13495 | sock_release(sock: cfg->sock); |
13496 | kfree(objp: cfg); |
13497 | return -EADDRINUSE; |
13498 | } |
13499 | cfg->src_port = inet_sk(cfg->sock->sk)->inet_num; |
13500 | #else |
13501 | if (!port) { |
13502 | kfree(cfg); |
13503 | return -EINVAL; |
13504 | } |
13505 | cfg->src_port = port; |
13506 | #endif |
13507 | |
13508 | cfg->dst_port = nla_get_u16(nla: tb[NL80211_WOWLAN_TCP_DST_PORT]); |
13509 | cfg->payload_len = data_size; |
13510 | cfg->payload = (u8 *)cfg + sizeof(*cfg) + tokens_size; |
13511 | memcpy((void *)cfg->payload, |
13512 | nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]), |
13513 | data_size); |
13514 | if (seq) |
13515 | cfg->payload_seq = *seq; |
13516 | cfg->data_interval = nla_get_u32(nla: tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]); |
13517 | cfg->wake_len = wake_size; |
13518 | cfg->wake_data = (u8 *)cfg + sizeof(*cfg) + tokens_size + data_size; |
13519 | memcpy((void *)cfg->wake_data, |
13520 | nla_data(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]), |
13521 | wake_size); |
13522 | cfg->wake_mask = (u8 *)cfg + sizeof(*cfg) + tokens_size + |
13523 | data_size + wake_size; |
13524 | memcpy((void *)cfg->wake_mask, |
13525 | nla_data(tb[NL80211_WOWLAN_TCP_WAKE_MASK]), |
13526 | wake_mask_size); |
13527 | if (tok) { |
13528 | cfg->tokens_size = tokens_size; |
13529 | cfg->payload_tok = *tok; |
13530 | memcpy(cfg->payload_tok.token_stream, tok->token_stream, |
13531 | tokens_size); |
13532 | } |
13533 | |
13534 | trig->tcp = cfg; |
13535 | |
13536 | return 0; |
13537 | } |
13538 | |
13539 | static int nl80211_parse_wowlan_nd(struct cfg80211_registered_device *rdev, |
13540 | const struct wiphy_wowlan_support *wowlan, |
13541 | struct nlattr *attr, |
13542 | struct cfg80211_wowlan *trig) |
13543 | { |
13544 | struct nlattr **tb; |
13545 | int err; |
13546 | |
13547 | tb = kcalloc(n: NUM_NL80211_ATTR, size: sizeof(*tb), GFP_KERNEL); |
13548 | if (!tb) |
13549 | return -ENOMEM; |
13550 | |
13551 | if (!(wowlan->flags & WIPHY_WOWLAN_NET_DETECT)) { |
13552 | err = -EOPNOTSUPP; |
13553 | goto out; |
13554 | } |
13555 | |
13556 | err = nla_parse_nested_deprecated(tb, maxtype: NL80211_ATTR_MAX, nla: attr, |
13557 | policy: nl80211_policy, NULL); |
13558 | if (err) |
13559 | goto out; |
13560 | |
13561 | trig->nd_config = nl80211_parse_sched_scan(wiphy: &rdev->wiphy, NULL, attrs: tb, |
13562 | max_match_sets: wowlan->max_nd_match_sets); |
13563 | err = PTR_ERR_OR_ZERO(ptr: trig->nd_config); |
13564 | if (err) |
13565 | trig->nd_config = NULL; |
13566 | |
13567 | out: |
13568 | kfree(objp: tb); |
13569 | return err; |
13570 | } |
13571 | |
13572 | static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info) |
13573 | { |
13574 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
13575 | struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG]; |
13576 | struct cfg80211_wowlan new_triggers = {}; |
13577 | struct cfg80211_wowlan *ntrig; |
13578 | const struct wiphy_wowlan_support *wowlan = rdev->wiphy.wowlan; |
13579 | int err, i; |
13580 | bool prev_enabled = rdev->wiphy.wowlan_config; |
13581 | bool regular = false; |
13582 | |
13583 | if (!wowlan) |
13584 | return -EOPNOTSUPP; |
13585 | |
13586 | if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) { |
13587 | cfg80211_rdev_free_wowlan(rdev); |
13588 | rdev->wiphy.wowlan_config = NULL; |
13589 | goto set_wakeup; |
13590 | } |
13591 | |
13592 | err = nla_parse_nested_deprecated(tb, maxtype: MAX_NL80211_WOWLAN_TRIG, |
13593 | nla: info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS], |
13594 | policy: nl80211_wowlan_policy, extack: info->extack); |
13595 | if (err) |
13596 | return err; |
13597 | |
13598 | if (tb[NL80211_WOWLAN_TRIG_ANY]) { |
13599 | if (!(wowlan->flags & WIPHY_WOWLAN_ANY)) |
13600 | return -EINVAL; |
13601 | new_triggers.any = true; |
13602 | } |
13603 | |
13604 | if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) { |
13605 | if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT)) |
13606 | return -EINVAL; |
13607 | new_triggers.disconnect = true; |
13608 | regular = true; |
13609 | } |
13610 | |
13611 | if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) { |
13612 | if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT)) |
13613 | return -EINVAL; |
13614 | new_triggers.magic_pkt = true; |
13615 | regular = true; |
13616 | } |
13617 | |
13618 | if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED]) |
13619 | return -EINVAL; |
13620 | |
13621 | if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) { |
13622 | if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE)) |
13623 | return -EINVAL; |
13624 | new_triggers.gtk_rekey_failure = true; |
13625 | regular = true; |
13626 | } |
13627 | |
13628 | if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) { |
13629 | if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ)) |
13630 | return -EINVAL; |
13631 | new_triggers.eap_identity_req = true; |
13632 | regular = true; |
13633 | } |
13634 | |
13635 | if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) { |
13636 | if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE)) |
13637 | return -EINVAL; |
13638 | new_triggers.four_way_handshake = true; |
13639 | regular = true; |
13640 | } |
13641 | |
13642 | if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) { |
13643 | if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE)) |
13644 | return -EINVAL; |
13645 | new_triggers.rfkill_release = true; |
13646 | regular = true; |
13647 | } |
13648 | |
13649 | if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) { |
13650 | struct nlattr *pat; |
13651 | int n_patterns = 0; |
13652 | int rem, pat_len, mask_len, pkt_offset; |
13653 | struct nlattr *pat_tb[NUM_NL80211_PKTPAT]; |
13654 | |
13655 | regular = true; |
13656 | |
13657 | nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], |
13658 | rem) |
13659 | n_patterns++; |
13660 | if (n_patterns > wowlan->n_patterns) |
13661 | return -EINVAL; |
13662 | |
13663 | new_triggers.patterns = kcalloc(n: n_patterns, |
13664 | size: sizeof(new_triggers.patterns[0]), |
13665 | GFP_KERNEL); |
13666 | if (!new_triggers.patterns) |
13667 | return -ENOMEM; |
13668 | |
13669 | new_triggers.n_patterns = n_patterns; |
13670 | i = 0; |
13671 | |
13672 | nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN], |
13673 | rem) { |
13674 | u8 *mask_pat; |
13675 | |
13676 | err = nla_parse_nested_deprecated(tb: pat_tb, |
13677 | maxtype: MAX_NL80211_PKTPAT, |
13678 | nla: pat, |
13679 | policy: nl80211_packet_pattern_policy, |
13680 | extack: info->extack); |
13681 | if (err) |
13682 | goto error; |
13683 | |
13684 | err = -EINVAL; |
13685 | if (!pat_tb[NL80211_PKTPAT_MASK] || |
13686 | !pat_tb[NL80211_PKTPAT_PATTERN]) |
13687 | goto error; |
13688 | pat_len = nla_len(nla: pat_tb[NL80211_PKTPAT_PATTERN]); |
13689 | mask_len = DIV_ROUND_UP(pat_len, 8); |
13690 | if (nla_len(nla: pat_tb[NL80211_PKTPAT_MASK]) != mask_len) |
13691 | goto error; |
13692 | if (pat_len > wowlan->pattern_max_len || |
13693 | pat_len < wowlan->pattern_min_len) |
13694 | goto error; |
13695 | |
13696 | if (!pat_tb[NL80211_PKTPAT_OFFSET]) |
13697 | pkt_offset = 0; |
13698 | else |
13699 | pkt_offset = nla_get_u32( |
13700 | nla: pat_tb[NL80211_PKTPAT_OFFSET]); |
13701 | if (pkt_offset > wowlan->max_pkt_offset) |
13702 | goto error; |
13703 | new_triggers.patterns[i].pkt_offset = pkt_offset; |
13704 | |
13705 | mask_pat = kmalloc(size: mask_len + pat_len, GFP_KERNEL); |
13706 | if (!mask_pat) { |
13707 | err = -ENOMEM; |
13708 | goto error; |
13709 | } |
13710 | new_triggers.patterns[i].mask = mask_pat; |
13711 | memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_MASK]), |
13712 | mask_len); |
13713 | mask_pat += mask_len; |
13714 | new_triggers.patterns[i].pattern = mask_pat; |
13715 | new_triggers.patterns[i].pattern_len = pat_len; |
13716 | memcpy(mask_pat, |
13717 | nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), |
13718 | pat_len); |
13719 | i++; |
13720 | } |
13721 | } |
13722 | |
13723 | if (tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION]) { |
13724 | regular = true; |
13725 | err = nl80211_parse_wowlan_tcp( |
13726 | rdev, attr: tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION], |
13727 | trig: &new_triggers); |
13728 | if (err) |
13729 | goto error; |
13730 | } |
13731 | |
13732 | if (tb[NL80211_WOWLAN_TRIG_NET_DETECT]) { |
13733 | regular = true; |
13734 | err = nl80211_parse_wowlan_nd( |
13735 | rdev, wowlan, attr: tb[NL80211_WOWLAN_TRIG_NET_DETECT], |
13736 | trig: &new_triggers); |
13737 | if (err) |
13738 | goto error; |
13739 | } |
13740 | |
13741 | /* The 'any' trigger means the device continues operating more or less |
13742 | * as in its normal operation mode and wakes up the host on most of the |
13743 | * normal interrupts (like packet RX, ...) |
13744 | * It therefore makes little sense to combine with the more constrained |
13745 | * wakeup trigger modes. |
13746 | */ |
13747 | if (new_triggers.any && regular) { |
13748 | err = -EINVAL; |
13749 | goto error; |
13750 | } |
13751 | |
13752 | ntrig = kmemdup(p: &new_triggers, size: sizeof(new_triggers), GFP_KERNEL); |
13753 | if (!ntrig) { |
13754 | err = -ENOMEM; |
13755 | goto error; |
13756 | } |
13757 | cfg80211_rdev_free_wowlan(rdev); |
13758 | rdev->wiphy.wowlan_config = ntrig; |
13759 | |
13760 | set_wakeup: |
13761 | if (rdev->ops->set_wakeup && |
13762 | prev_enabled != !!rdev->wiphy.wowlan_config) |
13763 | rdev_set_wakeup(rdev, enabled: rdev->wiphy.wowlan_config); |
13764 | |
13765 | return 0; |
13766 | error: |
13767 | for (i = 0; i < new_triggers.n_patterns; i++) |
13768 | kfree(objp: new_triggers.patterns[i].mask); |
13769 | kfree(objp: new_triggers.patterns); |
13770 | if (new_triggers.tcp && new_triggers.tcp->sock) |
13771 | sock_release(sock: new_triggers.tcp->sock); |
13772 | kfree(objp: new_triggers.tcp); |
13773 | kfree(objp: new_triggers.nd_config); |
13774 | return err; |
13775 | } |
13776 | #endif |
13777 | |
13778 | static int nl80211_send_coalesce_rules(struct sk_buff *msg, |
13779 | struct cfg80211_registered_device *rdev) |
13780 | { |
13781 | struct nlattr *nl_pats, *nl_pat, *nl_rule, *nl_rules; |
13782 | int i, j, pat_len; |
13783 | struct cfg80211_coalesce_rules *rule; |
13784 | |
13785 | if (!rdev->coalesce->n_rules) |
13786 | return 0; |
13787 | |
13788 | nl_rules = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_COALESCE_RULE); |
13789 | if (!nl_rules) |
13790 | return -ENOBUFS; |
13791 | |
13792 | for (i = 0; i < rdev->coalesce->n_rules; i++) { |
13793 | nl_rule = nla_nest_start_noflag(skb: msg, attrtype: i + 1); |
13794 | if (!nl_rule) |
13795 | return -ENOBUFS; |
13796 | |
13797 | rule = &rdev->coalesce->rules[i]; |
13798 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_COALESCE_RULE_DELAY, |
13799 | value: rule->delay)) |
13800 | return -ENOBUFS; |
13801 | |
13802 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_COALESCE_RULE_CONDITION, |
13803 | value: rule->condition)) |
13804 | return -ENOBUFS; |
13805 | |
13806 | nl_pats = nla_nest_start_noflag(skb: msg, |
13807 | attrtype: NL80211_ATTR_COALESCE_RULE_PKT_PATTERN); |
13808 | if (!nl_pats) |
13809 | return -ENOBUFS; |
13810 | |
13811 | for (j = 0; j < rule->n_patterns; j++) { |
13812 | nl_pat = nla_nest_start_noflag(skb: msg, attrtype: j + 1); |
13813 | if (!nl_pat) |
13814 | return -ENOBUFS; |
13815 | pat_len = rule->patterns[j].pattern_len; |
13816 | if (nla_put(skb: msg, attrtype: NL80211_PKTPAT_MASK, |
13817 | DIV_ROUND_UP(pat_len, 8), |
13818 | data: rule->patterns[j].mask) || |
13819 | nla_put(skb: msg, attrtype: NL80211_PKTPAT_PATTERN, attrlen: pat_len, |
13820 | data: rule->patterns[j].pattern) || |
13821 | nla_put_u32(skb: msg, attrtype: NL80211_PKTPAT_OFFSET, |
13822 | value: rule->patterns[j].pkt_offset)) |
13823 | return -ENOBUFS; |
13824 | nla_nest_end(skb: msg, start: nl_pat); |
13825 | } |
13826 | nla_nest_end(skb: msg, start: nl_pats); |
13827 | nla_nest_end(skb: msg, start: nl_rule); |
13828 | } |
13829 | nla_nest_end(skb: msg, start: nl_rules); |
13830 | |
13831 | return 0; |
13832 | } |
13833 | |
13834 | static int nl80211_get_coalesce(struct sk_buff *skb, struct genl_info *info) |
13835 | { |
13836 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
13837 | struct sk_buff *msg; |
13838 | void *hdr; |
13839 | |
13840 | if (!rdev->wiphy.coalesce) |
13841 | return -EOPNOTSUPP; |
13842 | |
13843 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
13844 | if (!msg) |
13845 | return -ENOMEM; |
13846 | |
13847 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
13848 | cmd: NL80211_CMD_GET_COALESCE); |
13849 | if (!hdr) |
13850 | goto nla_put_failure; |
13851 | |
13852 | if (rdev->coalesce && nl80211_send_coalesce_rules(msg, rdev)) |
13853 | goto nla_put_failure; |
13854 | |
13855 | genlmsg_end(skb: msg, hdr); |
13856 | return genlmsg_reply(skb: msg, info); |
13857 | |
13858 | nla_put_failure: |
13859 | nlmsg_free(skb: msg); |
13860 | return -ENOBUFS; |
13861 | } |
13862 | |
13863 | void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev) |
13864 | { |
13865 | struct cfg80211_coalesce *coalesce = rdev->coalesce; |
13866 | int i, j; |
13867 | struct cfg80211_coalesce_rules *rule; |
13868 | |
13869 | if (!coalesce) |
13870 | return; |
13871 | |
13872 | for (i = 0; i < coalesce->n_rules; i++) { |
13873 | rule = &coalesce->rules[i]; |
13874 | for (j = 0; j < rule->n_patterns; j++) |
13875 | kfree(objp: rule->patterns[j].mask); |
13876 | kfree(objp: rule->patterns); |
13877 | } |
13878 | kfree(objp: coalesce->rules); |
13879 | kfree(objp: coalesce); |
13880 | rdev->coalesce = NULL; |
13881 | } |
13882 | |
13883 | static int nl80211_parse_coalesce_rule(struct cfg80211_registered_device *rdev, |
13884 | struct nlattr *rule, |
13885 | struct cfg80211_coalesce_rules *new_rule) |
13886 | { |
13887 | int err, i; |
13888 | const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce; |
13889 | struct nlattr *tb[NUM_NL80211_ATTR_COALESCE_RULE], *pat; |
13890 | int rem, pat_len, mask_len, pkt_offset, n_patterns = 0; |
13891 | struct nlattr *pat_tb[NUM_NL80211_PKTPAT]; |
13892 | |
13893 | err = nla_parse_nested_deprecated(tb, maxtype: NL80211_ATTR_COALESCE_RULE_MAX, |
13894 | nla: rule, policy: nl80211_coalesce_policy, NULL); |
13895 | if (err) |
13896 | return err; |
13897 | |
13898 | if (tb[NL80211_ATTR_COALESCE_RULE_DELAY]) |
13899 | new_rule->delay = |
13900 | nla_get_u32(nla: tb[NL80211_ATTR_COALESCE_RULE_DELAY]); |
13901 | if (new_rule->delay > coalesce->max_delay) |
13902 | return -EINVAL; |
13903 | |
13904 | if (tb[NL80211_ATTR_COALESCE_RULE_CONDITION]) |
13905 | new_rule->condition = |
13906 | nla_get_u32(nla: tb[NL80211_ATTR_COALESCE_RULE_CONDITION]); |
13907 | |
13908 | if (!tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN]) |
13909 | return -EINVAL; |
13910 | |
13911 | nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN], |
13912 | rem) |
13913 | n_patterns++; |
13914 | if (n_patterns > coalesce->n_patterns) |
13915 | return -EINVAL; |
13916 | |
13917 | new_rule->patterns = kcalloc(n: n_patterns, size: sizeof(new_rule->patterns[0]), |
13918 | GFP_KERNEL); |
13919 | if (!new_rule->patterns) |
13920 | return -ENOMEM; |
13921 | |
13922 | new_rule->n_patterns = n_patterns; |
13923 | i = 0; |
13924 | |
13925 | nla_for_each_nested(pat, tb[NL80211_ATTR_COALESCE_RULE_PKT_PATTERN], |
13926 | rem) { |
13927 | u8 *mask_pat; |
13928 | |
13929 | err = nla_parse_nested_deprecated(tb: pat_tb, maxtype: MAX_NL80211_PKTPAT, |
13930 | nla: pat, |
13931 | policy: nl80211_packet_pattern_policy, |
13932 | NULL); |
13933 | if (err) |
13934 | return err; |
13935 | |
13936 | if (!pat_tb[NL80211_PKTPAT_MASK] || |
13937 | !pat_tb[NL80211_PKTPAT_PATTERN]) |
13938 | return -EINVAL; |
13939 | pat_len = nla_len(nla: pat_tb[NL80211_PKTPAT_PATTERN]); |
13940 | mask_len = DIV_ROUND_UP(pat_len, 8); |
13941 | if (nla_len(nla: pat_tb[NL80211_PKTPAT_MASK]) != mask_len) |
13942 | return -EINVAL; |
13943 | if (pat_len > coalesce->pattern_max_len || |
13944 | pat_len < coalesce->pattern_min_len) |
13945 | return -EINVAL; |
13946 | |
13947 | if (!pat_tb[NL80211_PKTPAT_OFFSET]) |
13948 | pkt_offset = 0; |
13949 | else |
13950 | pkt_offset = nla_get_u32(nla: pat_tb[NL80211_PKTPAT_OFFSET]); |
13951 | if (pkt_offset > coalesce->max_pkt_offset) |
13952 | return -EINVAL; |
13953 | new_rule->patterns[i].pkt_offset = pkt_offset; |
13954 | |
13955 | mask_pat = kmalloc(size: mask_len + pat_len, GFP_KERNEL); |
13956 | if (!mask_pat) |
13957 | return -ENOMEM; |
13958 | |
13959 | new_rule->patterns[i].mask = mask_pat; |
13960 | memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_MASK]), |
13961 | mask_len); |
13962 | |
13963 | mask_pat += mask_len; |
13964 | new_rule->patterns[i].pattern = mask_pat; |
13965 | new_rule->patterns[i].pattern_len = pat_len; |
13966 | memcpy(mask_pat, nla_data(pat_tb[NL80211_PKTPAT_PATTERN]), |
13967 | pat_len); |
13968 | i++; |
13969 | } |
13970 | |
13971 | return 0; |
13972 | } |
13973 | |
13974 | static int nl80211_set_coalesce(struct sk_buff *skb, struct genl_info *info) |
13975 | { |
13976 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
13977 | const struct wiphy_coalesce_support *coalesce = rdev->wiphy.coalesce; |
13978 | struct cfg80211_coalesce new_coalesce = {}; |
13979 | struct cfg80211_coalesce *n_coalesce; |
13980 | int err, rem_rule, n_rules = 0, i, j; |
13981 | struct nlattr *rule; |
13982 | struct cfg80211_coalesce_rules *tmp_rule; |
13983 | |
13984 | if (!rdev->wiphy.coalesce || !rdev->ops->set_coalesce) |
13985 | return -EOPNOTSUPP; |
13986 | |
13987 | if (!info->attrs[NL80211_ATTR_COALESCE_RULE]) { |
13988 | cfg80211_rdev_free_coalesce(rdev); |
13989 | rdev_set_coalesce(rdev, NULL); |
13990 | return 0; |
13991 | } |
13992 | |
13993 | nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE], |
13994 | rem_rule) |
13995 | n_rules++; |
13996 | if (n_rules > coalesce->n_rules) |
13997 | return -EINVAL; |
13998 | |
13999 | new_coalesce.rules = kcalloc(n: n_rules, size: sizeof(new_coalesce.rules[0]), |
14000 | GFP_KERNEL); |
14001 | if (!new_coalesce.rules) |
14002 | return -ENOMEM; |
14003 | |
14004 | new_coalesce.n_rules = n_rules; |
14005 | i = 0; |
14006 | |
14007 | nla_for_each_nested(rule, info->attrs[NL80211_ATTR_COALESCE_RULE], |
14008 | rem_rule) { |
14009 | err = nl80211_parse_coalesce_rule(rdev, rule, |
14010 | new_rule: &new_coalesce.rules[i]); |
14011 | if (err) |
14012 | goto error; |
14013 | |
14014 | i++; |
14015 | } |
14016 | |
14017 | err = rdev_set_coalesce(rdev, coalesce: &new_coalesce); |
14018 | if (err) |
14019 | goto error; |
14020 | |
14021 | n_coalesce = kmemdup(p: &new_coalesce, size: sizeof(new_coalesce), GFP_KERNEL); |
14022 | if (!n_coalesce) { |
14023 | err = -ENOMEM; |
14024 | goto error; |
14025 | } |
14026 | cfg80211_rdev_free_coalesce(rdev); |
14027 | rdev->coalesce = n_coalesce; |
14028 | |
14029 | return 0; |
14030 | error: |
14031 | for (i = 0; i < new_coalesce.n_rules; i++) { |
14032 | tmp_rule = &new_coalesce.rules[i]; |
14033 | for (j = 0; j < tmp_rule->n_patterns; j++) |
14034 | kfree(objp: tmp_rule->patterns[j].mask); |
14035 | kfree(objp: tmp_rule->patterns); |
14036 | } |
14037 | kfree(objp: new_coalesce.rules); |
14038 | |
14039 | return err; |
14040 | } |
14041 | |
14042 | static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info) |
14043 | { |
14044 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
14045 | struct net_device *dev = info->user_ptr[1]; |
14046 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
14047 | struct nlattr *tb[NUM_NL80211_REKEY_DATA]; |
14048 | struct cfg80211_gtk_rekey_data rekey_data = {}; |
14049 | int err; |
14050 | |
14051 | if (!info->attrs[NL80211_ATTR_REKEY_DATA]) |
14052 | return -EINVAL; |
14053 | |
14054 | err = nla_parse_nested_deprecated(tb, maxtype: MAX_NL80211_REKEY_DATA, |
14055 | nla: info->attrs[NL80211_ATTR_REKEY_DATA], |
14056 | policy: nl80211_rekey_policy, extack: info->extack); |
14057 | if (err) |
14058 | return err; |
14059 | |
14060 | if (!tb[NL80211_REKEY_DATA_REPLAY_CTR] || !tb[NL80211_REKEY_DATA_KEK] || |
14061 | !tb[NL80211_REKEY_DATA_KCK]) |
14062 | return -EINVAL; |
14063 | if (nla_len(nla: tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN && |
14064 | !(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK && |
14065 | nla_len(nla: tb[NL80211_REKEY_DATA_KEK]) == NL80211_KEK_EXT_LEN)) |
14066 | return -ERANGE; |
14067 | if (nla_len(nla: tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN && |
14068 | !(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK && |
14069 | nla_len(nla: tb[NL80211_REKEY_DATA_KCK]) == NL80211_KCK_EXT_LEN) && |
14070 | !(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_EXT_KCK_32 && |
14071 | nla_len(nla: tb[NL80211_REKEY_DATA_KCK]) == NL80211_KCK_EXT_LEN_32)) |
14072 | return -ERANGE; |
14073 | |
14074 | rekey_data.kek = nla_data(nla: tb[NL80211_REKEY_DATA_KEK]); |
14075 | rekey_data.kck = nla_data(nla: tb[NL80211_REKEY_DATA_KCK]); |
14076 | rekey_data.replay_ctr = nla_data(nla: tb[NL80211_REKEY_DATA_REPLAY_CTR]); |
14077 | rekey_data.kek_len = nla_len(nla: tb[NL80211_REKEY_DATA_KEK]); |
14078 | rekey_data.kck_len = nla_len(nla: tb[NL80211_REKEY_DATA_KCK]); |
14079 | if (tb[NL80211_REKEY_DATA_AKM]) |
14080 | rekey_data.akm = nla_get_u32(nla: tb[NL80211_REKEY_DATA_AKM]); |
14081 | |
14082 | if (!wdev->connected) |
14083 | return -ENOTCONN; |
14084 | |
14085 | if (!rdev->ops->set_rekey_data) |
14086 | return -EOPNOTSUPP; |
14087 | |
14088 | return rdev_set_rekey_data(rdev, dev, data: &rekey_data); |
14089 | } |
14090 | |
14091 | static int nl80211_register_unexpected_frame(struct sk_buff *skb, |
14092 | struct genl_info *info) |
14093 | { |
14094 | struct net_device *dev = info->user_ptr[1]; |
14095 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
14096 | |
14097 | if (wdev->iftype != NL80211_IFTYPE_AP && |
14098 | wdev->iftype != NL80211_IFTYPE_P2P_GO) |
14099 | return -EINVAL; |
14100 | |
14101 | if (wdev->ap_unexpected_nlportid) |
14102 | return -EBUSY; |
14103 | |
14104 | wdev->ap_unexpected_nlportid = info->snd_portid; |
14105 | return 0; |
14106 | } |
14107 | |
14108 | static int nl80211_probe_client(struct sk_buff *skb, |
14109 | struct genl_info *info) |
14110 | { |
14111 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
14112 | struct net_device *dev = info->user_ptr[1]; |
14113 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
14114 | struct sk_buff *msg; |
14115 | void *hdr; |
14116 | const u8 *addr; |
14117 | u64 cookie; |
14118 | int err; |
14119 | |
14120 | if (wdev->iftype != NL80211_IFTYPE_AP && |
14121 | wdev->iftype != NL80211_IFTYPE_P2P_GO) |
14122 | return -EOPNOTSUPP; |
14123 | |
14124 | if (!info->attrs[NL80211_ATTR_MAC]) |
14125 | return -EINVAL; |
14126 | |
14127 | if (!rdev->ops->probe_client) |
14128 | return -EOPNOTSUPP; |
14129 | |
14130 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
14131 | if (!msg) |
14132 | return -ENOMEM; |
14133 | |
14134 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
14135 | cmd: NL80211_CMD_PROBE_CLIENT); |
14136 | if (!hdr) { |
14137 | err = -ENOBUFS; |
14138 | goto free_msg; |
14139 | } |
14140 | |
14141 | addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
14142 | |
14143 | err = rdev_probe_client(rdev, dev, peer: addr, cookie: &cookie); |
14144 | if (err) |
14145 | goto free_msg; |
14146 | |
14147 | if (nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: cookie, |
14148 | padattr: NL80211_ATTR_PAD)) |
14149 | goto nla_put_failure; |
14150 | |
14151 | genlmsg_end(skb: msg, hdr); |
14152 | |
14153 | return genlmsg_reply(skb: msg, info); |
14154 | |
14155 | nla_put_failure: |
14156 | err = -ENOBUFS; |
14157 | free_msg: |
14158 | nlmsg_free(skb: msg); |
14159 | return err; |
14160 | } |
14161 | |
14162 | static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info) |
14163 | { |
14164 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
14165 | struct cfg80211_beacon_registration *reg, *nreg; |
14166 | int rv; |
14167 | |
14168 | if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS)) |
14169 | return -EOPNOTSUPP; |
14170 | |
14171 | nreg = kzalloc(size: sizeof(*nreg), GFP_KERNEL); |
14172 | if (!nreg) |
14173 | return -ENOMEM; |
14174 | |
14175 | /* First, check if already registered. */ |
14176 | spin_lock_bh(lock: &rdev->beacon_registrations_lock); |
14177 | list_for_each_entry(reg, &rdev->beacon_registrations, list) { |
14178 | if (reg->nlportid == info->snd_portid) { |
14179 | rv = -EALREADY; |
14180 | goto out_err; |
14181 | } |
14182 | } |
14183 | /* Add it to the list */ |
14184 | nreg->nlportid = info->snd_portid; |
14185 | list_add(new: &nreg->list, head: &rdev->beacon_registrations); |
14186 | |
14187 | spin_unlock_bh(lock: &rdev->beacon_registrations_lock); |
14188 | |
14189 | return 0; |
14190 | out_err: |
14191 | spin_unlock_bh(lock: &rdev->beacon_registrations_lock); |
14192 | kfree(objp: nreg); |
14193 | return rv; |
14194 | } |
14195 | |
14196 | static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info) |
14197 | { |
14198 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
14199 | struct wireless_dev *wdev = info->user_ptr[1]; |
14200 | int err; |
14201 | |
14202 | if (!rdev->ops->start_p2p_device) |
14203 | return -EOPNOTSUPP; |
14204 | |
14205 | if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE) |
14206 | return -EOPNOTSUPP; |
14207 | |
14208 | if (wdev_running(wdev)) |
14209 | return 0; |
14210 | |
14211 | if (rfkill_blocked(rfkill: rdev->wiphy.rfkill)) |
14212 | return -ERFKILL; |
14213 | |
14214 | err = rdev_start_p2p_device(rdev, wdev); |
14215 | if (err) |
14216 | return err; |
14217 | |
14218 | wdev->is_running = true; |
14219 | rdev->opencount++; |
14220 | |
14221 | return 0; |
14222 | } |
14223 | |
14224 | static int nl80211_stop_p2p_device(struct sk_buff *skb, struct genl_info *info) |
14225 | { |
14226 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
14227 | struct wireless_dev *wdev = info->user_ptr[1]; |
14228 | |
14229 | if (wdev->iftype != NL80211_IFTYPE_P2P_DEVICE) |
14230 | return -EOPNOTSUPP; |
14231 | |
14232 | if (!rdev->ops->stop_p2p_device) |
14233 | return -EOPNOTSUPP; |
14234 | |
14235 | cfg80211_stop_p2p_device(rdev, wdev); |
14236 | |
14237 | return 0; |
14238 | } |
14239 | |
14240 | static int nl80211_start_nan(struct sk_buff *skb, struct genl_info *info) |
14241 | { |
14242 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
14243 | struct wireless_dev *wdev = info->user_ptr[1]; |
14244 | struct cfg80211_nan_conf conf = {}; |
14245 | int err; |
14246 | |
14247 | if (wdev->iftype != NL80211_IFTYPE_NAN) |
14248 | return -EOPNOTSUPP; |
14249 | |
14250 | if (wdev_running(wdev)) |
14251 | return -EEXIST; |
14252 | |
14253 | if (rfkill_blocked(rfkill: rdev->wiphy.rfkill)) |
14254 | return -ERFKILL; |
14255 | |
14256 | if (!info->attrs[NL80211_ATTR_NAN_MASTER_PREF]) |
14257 | return -EINVAL; |
14258 | |
14259 | conf.master_pref = |
14260 | nla_get_u8(nla: info->attrs[NL80211_ATTR_NAN_MASTER_PREF]); |
14261 | |
14262 | if (info->attrs[NL80211_ATTR_BANDS]) { |
14263 | u32 bands = nla_get_u32(nla: info->attrs[NL80211_ATTR_BANDS]); |
14264 | |
14265 | if (bands & ~(u32)wdev->wiphy->nan_supported_bands) |
14266 | return -EOPNOTSUPP; |
14267 | |
14268 | if (bands && !(bands & BIT(NL80211_BAND_2GHZ))) |
14269 | return -EINVAL; |
14270 | |
14271 | conf.bands = bands; |
14272 | } |
14273 | |
14274 | err = rdev_start_nan(rdev, wdev, conf: &conf); |
14275 | if (err) |
14276 | return err; |
14277 | |
14278 | wdev->is_running = true; |
14279 | rdev->opencount++; |
14280 | |
14281 | return 0; |
14282 | } |
14283 | |
14284 | static int nl80211_stop_nan(struct sk_buff *skb, struct genl_info *info) |
14285 | { |
14286 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
14287 | struct wireless_dev *wdev = info->user_ptr[1]; |
14288 | |
14289 | if (wdev->iftype != NL80211_IFTYPE_NAN) |
14290 | return -EOPNOTSUPP; |
14291 | |
14292 | cfg80211_stop_nan(rdev, wdev); |
14293 | |
14294 | return 0; |
14295 | } |
14296 | |
14297 | static int validate_nan_filter(struct nlattr *filter_attr) |
14298 | { |
14299 | struct nlattr *attr; |
14300 | int len = 0, n_entries = 0, rem; |
14301 | |
14302 | nla_for_each_nested(attr, filter_attr, rem) { |
14303 | len += nla_len(nla: attr); |
14304 | n_entries++; |
14305 | } |
14306 | |
14307 | if (len >= U8_MAX) |
14308 | return -EINVAL; |
14309 | |
14310 | return n_entries; |
14311 | } |
14312 | |
14313 | static int handle_nan_filter(struct nlattr *attr_filter, |
14314 | struct cfg80211_nan_func *func, |
14315 | bool tx) |
14316 | { |
14317 | struct nlattr *attr; |
14318 | int n_entries, rem, i; |
14319 | struct cfg80211_nan_func_filter *filter; |
14320 | |
14321 | n_entries = validate_nan_filter(filter_attr: attr_filter); |
14322 | if (n_entries < 0) |
14323 | return n_entries; |
14324 | |
14325 | BUILD_BUG_ON(sizeof(*func->rx_filters) != sizeof(*func->tx_filters)); |
14326 | |
14327 | filter = kcalloc(n: n_entries, size: sizeof(*func->rx_filters), GFP_KERNEL); |
14328 | if (!filter) |
14329 | return -ENOMEM; |
14330 | |
14331 | i = 0; |
14332 | nla_for_each_nested(attr, attr_filter, rem) { |
14333 | filter[i].filter = nla_memdup(src: attr, GFP_KERNEL); |
14334 | if (!filter[i].filter) |
14335 | goto err; |
14336 | |
14337 | filter[i].len = nla_len(nla: attr); |
14338 | i++; |
14339 | } |
14340 | if (tx) { |
14341 | func->num_tx_filters = n_entries; |
14342 | func->tx_filters = filter; |
14343 | } else { |
14344 | func->num_rx_filters = n_entries; |
14345 | func->rx_filters = filter; |
14346 | } |
14347 | |
14348 | return 0; |
14349 | |
14350 | err: |
14351 | i = 0; |
14352 | nla_for_each_nested(attr, attr_filter, rem) { |
14353 | kfree(objp: filter[i].filter); |
14354 | i++; |
14355 | } |
14356 | kfree(objp: filter); |
14357 | return -ENOMEM; |
14358 | } |
14359 | |
14360 | static int nl80211_nan_add_func(struct sk_buff *skb, |
14361 | struct genl_info *info) |
14362 | { |
14363 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
14364 | struct wireless_dev *wdev = info->user_ptr[1]; |
14365 | struct nlattr *tb[NUM_NL80211_NAN_FUNC_ATTR], *func_attr; |
14366 | struct cfg80211_nan_func *func; |
14367 | struct sk_buff *msg = NULL; |
14368 | void *hdr = NULL; |
14369 | int err = 0; |
14370 | |
14371 | if (wdev->iftype != NL80211_IFTYPE_NAN) |
14372 | return -EOPNOTSUPP; |
14373 | |
14374 | if (!wdev_running(wdev)) |
14375 | return -ENOTCONN; |
14376 | |
14377 | if (!info->attrs[NL80211_ATTR_NAN_FUNC]) |
14378 | return -EINVAL; |
14379 | |
14380 | err = nla_parse_nested_deprecated(tb, maxtype: NL80211_NAN_FUNC_ATTR_MAX, |
14381 | nla: info->attrs[NL80211_ATTR_NAN_FUNC], |
14382 | policy: nl80211_nan_func_policy, |
14383 | extack: info->extack); |
14384 | if (err) |
14385 | return err; |
14386 | |
14387 | func = kzalloc(size: sizeof(*func), GFP_KERNEL); |
14388 | if (!func) |
14389 | return -ENOMEM; |
14390 | |
14391 | func->cookie = cfg80211_assign_cookie(rdev); |
14392 | |
14393 | if (!tb[NL80211_NAN_FUNC_TYPE]) { |
14394 | err = -EINVAL; |
14395 | goto out; |
14396 | } |
14397 | |
14398 | |
14399 | func->type = nla_get_u8(nla: tb[NL80211_NAN_FUNC_TYPE]); |
14400 | |
14401 | if (!tb[NL80211_NAN_FUNC_SERVICE_ID]) { |
14402 | err = -EINVAL; |
14403 | goto out; |
14404 | } |
14405 | |
14406 | memcpy(func->service_id, nla_data(tb[NL80211_NAN_FUNC_SERVICE_ID]), |
14407 | sizeof(func->service_id)); |
14408 | |
14409 | func->close_range = |
14410 | nla_get_flag(nla: tb[NL80211_NAN_FUNC_CLOSE_RANGE]); |
14411 | |
14412 | if (tb[NL80211_NAN_FUNC_SERVICE_INFO]) { |
14413 | func->serv_spec_info_len = |
14414 | nla_len(nla: tb[NL80211_NAN_FUNC_SERVICE_INFO]); |
14415 | func->serv_spec_info = |
14416 | kmemdup(p: nla_data(nla: tb[NL80211_NAN_FUNC_SERVICE_INFO]), |
14417 | size: func->serv_spec_info_len, |
14418 | GFP_KERNEL); |
14419 | if (!func->serv_spec_info) { |
14420 | err = -ENOMEM; |
14421 | goto out; |
14422 | } |
14423 | } |
14424 | |
14425 | if (tb[NL80211_NAN_FUNC_TTL]) |
14426 | func->ttl = nla_get_u32(nla: tb[NL80211_NAN_FUNC_TTL]); |
14427 | |
14428 | switch (func->type) { |
14429 | case NL80211_NAN_FUNC_PUBLISH: |
14430 | if (!tb[NL80211_NAN_FUNC_PUBLISH_TYPE]) { |
14431 | err = -EINVAL; |
14432 | goto out; |
14433 | } |
14434 | |
14435 | func->publish_type = |
14436 | nla_get_u8(nla: tb[NL80211_NAN_FUNC_PUBLISH_TYPE]); |
14437 | func->publish_bcast = |
14438 | nla_get_flag(nla: tb[NL80211_NAN_FUNC_PUBLISH_BCAST]); |
14439 | |
14440 | if ((!(func->publish_type & NL80211_NAN_SOLICITED_PUBLISH)) && |
14441 | func->publish_bcast) { |
14442 | err = -EINVAL; |
14443 | goto out; |
14444 | } |
14445 | break; |
14446 | case NL80211_NAN_FUNC_SUBSCRIBE: |
14447 | func->subscribe_active = |
14448 | nla_get_flag(nla: tb[NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE]); |
14449 | break; |
14450 | case NL80211_NAN_FUNC_FOLLOW_UP: |
14451 | if (!tb[NL80211_NAN_FUNC_FOLLOW_UP_ID] || |
14452 | !tb[NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID] || |
14453 | !tb[NL80211_NAN_FUNC_FOLLOW_UP_DEST]) { |
14454 | err = -EINVAL; |
14455 | goto out; |
14456 | } |
14457 | |
14458 | func->followup_id = |
14459 | nla_get_u8(nla: tb[NL80211_NAN_FUNC_FOLLOW_UP_ID]); |
14460 | func->followup_reqid = |
14461 | nla_get_u8(nla: tb[NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID]); |
14462 | memcpy(func->followup_dest.addr, |
14463 | nla_data(tb[NL80211_NAN_FUNC_FOLLOW_UP_DEST]), |
14464 | sizeof(func->followup_dest.addr)); |
14465 | if (func->ttl) { |
14466 | err = -EINVAL; |
14467 | goto out; |
14468 | } |
14469 | break; |
14470 | default: |
14471 | err = -EINVAL; |
14472 | goto out; |
14473 | } |
14474 | |
14475 | if (tb[NL80211_NAN_FUNC_SRF]) { |
14476 | struct nlattr *srf_tb[NUM_NL80211_NAN_SRF_ATTR]; |
14477 | |
14478 | err = nla_parse_nested_deprecated(tb: srf_tb, |
14479 | maxtype: NL80211_NAN_SRF_ATTR_MAX, |
14480 | nla: tb[NL80211_NAN_FUNC_SRF], |
14481 | policy: nl80211_nan_srf_policy, |
14482 | extack: info->extack); |
14483 | if (err) |
14484 | goto out; |
14485 | |
14486 | func->srf_include = |
14487 | nla_get_flag(nla: srf_tb[NL80211_NAN_SRF_INCLUDE]); |
14488 | |
14489 | if (srf_tb[NL80211_NAN_SRF_BF]) { |
14490 | if (srf_tb[NL80211_NAN_SRF_MAC_ADDRS] || |
14491 | !srf_tb[NL80211_NAN_SRF_BF_IDX]) { |
14492 | err = -EINVAL; |
14493 | goto out; |
14494 | } |
14495 | |
14496 | func->srf_bf_len = |
14497 | nla_len(nla: srf_tb[NL80211_NAN_SRF_BF]); |
14498 | func->srf_bf = |
14499 | kmemdup(p: nla_data(nla: srf_tb[NL80211_NAN_SRF_BF]), |
14500 | size: func->srf_bf_len, GFP_KERNEL); |
14501 | if (!func->srf_bf) { |
14502 | err = -ENOMEM; |
14503 | goto out; |
14504 | } |
14505 | |
14506 | func->srf_bf_idx = |
14507 | nla_get_u8(nla: srf_tb[NL80211_NAN_SRF_BF_IDX]); |
14508 | } else { |
14509 | struct nlattr *attr, *mac_attr = |
14510 | srf_tb[NL80211_NAN_SRF_MAC_ADDRS]; |
14511 | int n_entries, rem, i = 0; |
14512 | |
14513 | if (!mac_attr) { |
14514 | err = -EINVAL; |
14515 | goto out; |
14516 | } |
14517 | |
14518 | n_entries = validate_acl_mac_addrs(nl_attr: mac_attr); |
14519 | if (n_entries <= 0) { |
14520 | err = -EINVAL; |
14521 | goto out; |
14522 | } |
14523 | |
14524 | func->srf_num_macs = n_entries; |
14525 | func->srf_macs = |
14526 | kcalloc(n: n_entries, size: sizeof(*func->srf_macs), |
14527 | GFP_KERNEL); |
14528 | if (!func->srf_macs) { |
14529 | err = -ENOMEM; |
14530 | goto out; |
14531 | } |
14532 | |
14533 | nla_for_each_nested(attr, mac_attr, rem) |
14534 | memcpy(func->srf_macs[i++].addr, nla_data(attr), |
14535 | sizeof(*func->srf_macs)); |
14536 | } |
14537 | } |
14538 | |
14539 | if (tb[NL80211_NAN_FUNC_TX_MATCH_FILTER]) { |
14540 | err = handle_nan_filter(attr_filter: tb[NL80211_NAN_FUNC_TX_MATCH_FILTER], |
14541 | func, tx: true); |
14542 | if (err) |
14543 | goto out; |
14544 | } |
14545 | |
14546 | if (tb[NL80211_NAN_FUNC_RX_MATCH_FILTER]) { |
14547 | err = handle_nan_filter(attr_filter: tb[NL80211_NAN_FUNC_RX_MATCH_FILTER], |
14548 | func, tx: false); |
14549 | if (err) |
14550 | goto out; |
14551 | } |
14552 | |
14553 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
14554 | if (!msg) { |
14555 | err = -ENOMEM; |
14556 | goto out; |
14557 | } |
14558 | |
14559 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
14560 | cmd: NL80211_CMD_ADD_NAN_FUNCTION); |
14561 | /* This can't really happen - we just allocated 4KB */ |
14562 | if (WARN_ON(!hdr)) { |
14563 | err = -ENOMEM; |
14564 | goto out; |
14565 | } |
14566 | |
14567 | err = rdev_add_nan_func(rdev, wdev, nan_func: func); |
14568 | out: |
14569 | if (err < 0) { |
14570 | cfg80211_free_nan_func(f: func); |
14571 | nlmsg_free(skb: msg); |
14572 | return err; |
14573 | } |
14574 | |
14575 | /* propagate the instance id and cookie to userspace */ |
14576 | if (nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: func->cookie, |
14577 | padattr: NL80211_ATTR_PAD)) |
14578 | goto nla_put_failure; |
14579 | |
14580 | func_attr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_NAN_FUNC); |
14581 | if (!func_attr) |
14582 | goto nla_put_failure; |
14583 | |
14584 | if (nla_put_u8(skb: msg, attrtype: NL80211_NAN_FUNC_INSTANCE_ID, |
14585 | value: func->instance_id)) |
14586 | goto nla_put_failure; |
14587 | |
14588 | nla_nest_end(skb: msg, start: func_attr); |
14589 | |
14590 | genlmsg_end(skb: msg, hdr); |
14591 | return genlmsg_reply(skb: msg, info); |
14592 | |
14593 | nla_put_failure: |
14594 | nlmsg_free(skb: msg); |
14595 | return -ENOBUFS; |
14596 | } |
14597 | |
14598 | static int nl80211_nan_del_func(struct sk_buff *skb, |
14599 | struct genl_info *info) |
14600 | { |
14601 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
14602 | struct wireless_dev *wdev = info->user_ptr[1]; |
14603 | u64 cookie; |
14604 | |
14605 | if (wdev->iftype != NL80211_IFTYPE_NAN) |
14606 | return -EOPNOTSUPP; |
14607 | |
14608 | if (!wdev_running(wdev)) |
14609 | return -ENOTCONN; |
14610 | |
14611 | if (!info->attrs[NL80211_ATTR_COOKIE]) |
14612 | return -EINVAL; |
14613 | |
14614 | cookie = nla_get_u64(nla: info->attrs[NL80211_ATTR_COOKIE]); |
14615 | |
14616 | rdev_del_nan_func(rdev, wdev, cookie); |
14617 | |
14618 | return 0; |
14619 | } |
14620 | |
14621 | static int nl80211_nan_change_config(struct sk_buff *skb, |
14622 | struct genl_info *info) |
14623 | { |
14624 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
14625 | struct wireless_dev *wdev = info->user_ptr[1]; |
14626 | struct cfg80211_nan_conf conf = {}; |
14627 | u32 changed = 0; |
14628 | |
14629 | if (wdev->iftype != NL80211_IFTYPE_NAN) |
14630 | return -EOPNOTSUPP; |
14631 | |
14632 | if (!wdev_running(wdev)) |
14633 | return -ENOTCONN; |
14634 | |
14635 | if (info->attrs[NL80211_ATTR_NAN_MASTER_PREF]) { |
14636 | conf.master_pref = |
14637 | nla_get_u8(nla: info->attrs[NL80211_ATTR_NAN_MASTER_PREF]); |
14638 | if (conf.master_pref <= 1 || conf.master_pref == 255) |
14639 | return -EINVAL; |
14640 | |
14641 | changed |= CFG80211_NAN_CONF_CHANGED_PREF; |
14642 | } |
14643 | |
14644 | if (info->attrs[NL80211_ATTR_BANDS]) { |
14645 | u32 bands = nla_get_u32(nla: info->attrs[NL80211_ATTR_BANDS]); |
14646 | |
14647 | if (bands & ~(u32)wdev->wiphy->nan_supported_bands) |
14648 | return -EOPNOTSUPP; |
14649 | |
14650 | if (bands && !(bands & BIT(NL80211_BAND_2GHZ))) |
14651 | return -EINVAL; |
14652 | |
14653 | conf.bands = bands; |
14654 | changed |= CFG80211_NAN_CONF_CHANGED_BANDS; |
14655 | } |
14656 | |
14657 | if (!changed) |
14658 | return -EINVAL; |
14659 | |
14660 | return rdev_nan_change_conf(rdev, wdev, conf: &conf, changes: changed); |
14661 | } |
14662 | |
14663 | void cfg80211_nan_match(struct wireless_dev *wdev, |
14664 | struct cfg80211_nan_match_params *match, gfp_t gfp) |
14665 | { |
14666 | struct wiphy *wiphy = wdev->wiphy; |
14667 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
14668 | struct nlattr *match_attr, *local_func_attr, *peer_func_attr; |
14669 | struct sk_buff *msg; |
14670 | void *hdr; |
14671 | |
14672 | if (WARN_ON(!match->inst_id || !match->peer_inst_id || !match->addr)) |
14673 | return; |
14674 | |
14675 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); |
14676 | if (!msg) |
14677 | return; |
14678 | |
14679 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_NAN_MATCH); |
14680 | if (!hdr) { |
14681 | nlmsg_free(skb: msg); |
14682 | return; |
14683 | } |
14684 | |
14685 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
14686 | (wdev->netdev && nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, |
14687 | value: wdev->netdev->ifindex)) || |
14688 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), |
14689 | padattr: NL80211_ATTR_PAD)) |
14690 | goto nla_put_failure; |
14691 | |
14692 | if (nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: match->cookie, |
14693 | padattr: NL80211_ATTR_PAD) || |
14694 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: match->addr)) |
14695 | goto nla_put_failure; |
14696 | |
14697 | match_attr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_NAN_MATCH); |
14698 | if (!match_attr) |
14699 | goto nla_put_failure; |
14700 | |
14701 | local_func_attr = nla_nest_start_noflag(skb: msg, |
14702 | attrtype: NL80211_NAN_MATCH_FUNC_LOCAL); |
14703 | if (!local_func_attr) |
14704 | goto nla_put_failure; |
14705 | |
14706 | if (nla_put_u8(skb: msg, attrtype: NL80211_NAN_FUNC_INSTANCE_ID, value: match->inst_id)) |
14707 | goto nla_put_failure; |
14708 | |
14709 | nla_nest_end(skb: msg, start: local_func_attr); |
14710 | |
14711 | peer_func_attr = nla_nest_start_noflag(skb: msg, |
14712 | attrtype: NL80211_NAN_MATCH_FUNC_PEER); |
14713 | if (!peer_func_attr) |
14714 | goto nla_put_failure; |
14715 | |
14716 | if (nla_put_u8(skb: msg, attrtype: NL80211_NAN_FUNC_TYPE, value: match->type) || |
14717 | nla_put_u8(skb: msg, attrtype: NL80211_NAN_FUNC_INSTANCE_ID, value: match->peer_inst_id)) |
14718 | goto nla_put_failure; |
14719 | |
14720 | if (match->info && match->info_len && |
14721 | nla_put(skb: msg, attrtype: NL80211_NAN_FUNC_SERVICE_INFO, attrlen: match->info_len, |
14722 | data: match->info)) |
14723 | goto nla_put_failure; |
14724 | |
14725 | nla_nest_end(skb: msg, start: peer_func_attr); |
14726 | nla_nest_end(skb: msg, start: match_attr); |
14727 | genlmsg_end(skb: msg, hdr); |
14728 | |
14729 | if (!wdev->owner_nlportid) |
14730 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), |
14731 | skb: msg, portid: 0, group: NL80211_MCGRP_NAN, flags: gfp); |
14732 | else |
14733 | genlmsg_unicast(net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, |
14734 | portid: wdev->owner_nlportid); |
14735 | |
14736 | return; |
14737 | |
14738 | nla_put_failure: |
14739 | nlmsg_free(skb: msg); |
14740 | } |
14741 | EXPORT_SYMBOL(cfg80211_nan_match); |
14742 | |
14743 | void cfg80211_nan_func_terminated(struct wireless_dev *wdev, |
14744 | u8 inst_id, |
14745 | enum nl80211_nan_func_term_reason reason, |
14746 | u64 cookie, gfp_t gfp) |
14747 | { |
14748 | struct wiphy *wiphy = wdev->wiphy; |
14749 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
14750 | struct sk_buff *msg; |
14751 | struct nlattr *func_attr; |
14752 | void *hdr; |
14753 | |
14754 | if (WARN_ON(!inst_id)) |
14755 | return; |
14756 | |
14757 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); |
14758 | if (!msg) |
14759 | return; |
14760 | |
14761 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_DEL_NAN_FUNCTION); |
14762 | if (!hdr) { |
14763 | nlmsg_free(skb: msg); |
14764 | return; |
14765 | } |
14766 | |
14767 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
14768 | (wdev->netdev && nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, |
14769 | value: wdev->netdev->ifindex)) || |
14770 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), |
14771 | padattr: NL80211_ATTR_PAD)) |
14772 | goto nla_put_failure; |
14773 | |
14774 | if (nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: cookie, |
14775 | padattr: NL80211_ATTR_PAD)) |
14776 | goto nla_put_failure; |
14777 | |
14778 | func_attr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_NAN_FUNC); |
14779 | if (!func_attr) |
14780 | goto nla_put_failure; |
14781 | |
14782 | if (nla_put_u8(skb: msg, attrtype: NL80211_NAN_FUNC_INSTANCE_ID, value: inst_id) || |
14783 | nla_put_u8(skb: msg, attrtype: NL80211_NAN_FUNC_TERM_REASON, value: reason)) |
14784 | goto nla_put_failure; |
14785 | |
14786 | nla_nest_end(skb: msg, start: func_attr); |
14787 | genlmsg_end(skb: msg, hdr); |
14788 | |
14789 | if (!wdev->owner_nlportid) |
14790 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), |
14791 | skb: msg, portid: 0, group: NL80211_MCGRP_NAN, flags: gfp); |
14792 | else |
14793 | genlmsg_unicast(net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, |
14794 | portid: wdev->owner_nlportid); |
14795 | |
14796 | return; |
14797 | |
14798 | nla_put_failure: |
14799 | nlmsg_free(skb: msg); |
14800 | } |
14801 | EXPORT_SYMBOL(cfg80211_nan_func_terminated); |
14802 | |
14803 | static int nl80211_get_protocol_features(struct sk_buff *skb, |
14804 | struct genl_info *info) |
14805 | { |
14806 | void *hdr; |
14807 | struct sk_buff *msg; |
14808 | |
14809 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
14810 | if (!msg) |
14811 | return -ENOMEM; |
14812 | |
14813 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
14814 | cmd: NL80211_CMD_GET_PROTOCOL_FEATURES); |
14815 | if (!hdr) |
14816 | goto nla_put_failure; |
14817 | |
14818 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_PROTOCOL_FEATURES, |
14819 | value: NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)) |
14820 | goto nla_put_failure; |
14821 | |
14822 | genlmsg_end(skb: msg, hdr); |
14823 | return genlmsg_reply(skb: msg, info); |
14824 | |
14825 | nla_put_failure: |
14826 | kfree_skb(skb: msg); |
14827 | return -ENOBUFS; |
14828 | } |
14829 | |
14830 | static int nl80211_update_ft_ies(struct sk_buff *skb, struct genl_info *info) |
14831 | { |
14832 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
14833 | struct cfg80211_update_ft_ies_params ft_params; |
14834 | struct net_device *dev = info->user_ptr[1]; |
14835 | |
14836 | if (!rdev->ops->update_ft_ies) |
14837 | return -EOPNOTSUPP; |
14838 | |
14839 | if (!info->attrs[NL80211_ATTR_MDID] || |
14840 | !info->attrs[NL80211_ATTR_IE]) |
14841 | return -EINVAL; |
14842 | |
14843 | memset(&ft_params, 0, sizeof(ft_params)); |
14844 | ft_params.md = nla_get_u16(nla: info->attrs[NL80211_ATTR_MDID]); |
14845 | ft_params.ie = nla_data(nla: info->attrs[NL80211_ATTR_IE]); |
14846 | ft_params.ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); |
14847 | |
14848 | return rdev_update_ft_ies(rdev, dev, ftie: &ft_params); |
14849 | } |
14850 | |
14851 | static int nl80211_crit_protocol_start(struct sk_buff *skb, |
14852 | struct genl_info *info) |
14853 | { |
14854 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
14855 | struct wireless_dev *wdev = info->user_ptr[1]; |
14856 | enum nl80211_crit_proto_id proto = NL80211_CRIT_PROTO_UNSPEC; |
14857 | u16 duration; |
14858 | int ret; |
14859 | |
14860 | if (!rdev->ops->crit_proto_start) |
14861 | return -EOPNOTSUPP; |
14862 | |
14863 | if (WARN_ON(!rdev->ops->crit_proto_stop)) |
14864 | return -EINVAL; |
14865 | |
14866 | if (rdev->crit_proto_nlportid) |
14867 | return -EBUSY; |
14868 | |
14869 | /* determine protocol if provided */ |
14870 | if (info->attrs[NL80211_ATTR_CRIT_PROT_ID]) |
14871 | proto = nla_get_u16(nla: info->attrs[NL80211_ATTR_CRIT_PROT_ID]); |
14872 | |
14873 | if (proto >= NUM_NL80211_CRIT_PROTO) |
14874 | return -EINVAL; |
14875 | |
14876 | /* timeout must be provided */ |
14877 | if (!info->attrs[NL80211_ATTR_MAX_CRIT_PROT_DURATION]) |
14878 | return -EINVAL; |
14879 | |
14880 | duration = |
14881 | nla_get_u16(nla: info->attrs[NL80211_ATTR_MAX_CRIT_PROT_DURATION]); |
14882 | |
14883 | ret = rdev_crit_proto_start(rdev, wdev, protocol: proto, duration); |
14884 | if (!ret) |
14885 | rdev->crit_proto_nlportid = info->snd_portid; |
14886 | |
14887 | return ret; |
14888 | } |
14889 | |
14890 | static int nl80211_crit_protocol_stop(struct sk_buff *skb, |
14891 | struct genl_info *info) |
14892 | { |
14893 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
14894 | struct wireless_dev *wdev = info->user_ptr[1]; |
14895 | |
14896 | if (!rdev->ops->crit_proto_stop) |
14897 | return -EOPNOTSUPP; |
14898 | |
14899 | if (rdev->crit_proto_nlportid) { |
14900 | rdev->crit_proto_nlportid = 0; |
14901 | rdev_crit_proto_stop(rdev, wdev); |
14902 | } |
14903 | return 0; |
14904 | } |
14905 | |
14906 | static int nl80211_vendor_check_policy(const struct wiphy_vendor_command *vcmd, |
14907 | struct nlattr *attr, |
14908 | struct netlink_ext_ack *extack) |
14909 | { |
14910 | if (vcmd->policy == VENDOR_CMD_RAW_DATA) { |
14911 | if (attr->nla_type & NLA_F_NESTED) { |
14912 | NL_SET_ERR_MSG_ATTR(extack, attr, |
14913 | "unexpected nested data" ); |
14914 | return -EINVAL; |
14915 | } |
14916 | |
14917 | return 0; |
14918 | } |
14919 | |
14920 | if (!(attr->nla_type & NLA_F_NESTED)) { |
14921 | NL_SET_ERR_MSG_ATTR(extack, attr, "expected nested data" ); |
14922 | return -EINVAL; |
14923 | } |
14924 | |
14925 | return nla_validate_nested(start: attr, maxtype: vcmd->maxattr, policy: vcmd->policy, extack); |
14926 | } |
14927 | |
14928 | static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info) |
14929 | { |
14930 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
14931 | struct wireless_dev *wdev = |
14932 | __cfg80211_wdev_from_attrs(rdev, netns: genl_info_net(info), |
14933 | attrs: info->attrs); |
14934 | int i, err; |
14935 | u32 vid, subcmd; |
14936 | |
14937 | if (!rdev->wiphy.vendor_commands) |
14938 | return -EOPNOTSUPP; |
14939 | |
14940 | if (IS_ERR(ptr: wdev)) { |
14941 | err = PTR_ERR(ptr: wdev); |
14942 | if (err != -EINVAL) |
14943 | return err; |
14944 | wdev = NULL; |
14945 | } else if (wdev->wiphy != &rdev->wiphy) { |
14946 | return -EINVAL; |
14947 | } |
14948 | |
14949 | if (!info->attrs[NL80211_ATTR_VENDOR_ID] || |
14950 | !info->attrs[NL80211_ATTR_VENDOR_SUBCMD]) |
14951 | return -EINVAL; |
14952 | |
14953 | vid = nla_get_u32(nla: info->attrs[NL80211_ATTR_VENDOR_ID]); |
14954 | subcmd = nla_get_u32(nla: info->attrs[NL80211_ATTR_VENDOR_SUBCMD]); |
14955 | for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) { |
14956 | const struct wiphy_vendor_command *vcmd; |
14957 | void *data = NULL; |
14958 | int len = 0; |
14959 | |
14960 | vcmd = &rdev->wiphy.vendor_commands[i]; |
14961 | |
14962 | if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd) |
14963 | continue; |
14964 | |
14965 | if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV | |
14966 | WIPHY_VENDOR_CMD_NEED_NETDEV)) { |
14967 | if (!wdev) |
14968 | return -EINVAL; |
14969 | if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV && |
14970 | !wdev->netdev) |
14971 | return -EINVAL; |
14972 | |
14973 | if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) { |
14974 | if (!wdev_running(wdev)) |
14975 | return -ENETDOWN; |
14976 | } |
14977 | } else { |
14978 | wdev = NULL; |
14979 | } |
14980 | |
14981 | if (!vcmd->doit) |
14982 | return -EOPNOTSUPP; |
14983 | |
14984 | if (info->attrs[NL80211_ATTR_VENDOR_DATA]) { |
14985 | data = nla_data(nla: info->attrs[NL80211_ATTR_VENDOR_DATA]); |
14986 | len = nla_len(nla: info->attrs[NL80211_ATTR_VENDOR_DATA]); |
14987 | |
14988 | err = nl80211_vendor_check_policy(vcmd, |
14989 | attr: info->attrs[NL80211_ATTR_VENDOR_DATA], |
14990 | extack: info->extack); |
14991 | if (err) |
14992 | return err; |
14993 | } |
14994 | |
14995 | rdev->cur_cmd_info = info; |
14996 | err = vcmd->doit(&rdev->wiphy, wdev, data, len); |
14997 | rdev->cur_cmd_info = NULL; |
14998 | return err; |
14999 | } |
15000 | |
15001 | return -EOPNOTSUPP; |
15002 | } |
15003 | |
15004 | static int nl80211_prepare_vendor_dump(struct sk_buff *skb, |
15005 | struct netlink_callback *cb, |
15006 | struct cfg80211_registered_device **rdev, |
15007 | struct wireless_dev **wdev) |
15008 | { |
15009 | struct nlattr **attrbuf; |
15010 | u32 vid, subcmd; |
15011 | unsigned int i; |
15012 | int vcmd_idx = -1; |
15013 | int err; |
15014 | void *data = NULL; |
15015 | unsigned int data_len = 0; |
15016 | |
15017 | if (cb->args[0]) { |
15018 | /* subtract the 1 again here */ |
15019 | struct wiphy *wiphy = wiphy_idx_to_wiphy(wiphy_idx: cb->args[0] - 1); |
15020 | struct wireless_dev *tmp; |
15021 | |
15022 | if (!wiphy) |
15023 | return -ENODEV; |
15024 | *rdev = wiphy_to_rdev(wiphy); |
15025 | *wdev = NULL; |
15026 | |
15027 | if (cb->args[1]) { |
15028 | list_for_each_entry(tmp, &wiphy->wdev_list, list) { |
15029 | if (tmp->identifier == cb->args[1] - 1) { |
15030 | *wdev = tmp; |
15031 | break; |
15032 | } |
15033 | } |
15034 | } |
15035 | |
15036 | /* keep rtnl locked in successful case */ |
15037 | return 0; |
15038 | } |
15039 | |
15040 | attrbuf = kcalloc(n: NUM_NL80211_ATTR, size: sizeof(*attrbuf), GFP_KERNEL); |
15041 | if (!attrbuf) |
15042 | return -ENOMEM; |
15043 | |
15044 | err = nlmsg_parse_deprecated(nlh: cb->nlh, |
15045 | GENL_HDRLEN + nl80211_fam.hdrsize, |
15046 | tb: attrbuf, maxtype: nl80211_fam.maxattr, |
15047 | policy: nl80211_policy, NULL); |
15048 | if (err) |
15049 | goto out; |
15050 | |
15051 | if (!attrbuf[NL80211_ATTR_VENDOR_ID] || |
15052 | !attrbuf[NL80211_ATTR_VENDOR_SUBCMD]) { |
15053 | err = -EINVAL; |
15054 | goto out; |
15055 | } |
15056 | |
15057 | *wdev = __cfg80211_wdev_from_attrs(NULL, netns: sock_net(sk: skb->sk), attrs: attrbuf); |
15058 | if (IS_ERR(ptr: *wdev)) |
15059 | *wdev = NULL; |
15060 | |
15061 | *rdev = __cfg80211_rdev_from_attrs(netns: sock_net(sk: skb->sk), attrs: attrbuf); |
15062 | if (IS_ERR(ptr: *rdev)) { |
15063 | err = PTR_ERR(ptr: *rdev); |
15064 | goto out; |
15065 | } |
15066 | |
15067 | vid = nla_get_u32(nla: attrbuf[NL80211_ATTR_VENDOR_ID]); |
15068 | subcmd = nla_get_u32(nla: attrbuf[NL80211_ATTR_VENDOR_SUBCMD]); |
15069 | |
15070 | for (i = 0; i < (*rdev)->wiphy.n_vendor_commands; i++) { |
15071 | const struct wiphy_vendor_command *vcmd; |
15072 | |
15073 | vcmd = &(*rdev)->wiphy.vendor_commands[i]; |
15074 | |
15075 | if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd) |
15076 | continue; |
15077 | |
15078 | if (!vcmd->dumpit) { |
15079 | err = -EOPNOTSUPP; |
15080 | goto out; |
15081 | } |
15082 | |
15083 | vcmd_idx = i; |
15084 | break; |
15085 | } |
15086 | |
15087 | if (vcmd_idx < 0) { |
15088 | err = -EOPNOTSUPP; |
15089 | goto out; |
15090 | } |
15091 | |
15092 | if (attrbuf[NL80211_ATTR_VENDOR_DATA]) { |
15093 | data = nla_data(nla: attrbuf[NL80211_ATTR_VENDOR_DATA]); |
15094 | data_len = nla_len(nla: attrbuf[NL80211_ATTR_VENDOR_DATA]); |
15095 | |
15096 | err = nl80211_vendor_check_policy( |
15097 | vcmd: &(*rdev)->wiphy.vendor_commands[vcmd_idx], |
15098 | attr: attrbuf[NL80211_ATTR_VENDOR_DATA], |
15099 | extack: cb->extack); |
15100 | if (err) |
15101 | goto out; |
15102 | } |
15103 | |
15104 | /* 0 is the first index - add 1 to parse only once */ |
15105 | cb->args[0] = (*rdev)->wiphy_idx + 1; |
15106 | /* add 1 to know if it was NULL */ |
15107 | cb->args[1] = *wdev ? (*wdev)->identifier + 1 : 0; |
15108 | cb->args[2] = vcmd_idx; |
15109 | cb->args[3] = (unsigned long)data; |
15110 | cb->args[4] = data_len; |
15111 | |
15112 | /* keep rtnl locked in successful case */ |
15113 | err = 0; |
15114 | out: |
15115 | kfree(objp: attrbuf); |
15116 | return err; |
15117 | } |
15118 | |
15119 | static int nl80211_vendor_cmd_dump(struct sk_buff *skb, |
15120 | struct netlink_callback *cb) |
15121 | { |
15122 | struct cfg80211_registered_device *rdev; |
15123 | struct wireless_dev *wdev; |
15124 | unsigned int vcmd_idx; |
15125 | const struct wiphy_vendor_command *vcmd; |
15126 | void *data; |
15127 | int data_len; |
15128 | int err; |
15129 | struct nlattr *vendor_data; |
15130 | |
15131 | rtnl_lock(); |
15132 | err = nl80211_prepare_vendor_dump(skb, cb, rdev: &rdev, wdev: &wdev); |
15133 | if (err) |
15134 | goto out; |
15135 | |
15136 | vcmd_idx = cb->args[2]; |
15137 | data = (void *)cb->args[3]; |
15138 | data_len = cb->args[4]; |
15139 | vcmd = &rdev->wiphy.vendor_commands[vcmd_idx]; |
15140 | |
15141 | if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV | |
15142 | WIPHY_VENDOR_CMD_NEED_NETDEV)) { |
15143 | if (!wdev) { |
15144 | err = -EINVAL; |
15145 | goto out; |
15146 | } |
15147 | if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV && |
15148 | !wdev->netdev) { |
15149 | err = -EINVAL; |
15150 | goto out; |
15151 | } |
15152 | |
15153 | if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) { |
15154 | if (!wdev_running(wdev)) { |
15155 | err = -ENETDOWN; |
15156 | goto out; |
15157 | } |
15158 | } |
15159 | } |
15160 | |
15161 | while (1) { |
15162 | void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).portid, |
15163 | seq: cb->nlh->nlmsg_seq, NLM_F_MULTI, |
15164 | cmd: NL80211_CMD_VENDOR); |
15165 | if (!hdr) |
15166 | break; |
15167 | |
15168 | if (nla_put_u32(skb, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
15169 | (wdev && nla_put_u64_64bit(skb, attrtype: NL80211_ATTR_WDEV, |
15170 | value: wdev_id(wdev), |
15171 | padattr: NL80211_ATTR_PAD))) { |
15172 | genlmsg_cancel(skb, hdr); |
15173 | break; |
15174 | } |
15175 | |
15176 | vendor_data = nla_nest_start_noflag(skb, |
15177 | attrtype: NL80211_ATTR_VENDOR_DATA); |
15178 | if (!vendor_data) { |
15179 | genlmsg_cancel(skb, hdr); |
15180 | break; |
15181 | } |
15182 | |
15183 | err = vcmd->dumpit(&rdev->wiphy, wdev, skb, data, data_len, |
15184 | (unsigned long *)&cb->args[5]); |
15185 | nla_nest_end(skb, start: vendor_data); |
15186 | |
15187 | if (err == -ENOBUFS || err == -ENOENT) { |
15188 | genlmsg_cancel(skb, hdr); |
15189 | break; |
15190 | } else if (err <= 0) { |
15191 | genlmsg_cancel(skb, hdr); |
15192 | goto out; |
15193 | } |
15194 | |
15195 | genlmsg_end(skb, hdr); |
15196 | } |
15197 | |
15198 | err = skb->len; |
15199 | out: |
15200 | rtnl_unlock(); |
15201 | return err; |
15202 | } |
15203 | |
15204 | struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy, |
15205 | enum nl80211_commands cmd, |
15206 | enum nl80211_attrs attr, |
15207 | int approxlen) |
15208 | { |
15209 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
15210 | |
15211 | if (WARN_ON(!rdev->cur_cmd_info)) |
15212 | return NULL; |
15213 | |
15214 | return __cfg80211_alloc_vendor_skb(rdev, NULL, approxlen, |
15215 | portid: rdev->cur_cmd_info->snd_portid, |
15216 | seq: rdev->cur_cmd_info->snd_seq, |
15217 | cmd, attr, NULL, GFP_KERNEL); |
15218 | } |
15219 | EXPORT_SYMBOL(__cfg80211_alloc_reply_skb); |
15220 | |
15221 | int cfg80211_vendor_cmd_reply(struct sk_buff *skb) |
15222 | { |
15223 | struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; |
15224 | void *hdr = ((void **)skb->cb)[1]; |
15225 | struct nlattr *data = ((void **)skb->cb)[2]; |
15226 | |
15227 | /* clear CB data for netlink core to own from now on */ |
15228 | memset(skb->cb, 0, sizeof(skb->cb)); |
15229 | |
15230 | if (WARN_ON(!rdev->cur_cmd_info)) { |
15231 | kfree_skb(skb); |
15232 | return -EINVAL; |
15233 | } |
15234 | |
15235 | nla_nest_end(skb, start: data); |
15236 | genlmsg_end(skb, hdr); |
15237 | return genlmsg_reply(skb, info: rdev->cur_cmd_info); |
15238 | } |
15239 | EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_reply); |
15240 | |
15241 | unsigned int cfg80211_vendor_cmd_get_sender(struct wiphy *wiphy) |
15242 | { |
15243 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
15244 | |
15245 | if (WARN_ON(!rdev->cur_cmd_info)) |
15246 | return 0; |
15247 | |
15248 | return rdev->cur_cmd_info->snd_portid; |
15249 | } |
15250 | EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_get_sender); |
15251 | |
15252 | static int nl80211_set_qos_map(struct sk_buff *skb, |
15253 | struct genl_info *info) |
15254 | { |
15255 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
15256 | struct cfg80211_qos_map *qos_map = NULL; |
15257 | struct net_device *dev = info->user_ptr[1]; |
15258 | u8 *pos, len, num_des, des_len, des; |
15259 | int ret; |
15260 | |
15261 | if (!rdev->ops->set_qos_map) |
15262 | return -EOPNOTSUPP; |
15263 | |
15264 | if (info->attrs[NL80211_ATTR_QOS_MAP]) { |
15265 | pos = nla_data(nla: info->attrs[NL80211_ATTR_QOS_MAP]); |
15266 | len = nla_len(nla: info->attrs[NL80211_ATTR_QOS_MAP]); |
15267 | |
15268 | if (len % 2) |
15269 | return -EINVAL; |
15270 | |
15271 | qos_map = kzalloc(size: sizeof(struct cfg80211_qos_map), GFP_KERNEL); |
15272 | if (!qos_map) |
15273 | return -ENOMEM; |
15274 | |
15275 | num_des = (len - IEEE80211_QOS_MAP_LEN_MIN) >> 1; |
15276 | if (num_des) { |
15277 | des_len = num_des * |
15278 | sizeof(struct cfg80211_dscp_exception); |
15279 | memcpy(qos_map->dscp_exception, pos, des_len); |
15280 | qos_map->num_des = num_des; |
15281 | for (des = 0; des < num_des; des++) { |
15282 | if (qos_map->dscp_exception[des].up > 7) { |
15283 | kfree(objp: qos_map); |
15284 | return -EINVAL; |
15285 | } |
15286 | } |
15287 | pos += des_len; |
15288 | } |
15289 | memcpy(qos_map->up, pos, IEEE80211_QOS_MAP_LEN_MIN); |
15290 | } |
15291 | |
15292 | ret = nl80211_key_allowed(wdev: dev->ieee80211_ptr); |
15293 | if (!ret) |
15294 | ret = rdev_set_qos_map(rdev, dev, qos_map); |
15295 | |
15296 | kfree(objp: qos_map); |
15297 | return ret; |
15298 | } |
15299 | |
15300 | static int nl80211_add_tx_ts(struct sk_buff *skb, struct genl_info *info) |
15301 | { |
15302 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
15303 | struct net_device *dev = info->user_ptr[1]; |
15304 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
15305 | const u8 *peer; |
15306 | u8 tsid, up; |
15307 | u16 admitted_time = 0; |
15308 | |
15309 | if (!(rdev->wiphy.features & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)) |
15310 | return -EOPNOTSUPP; |
15311 | |
15312 | if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC] || |
15313 | !info->attrs[NL80211_ATTR_USER_PRIO]) |
15314 | return -EINVAL; |
15315 | |
15316 | tsid = nla_get_u8(nla: info->attrs[NL80211_ATTR_TSID]); |
15317 | up = nla_get_u8(nla: info->attrs[NL80211_ATTR_USER_PRIO]); |
15318 | |
15319 | /* WMM uses TIDs 0-7 even for TSPEC */ |
15320 | if (tsid >= IEEE80211_FIRST_TSPEC_TSID) { |
15321 | /* TODO: handle 802.11 TSPEC/admission control |
15322 | * need more attributes for that (e.g. BA session requirement); |
15323 | * change the WMM adminssion test above to allow both then |
15324 | */ |
15325 | return -EINVAL; |
15326 | } |
15327 | |
15328 | peer = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
15329 | |
15330 | if (info->attrs[NL80211_ATTR_ADMITTED_TIME]) { |
15331 | admitted_time = |
15332 | nla_get_u16(nla: info->attrs[NL80211_ATTR_ADMITTED_TIME]); |
15333 | if (!admitted_time) |
15334 | return -EINVAL; |
15335 | } |
15336 | |
15337 | switch (wdev->iftype) { |
15338 | case NL80211_IFTYPE_STATION: |
15339 | case NL80211_IFTYPE_P2P_CLIENT: |
15340 | if (wdev->connected) |
15341 | break; |
15342 | return -ENOTCONN; |
15343 | default: |
15344 | return -EOPNOTSUPP; |
15345 | } |
15346 | |
15347 | return rdev_add_tx_ts(rdev, dev, tsid, peer, user_prio: up, admitted_time); |
15348 | } |
15349 | |
15350 | static int nl80211_del_tx_ts(struct sk_buff *skb, struct genl_info *info) |
15351 | { |
15352 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
15353 | struct net_device *dev = info->user_ptr[1]; |
15354 | const u8 *peer; |
15355 | u8 tsid; |
15356 | |
15357 | if (!info->attrs[NL80211_ATTR_TSID] || !info->attrs[NL80211_ATTR_MAC]) |
15358 | return -EINVAL; |
15359 | |
15360 | tsid = nla_get_u8(nla: info->attrs[NL80211_ATTR_TSID]); |
15361 | peer = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
15362 | |
15363 | return rdev_del_tx_ts(rdev, dev, tsid, peer); |
15364 | } |
15365 | |
15366 | static int nl80211_tdls_channel_switch(struct sk_buff *skb, |
15367 | struct genl_info *info) |
15368 | { |
15369 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
15370 | struct net_device *dev = info->user_ptr[1]; |
15371 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
15372 | struct cfg80211_chan_def chandef = {}; |
15373 | const u8 *addr; |
15374 | u8 oper_class; |
15375 | int err; |
15376 | |
15377 | if (!rdev->ops->tdls_channel_switch || |
15378 | !(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH)) |
15379 | return -EOPNOTSUPP; |
15380 | |
15381 | switch (dev->ieee80211_ptr->iftype) { |
15382 | case NL80211_IFTYPE_STATION: |
15383 | case NL80211_IFTYPE_P2P_CLIENT: |
15384 | break; |
15385 | default: |
15386 | return -EOPNOTSUPP; |
15387 | } |
15388 | |
15389 | if (!info->attrs[NL80211_ATTR_MAC] || |
15390 | !info->attrs[NL80211_ATTR_OPER_CLASS]) |
15391 | return -EINVAL; |
15392 | |
15393 | err = nl80211_parse_chandef(rdev, info, chandef: &chandef); |
15394 | if (err) |
15395 | return err; |
15396 | |
15397 | /* |
15398 | * Don't allow wide channels on the 2.4Ghz band, as per IEEE802.11-2012 |
15399 | * section 10.22.6.2.1. Disallow 5/10Mhz channels as well for now, the |
15400 | * specification is not defined for them. |
15401 | */ |
15402 | if (chandef.chan->band == NL80211_BAND_2GHZ && |
15403 | chandef.width != NL80211_CHAN_WIDTH_20_NOHT && |
15404 | chandef.width != NL80211_CHAN_WIDTH_20) |
15405 | return -EINVAL; |
15406 | |
15407 | /* we will be active on the TDLS link */ |
15408 | if (!cfg80211_reg_can_beacon_relax(wiphy: &rdev->wiphy, chandef: &chandef, |
15409 | iftype: wdev->iftype)) |
15410 | return -EINVAL; |
15411 | |
15412 | /* don't allow switching to DFS channels */ |
15413 | if (cfg80211_chandef_dfs_required(wiphy: wdev->wiphy, chandef: &chandef, iftype: wdev->iftype)) |
15414 | return -EINVAL; |
15415 | |
15416 | addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
15417 | oper_class = nla_get_u8(nla: info->attrs[NL80211_ATTR_OPER_CLASS]); |
15418 | |
15419 | return rdev_tdls_channel_switch(rdev, dev, addr, oper_class, chandef: &chandef); |
15420 | } |
15421 | |
15422 | static int nl80211_tdls_cancel_channel_switch(struct sk_buff *skb, |
15423 | struct genl_info *info) |
15424 | { |
15425 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
15426 | struct net_device *dev = info->user_ptr[1]; |
15427 | const u8 *addr; |
15428 | |
15429 | if (!rdev->ops->tdls_channel_switch || |
15430 | !rdev->ops->tdls_cancel_channel_switch || |
15431 | !(rdev->wiphy.features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH)) |
15432 | return -EOPNOTSUPP; |
15433 | |
15434 | switch (dev->ieee80211_ptr->iftype) { |
15435 | case NL80211_IFTYPE_STATION: |
15436 | case NL80211_IFTYPE_P2P_CLIENT: |
15437 | break; |
15438 | default: |
15439 | return -EOPNOTSUPP; |
15440 | } |
15441 | |
15442 | if (!info->attrs[NL80211_ATTR_MAC]) |
15443 | return -EINVAL; |
15444 | |
15445 | addr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
15446 | |
15447 | rdev_tdls_cancel_channel_switch(rdev, dev, addr); |
15448 | |
15449 | return 0; |
15450 | } |
15451 | |
15452 | static int nl80211_set_multicast_to_unicast(struct sk_buff *skb, |
15453 | struct genl_info *info) |
15454 | { |
15455 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
15456 | struct net_device *dev = info->user_ptr[1]; |
15457 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
15458 | const struct nlattr *nla; |
15459 | bool enabled; |
15460 | |
15461 | if (!rdev->ops->set_multicast_to_unicast) |
15462 | return -EOPNOTSUPP; |
15463 | |
15464 | if (wdev->iftype != NL80211_IFTYPE_AP && |
15465 | wdev->iftype != NL80211_IFTYPE_P2P_GO) |
15466 | return -EOPNOTSUPP; |
15467 | |
15468 | nla = info->attrs[NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED]; |
15469 | enabled = nla_get_flag(nla); |
15470 | |
15471 | return rdev_set_multicast_to_unicast(rdev, dev, enabled); |
15472 | } |
15473 | |
15474 | static int nl80211_set_pmk(struct sk_buff *skb, struct genl_info *info) |
15475 | { |
15476 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
15477 | struct net_device *dev = info->user_ptr[1]; |
15478 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
15479 | struct cfg80211_pmk_conf pmk_conf = {}; |
15480 | |
15481 | if (wdev->iftype != NL80211_IFTYPE_STATION && |
15482 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) |
15483 | return -EOPNOTSUPP; |
15484 | |
15485 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
15486 | ftidx: NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X)) |
15487 | return -EOPNOTSUPP; |
15488 | |
15489 | if (!info->attrs[NL80211_ATTR_MAC] || !info->attrs[NL80211_ATTR_PMK]) |
15490 | return -EINVAL; |
15491 | |
15492 | if (!wdev->connected) |
15493 | return -ENOTCONN; |
15494 | |
15495 | pmk_conf.aa = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
15496 | if (memcmp(p: pmk_conf.aa, q: wdev->u.client.connected_addr, ETH_ALEN)) |
15497 | return -EINVAL; |
15498 | |
15499 | pmk_conf.pmk = nla_data(nla: info->attrs[NL80211_ATTR_PMK]); |
15500 | pmk_conf.pmk_len = nla_len(nla: info->attrs[NL80211_ATTR_PMK]); |
15501 | if (pmk_conf.pmk_len != WLAN_PMK_LEN && |
15502 | pmk_conf.pmk_len != WLAN_PMK_LEN_SUITE_B_192) |
15503 | return -EINVAL; |
15504 | |
15505 | if (info->attrs[NL80211_ATTR_PMKR0_NAME]) |
15506 | pmk_conf.pmk_r0_name = |
15507 | nla_data(nla: info->attrs[NL80211_ATTR_PMKR0_NAME]); |
15508 | |
15509 | return rdev_set_pmk(rdev, dev, pmk_conf: &pmk_conf); |
15510 | } |
15511 | |
15512 | static int nl80211_del_pmk(struct sk_buff *skb, struct genl_info *info) |
15513 | { |
15514 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
15515 | struct net_device *dev = info->user_ptr[1]; |
15516 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
15517 | const u8 *aa; |
15518 | |
15519 | if (wdev->iftype != NL80211_IFTYPE_STATION && |
15520 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) |
15521 | return -EOPNOTSUPP; |
15522 | |
15523 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
15524 | ftidx: NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X)) |
15525 | return -EOPNOTSUPP; |
15526 | |
15527 | if (!info->attrs[NL80211_ATTR_MAC]) |
15528 | return -EINVAL; |
15529 | |
15530 | aa = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
15531 | return rdev_del_pmk(rdev, dev, aa); |
15532 | } |
15533 | |
15534 | static int nl80211_external_auth(struct sk_buff *skb, struct genl_info *info) |
15535 | { |
15536 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
15537 | struct net_device *dev = info->user_ptr[1]; |
15538 | struct cfg80211_external_auth_params params; |
15539 | |
15540 | if (!rdev->ops->external_auth) |
15541 | return -EOPNOTSUPP; |
15542 | |
15543 | if (!info->attrs[NL80211_ATTR_SSID] && |
15544 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && |
15545 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) |
15546 | return -EINVAL; |
15547 | |
15548 | if (!info->attrs[NL80211_ATTR_BSSID]) |
15549 | return -EINVAL; |
15550 | |
15551 | if (!info->attrs[NL80211_ATTR_STATUS_CODE]) |
15552 | return -EINVAL; |
15553 | |
15554 | memset(¶ms, 0, sizeof(params)); |
15555 | |
15556 | if (info->attrs[NL80211_ATTR_SSID]) { |
15557 | params.ssid.ssid_len = nla_len(nla: info->attrs[NL80211_ATTR_SSID]); |
15558 | if (params.ssid.ssid_len == 0) |
15559 | return -EINVAL; |
15560 | memcpy(params.ssid.ssid, |
15561 | nla_data(info->attrs[NL80211_ATTR_SSID]), |
15562 | params.ssid.ssid_len); |
15563 | } |
15564 | |
15565 | memcpy(params.bssid, nla_data(info->attrs[NL80211_ATTR_BSSID]), |
15566 | ETH_ALEN); |
15567 | |
15568 | params.status = nla_get_u16(nla: info->attrs[NL80211_ATTR_STATUS_CODE]); |
15569 | |
15570 | if (info->attrs[NL80211_ATTR_PMKID]) |
15571 | params.pmkid = nla_data(nla: info->attrs[NL80211_ATTR_PMKID]); |
15572 | |
15573 | return rdev_external_auth(rdev, dev, params: ¶ms); |
15574 | } |
15575 | |
15576 | static int nl80211_tx_control_port(struct sk_buff *skb, struct genl_info *info) |
15577 | { |
15578 | bool dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK]; |
15579 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
15580 | struct net_device *dev = info->user_ptr[1]; |
15581 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
15582 | const u8 *buf; |
15583 | size_t len; |
15584 | u8 *dest; |
15585 | u16 proto; |
15586 | bool noencrypt; |
15587 | u64 cookie = 0; |
15588 | int link_id; |
15589 | int err; |
15590 | |
15591 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
15592 | ftidx: NL80211_EXT_FEATURE_CONTROL_PORT_OVER_NL80211)) |
15593 | return -EOPNOTSUPP; |
15594 | |
15595 | if (!rdev->ops->tx_control_port) |
15596 | return -EOPNOTSUPP; |
15597 | |
15598 | if (!info->attrs[NL80211_ATTR_FRAME] || |
15599 | !info->attrs[NL80211_ATTR_MAC] || |
15600 | !info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) { |
15601 | GENL_SET_ERR_MSG(info, "Frame, MAC or ethertype missing" ); |
15602 | return -EINVAL; |
15603 | } |
15604 | |
15605 | switch (wdev->iftype) { |
15606 | case NL80211_IFTYPE_AP: |
15607 | case NL80211_IFTYPE_P2P_GO: |
15608 | case NL80211_IFTYPE_MESH_POINT: |
15609 | break; |
15610 | case NL80211_IFTYPE_ADHOC: |
15611 | if (wdev->u.ibss.current_bss) |
15612 | break; |
15613 | return -ENOTCONN; |
15614 | case NL80211_IFTYPE_STATION: |
15615 | case NL80211_IFTYPE_P2P_CLIENT: |
15616 | if (wdev->connected) |
15617 | break; |
15618 | return -ENOTCONN; |
15619 | default: |
15620 | return -EOPNOTSUPP; |
15621 | } |
15622 | |
15623 | buf = nla_data(nla: info->attrs[NL80211_ATTR_FRAME]); |
15624 | len = nla_len(nla: info->attrs[NL80211_ATTR_FRAME]); |
15625 | dest = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
15626 | proto = nla_get_u16(nla: info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]); |
15627 | noencrypt = |
15628 | nla_get_flag(nla: info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT]); |
15629 | |
15630 | link_id = nl80211_link_id_or_invalid(attrs: info->attrs); |
15631 | |
15632 | err = rdev_tx_control_port(rdev, dev, buf, len, |
15633 | dest, cpu_to_be16(proto), noencrypt, link: link_id, |
15634 | cookie: dont_wait_for_ack ? NULL : &cookie); |
15635 | if (!err && !dont_wait_for_ack) |
15636 | nl_set_extack_cookie_u64(extack: info->extack, cookie); |
15637 | return err; |
15638 | } |
15639 | |
15640 | static int nl80211_get_ftm_responder_stats(struct sk_buff *skb, |
15641 | struct genl_info *info) |
15642 | { |
15643 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
15644 | struct net_device *dev = info->user_ptr[1]; |
15645 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
15646 | struct cfg80211_ftm_responder_stats ftm_stats = {}; |
15647 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); |
15648 | struct sk_buff *msg; |
15649 | void *hdr; |
15650 | struct nlattr *ftm_stats_attr; |
15651 | int err; |
15652 | |
15653 | if (wdev->iftype != NL80211_IFTYPE_AP || |
15654 | !wdev->links[link_id].ap.beacon_interval) |
15655 | return -EOPNOTSUPP; |
15656 | |
15657 | err = rdev_get_ftm_responder_stats(rdev, dev, ftm_stats: &ftm_stats); |
15658 | if (err) |
15659 | return err; |
15660 | |
15661 | if (!ftm_stats.filled) |
15662 | return -ENODATA; |
15663 | |
15664 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
15665 | if (!msg) |
15666 | return -ENOMEM; |
15667 | |
15668 | hdr = nl80211hdr_put(skb: msg, portid: info->snd_portid, seq: info->snd_seq, flags: 0, |
15669 | cmd: NL80211_CMD_GET_FTM_RESPONDER_STATS); |
15670 | if (!hdr) |
15671 | goto nla_put_failure; |
15672 | |
15673 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex)) |
15674 | goto nla_put_failure; |
15675 | |
15676 | ftm_stats_attr = nla_nest_start_noflag(skb: msg, |
15677 | attrtype: NL80211_ATTR_FTM_RESPONDER_STATS); |
15678 | if (!ftm_stats_attr) |
15679 | goto nla_put_failure; |
15680 | |
15681 | #define SET_FTM(field, name, type) \ |
15682 | do { if ((ftm_stats.filled & BIT(NL80211_FTM_STATS_ ## name)) && \ |
15683 | nla_put_ ## type(msg, NL80211_FTM_STATS_ ## name, \ |
15684 | ftm_stats.field)) \ |
15685 | goto nla_put_failure; } while (0) |
15686 | #define SET_FTM_U64(field, name) \ |
15687 | do { if ((ftm_stats.filled & BIT(NL80211_FTM_STATS_ ## name)) && \ |
15688 | nla_put_u64_64bit(msg, NL80211_FTM_STATS_ ## name, \ |
15689 | ftm_stats.field, NL80211_FTM_STATS_PAD)) \ |
15690 | goto nla_put_failure; } while (0) |
15691 | |
15692 | SET_FTM(success_num, SUCCESS_NUM, u32); |
15693 | SET_FTM(partial_num, PARTIAL_NUM, u32); |
15694 | SET_FTM(failed_num, FAILED_NUM, u32); |
15695 | SET_FTM(asap_num, ASAP_NUM, u32); |
15696 | SET_FTM(non_asap_num, NON_ASAP_NUM, u32); |
15697 | SET_FTM_U64(total_duration_ms, TOTAL_DURATION_MSEC); |
15698 | SET_FTM(unknown_triggers_num, UNKNOWN_TRIGGERS_NUM, u32); |
15699 | SET_FTM(reschedule_requests_num, RESCHEDULE_REQUESTS_NUM, u32); |
15700 | SET_FTM(out_of_window_triggers_num, OUT_OF_WINDOW_TRIGGERS_NUM, u32); |
15701 | #undef SET_FTM |
15702 | |
15703 | nla_nest_end(skb: msg, start: ftm_stats_attr); |
15704 | |
15705 | genlmsg_end(skb: msg, hdr); |
15706 | return genlmsg_reply(skb: msg, info); |
15707 | |
15708 | nla_put_failure: |
15709 | nlmsg_free(skb: msg); |
15710 | return -ENOBUFS; |
15711 | } |
15712 | |
15713 | static int nl80211_update_owe_info(struct sk_buff *skb, struct genl_info *info) |
15714 | { |
15715 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
15716 | struct cfg80211_update_owe_info owe_info; |
15717 | struct net_device *dev = info->user_ptr[1]; |
15718 | |
15719 | if (!rdev->ops->update_owe_info) |
15720 | return -EOPNOTSUPP; |
15721 | |
15722 | if (!info->attrs[NL80211_ATTR_STATUS_CODE] || |
15723 | !info->attrs[NL80211_ATTR_MAC]) |
15724 | return -EINVAL; |
15725 | |
15726 | memset(&owe_info, 0, sizeof(owe_info)); |
15727 | owe_info.status = nla_get_u16(nla: info->attrs[NL80211_ATTR_STATUS_CODE]); |
15728 | nla_memcpy(dest: owe_info.peer, src: info->attrs[NL80211_ATTR_MAC], ETH_ALEN); |
15729 | |
15730 | if (info->attrs[NL80211_ATTR_IE]) { |
15731 | owe_info.ie = nla_data(nla: info->attrs[NL80211_ATTR_IE]); |
15732 | owe_info.ie_len = nla_len(nla: info->attrs[NL80211_ATTR_IE]); |
15733 | } |
15734 | |
15735 | return rdev_update_owe_info(rdev, dev, oweinfo: &owe_info); |
15736 | } |
15737 | |
15738 | static int nl80211_probe_mesh_link(struct sk_buff *skb, struct genl_info *info) |
15739 | { |
15740 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
15741 | struct net_device *dev = info->user_ptr[1]; |
15742 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
15743 | struct station_info sinfo = {}; |
15744 | const u8 *buf; |
15745 | size_t len; |
15746 | u8 *dest; |
15747 | int err; |
15748 | |
15749 | if (!rdev->ops->probe_mesh_link || !rdev->ops->get_station) |
15750 | return -EOPNOTSUPP; |
15751 | |
15752 | if (!info->attrs[NL80211_ATTR_MAC] || |
15753 | !info->attrs[NL80211_ATTR_FRAME]) { |
15754 | GENL_SET_ERR_MSG(info, "Frame or MAC missing" ); |
15755 | return -EINVAL; |
15756 | } |
15757 | |
15758 | if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) |
15759 | return -EOPNOTSUPP; |
15760 | |
15761 | dest = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
15762 | buf = nla_data(nla: info->attrs[NL80211_ATTR_FRAME]); |
15763 | len = nla_len(nla: info->attrs[NL80211_ATTR_FRAME]); |
15764 | |
15765 | if (len < sizeof(struct ethhdr)) |
15766 | return -EINVAL; |
15767 | |
15768 | if (!ether_addr_equal(addr1: buf, addr2: dest) || is_multicast_ether_addr(addr: buf) || |
15769 | !ether_addr_equal(addr1: buf + ETH_ALEN, addr2: dev->dev_addr)) |
15770 | return -EINVAL; |
15771 | |
15772 | err = rdev_get_station(rdev, dev, mac: dest, sinfo: &sinfo); |
15773 | if (err) |
15774 | return err; |
15775 | |
15776 | cfg80211_sinfo_release_content(sinfo: &sinfo); |
15777 | |
15778 | return rdev_probe_mesh_link(rdev, dev, dest, buf, len); |
15779 | } |
15780 | |
15781 | static int parse_tid_conf(struct cfg80211_registered_device *rdev, |
15782 | struct nlattr *attrs[], struct net_device *dev, |
15783 | struct cfg80211_tid_cfg *tid_conf, |
15784 | struct genl_info *info, const u8 *peer, |
15785 | unsigned int link_id) |
15786 | { |
15787 | struct netlink_ext_ack *extack = info->extack; |
15788 | u64 mask; |
15789 | int err; |
15790 | |
15791 | if (!attrs[NL80211_TID_CONFIG_ATTR_TIDS]) |
15792 | return -EINVAL; |
15793 | |
15794 | tid_conf->config_override = |
15795 | nla_get_flag(nla: attrs[NL80211_TID_CONFIG_ATTR_OVERRIDE]); |
15796 | tid_conf->tids = nla_get_u16(nla: attrs[NL80211_TID_CONFIG_ATTR_TIDS]); |
15797 | |
15798 | if (tid_conf->config_override) { |
15799 | if (rdev->ops->reset_tid_config) { |
15800 | err = rdev_reset_tid_config(rdev, dev, peer, |
15801 | tids: tid_conf->tids); |
15802 | if (err) |
15803 | return err; |
15804 | } else { |
15805 | return -EINVAL; |
15806 | } |
15807 | } |
15808 | |
15809 | if (attrs[NL80211_TID_CONFIG_ATTR_NOACK]) { |
15810 | tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_NOACK); |
15811 | tid_conf->noack = |
15812 | nla_get_u8(nla: attrs[NL80211_TID_CONFIG_ATTR_NOACK]); |
15813 | } |
15814 | |
15815 | if (attrs[NL80211_TID_CONFIG_ATTR_RETRY_SHORT]) { |
15816 | tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_RETRY_SHORT); |
15817 | tid_conf->retry_short = |
15818 | nla_get_u8(nla: attrs[NL80211_TID_CONFIG_ATTR_RETRY_SHORT]); |
15819 | |
15820 | if (tid_conf->retry_short > rdev->wiphy.max_data_retry_count) |
15821 | return -EINVAL; |
15822 | } |
15823 | |
15824 | if (attrs[NL80211_TID_CONFIG_ATTR_RETRY_LONG]) { |
15825 | tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_RETRY_LONG); |
15826 | tid_conf->retry_long = |
15827 | nla_get_u8(nla: attrs[NL80211_TID_CONFIG_ATTR_RETRY_LONG]); |
15828 | |
15829 | if (tid_conf->retry_long > rdev->wiphy.max_data_retry_count) |
15830 | return -EINVAL; |
15831 | } |
15832 | |
15833 | if (attrs[NL80211_TID_CONFIG_ATTR_AMPDU_CTRL]) { |
15834 | tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_AMPDU_CTRL); |
15835 | tid_conf->ampdu = |
15836 | nla_get_u8(nla: attrs[NL80211_TID_CONFIG_ATTR_AMPDU_CTRL]); |
15837 | } |
15838 | |
15839 | if (attrs[NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL]) { |
15840 | tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL); |
15841 | tid_conf->rtscts = |
15842 | nla_get_u8(nla: attrs[NL80211_TID_CONFIG_ATTR_RTSCTS_CTRL]); |
15843 | } |
15844 | |
15845 | if (attrs[NL80211_TID_CONFIG_ATTR_AMSDU_CTRL]) { |
15846 | tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_AMSDU_CTRL); |
15847 | tid_conf->amsdu = |
15848 | nla_get_u8(nla: attrs[NL80211_TID_CONFIG_ATTR_AMSDU_CTRL]); |
15849 | } |
15850 | |
15851 | if (attrs[NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE]) { |
15852 | u32 idx = NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE, attr; |
15853 | |
15854 | tid_conf->txrate_type = nla_get_u8(nla: attrs[idx]); |
15855 | |
15856 | if (tid_conf->txrate_type != NL80211_TX_RATE_AUTOMATIC) { |
15857 | attr = NL80211_TID_CONFIG_ATTR_TX_RATE; |
15858 | err = nl80211_parse_tx_bitrate_mask(info, attrs, attr, |
15859 | mask: &tid_conf->txrate_mask, dev, |
15860 | default_all_enabled: true, link_id); |
15861 | if (err) |
15862 | return err; |
15863 | |
15864 | tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_TX_RATE); |
15865 | } |
15866 | tid_conf->mask |= BIT(NL80211_TID_CONFIG_ATTR_TX_RATE_TYPE); |
15867 | } |
15868 | |
15869 | if (peer) |
15870 | mask = rdev->wiphy.tid_config_support.peer; |
15871 | else |
15872 | mask = rdev->wiphy.tid_config_support.vif; |
15873 | |
15874 | if (tid_conf->mask & ~mask) { |
15875 | NL_SET_ERR_MSG(extack, "unsupported TID configuration" ); |
15876 | return -EOPNOTSUPP; |
15877 | } |
15878 | |
15879 | return 0; |
15880 | } |
15881 | |
15882 | static int nl80211_set_tid_config(struct sk_buff *skb, |
15883 | struct genl_info *info) |
15884 | { |
15885 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
15886 | struct nlattr *attrs[NL80211_TID_CONFIG_ATTR_MAX + 1]; |
15887 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); |
15888 | struct net_device *dev = info->user_ptr[1]; |
15889 | struct cfg80211_tid_config *tid_config; |
15890 | struct nlattr *tid; |
15891 | int conf_idx = 0, rem_conf; |
15892 | int ret = -EINVAL; |
15893 | u32 num_conf = 0; |
15894 | |
15895 | if (!info->attrs[NL80211_ATTR_TID_CONFIG]) |
15896 | return -EINVAL; |
15897 | |
15898 | if (!rdev->ops->set_tid_config) |
15899 | return -EOPNOTSUPP; |
15900 | |
15901 | nla_for_each_nested(tid, info->attrs[NL80211_ATTR_TID_CONFIG], |
15902 | rem_conf) |
15903 | num_conf++; |
15904 | |
15905 | tid_config = kzalloc(struct_size(tid_config, tid_conf, num_conf), |
15906 | GFP_KERNEL); |
15907 | if (!tid_config) |
15908 | return -ENOMEM; |
15909 | |
15910 | tid_config->n_tid_conf = num_conf; |
15911 | |
15912 | if (info->attrs[NL80211_ATTR_MAC]) |
15913 | tid_config->peer = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
15914 | |
15915 | nla_for_each_nested(tid, info->attrs[NL80211_ATTR_TID_CONFIG], |
15916 | rem_conf) { |
15917 | ret = nla_parse_nested(tb: attrs, maxtype: NL80211_TID_CONFIG_ATTR_MAX, |
15918 | nla: tid, NULL, NULL); |
15919 | |
15920 | if (ret) |
15921 | goto bad_tid_conf; |
15922 | |
15923 | ret = parse_tid_conf(rdev, attrs, dev, |
15924 | tid_conf: &tid_config->tid_conf[conf_idx], |
15925 | info, peer: tid_config->peer, link_id); |
15926 | if (ret) |
15927 | goto bad_tid_conf; |
15928 | |
15929 | conf_idx++; |
15930 | } |
15931 | |
15932 | ret = rdev_set_tid_config(rdev, dev, tid_conf: tid_config); |
15933 | |
15934 | bad_tid_conf: |
15935 | kfree(objp: tid_config); |
15936 | return ret; |
15937 | } |
15938 | |
15939 | static int nl80211_color_change(struct sk_buff *skb, struct genl_info *info) |
15940 | { |
15941 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
15942 | struct cfg80211_color_change_settings params = {}; |
15943 | struct net_device *dev = info->user_ptr[1]; |
15944 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
15945 | struct nlattr **tb; |
15946 | u16 offset; |
15947 | int err; |
15948 | |
15949 | if (!rdev->ops->color_change) |
15950 | return -EOPNOTSUPP; |
15951 | |
15952 | if (!wiphy_ext_feature_isset(wiphy: &rdev->wiphy, |
15953 | ftidx: NL80211_EXT_FEATURE_BSS_COLOR)) |
15954 | return -EOPNOTSUPP; |
15955 | |
15956 | if (wdev->iftype != NL80211_IFTYPE_AP) |
15957 | return -EOPNOTSUPP; |
15958 | |
15959 | if (!info->attrs[NL80211_ATTR_COLOR_CHANGE_COUNT] || |
15960 | !info->attrs[NL80211_ATTR_COLOR_CHANGE_COLOR] || |
15961 | !info->attrs[NL80211_ATTR_COLOR_CHANGE_ELEMS]) |
15962 | return -EINVAL; |
15963 | |
15964 | params.count = nla_get_u8(nla: info->attrs[NL80211_ATTR_COLOR_CHANGE_COUNT]); |
15965 | params.color = nla_get_u8(nla: info->attrs[NL80211_ATTR_COLOR_CHANGE_COLOR]); |
15966 | |
15967 | err = nl80211_parse_beacon(rdev, attrs: info->attrs, bcn: ¶ms.beacon_next, |
15968 | extack: info->extack); |
15969 | if (err) |
15970 | return err; |
15971 | |
15972 | tb = kcalloc(n: NL80211_ATTR_MAX + 1, size: sizeof(*tb), GFP_KERNEL); |
15973 | if (!tb) |
15974 | return -ENOMEM; |
15975 | |
15976 | err = nla_parse_nested(tb, maxtype: NL80211_ATTR_MAX, |
15977 | nla: info->attrs[NL80211_ATTR_COLOR_CHANGE_ELEMS], |
15978 | policy: nl80211_policy, extack: info->extack); |
15979 | if (err) |
15980 | goto out; |
15981 | |
15982 | err = nl80211_parse_beacon(rdev, attrs: tb, bcn: ¶ms.beacon_color_change, |
15983 | extack: info->extack); |
15984 | if (err) |
15985 | goto out; |
15986 | |
15987 | if (!tb[NL80211_ATTR_CNTDWN_OFFS_BEACON]) { |
15988 | err = -EINVAL; |
15989 | goto out; |
15990 | } |
15991 | |
15992 | if (nla_len(nla: tb[NL80211_ATTR_CNTDWN_OFFS_BEACON]) != sizeof(u16)) { |
15993 | err = -EINVAL; |
15994 | goto out; |
15995 | } |
15996 | |
15997 | offset = nla_get_u16(nla: tb[NL80211_ATTR_CNTDWN_OFFS_BEACON]); |
15998 | if (offset >= params.beacon_color_change.tail_len) { |
15999 | err = -EINVAL; |
16000 | goto out; |
16001 | } |
16002 | |
16003 | if (params.beacon_color_change.tail[offset] != params.count) { |
16004 | err = -EINVAL; |
16005 | goto out; |
16006 | } |
16007 | |
16008 | params.counter_offset_beacon = offset; |
16009 | |
16010 | if (tb[NL80211_ATTR_CNTDWN_OFFS_PRESP]) { |
16011 | if (nla_len(nla: tb[NL80211_ATTR_CNTDWN_OFFS_PRESP]) != |
16012 | sizeof(u16)) { |
16013 | err = -EINVAL; |
16014 | goto out; |
16015 | } |
16016 | |
16017 | offset = nla_get_u16(nla: tb[NL80211_ATTR_CNTDWN_OFFS_PRESP]); |
16018 | if (offset >= params.beacon_color_change.probe_resp_len) { |
16019 | err = -EINVAL; |
16020 | goto out; |
16021 | } |
16022 | |
16023 | if (params.beacon_color_change.probe_resp[offset] != |
16024 | params.count) { |
16025 | err = -EINVAL; |
16026 | goto out; |
16027 | } |
16028 | |
16029 | params.counter_offset_presp = offset; |
16030 | } |
16031 | |
16032 | err = rdev_color_change(rdev, dev, params: ¶ms); |
16033 | |
16034 | out: |
16035 | kfree(objp: params.beacon_next.mbssid_ies); |
16036 | kfree(objp: params.beacon_color_change.mbssid_ies); |
16037 | kfree(objp: params.beacon_next.rnr_ies); |
16038 | kfree(objp: params.beacon_color_change.rnr_ies); |
16039 | kfree(objp: tb); |
16040 | return err; |
16041 | } |
16042 | |
16043 | static int nl80211_set_fils_aad(struct sk_buff *skb, |
16044 | struct genl_info *info) |
16045 | { |
16046 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
16047 | struct net_device *dev = info->user_ptr[1]; |
16048 | struct cfg80211_fils_aad fils_aad = {}; |
16049 | u8 *nonces; |
16050 | |
16051 | if (!info->attrs[NL80211_ATTR_MAC] || |
16052 | !info->attrs[NL80211_ATTR_FILS_KEK] || |
16053 | !info->attrs[NL80211_ATTR_FILS_NONCES]) |
16054 | return -EINVAL; |
16055 | |
16056 | fils_aad.macaddr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
16057 | fils_aad.kek_len = nla_len(nla: info->attrs[NL80211_ATTR_FILS_KEK]); |
16058 | fils_aad.kek = nla_data(nla: info->attrs[NL80211_ATTR_FILS_KEK]); |
16059 | nonces = nla_data(nla: info->attrs[NL80211_ATTR_FILS_NONCES]); |
16060 | fils_aad.snonce = nonces; |
16061 | fils_aad.anonce = nonces + FILS_NONCE_LEN; |
16062 | |
16063 | return rdev_set_fils_aad(rdev, dev, fils_aad: &fils_aad); |
16064 | } |
16065 | |
16066 | static int nl80211_add_link(struct sk_buff *skb, struct genl_info *info) |
16067 | { |
16068 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
16069 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); |
16070 | struct net_device *dev = info->user_ptr[1]; |
16071 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
16072 | int ret; |
16073 | |
16074 | if (!(wdev->wiphy->flags & WIPHY_FLAG_SUPPORTS_MLO)) |
16075 | return -EINVAL; |
16076 | |
16077 | switch (wdev->iftype) { |
16078 | case NL80211_IFTYPE_AP: |
16079 | break; |
16080 | default: |
16081 | return -EINVAL; |
16082 | } |
16083 | |
16084 | if (!info->attrs[NL80211_ATTR_MAC] || |
16085 | !is_valid_ether_addr(addr: nla_data(nla: info->attrs[NL80211_ATTR_MAC]))) |
16086 | return -EINVAL; |
16087 | |
16088 | wdev->valid_links |= BIT(link_id); |
16089 | ether_addr_copy(dst: wdev->links[link_id].addr, |
16090 | src: nla_data(nla: info->attrs[NL80211_ATTR_MAC])); |
16091 | |
16092 | ret = rdev_add_intf_link(rdev, wdev, link_id); |
16093 | if (ret) { |
16094 | wdev->valid_links &= ~BIT(link_id); |
16095 | eth_zero_addr(addr: wdev->links[link_id].addr); |
16096 | } |
16097 | |
16098 | return ret; |
16099 | } |
16100 | |
16101 | static int nl80211_remove_link(struct sk_buff *skb, struct genl_info *info) |
16102 | { |
16103 | unsigned int link_id = nl80211_link_id(attrs: info->attrs); |
16104 | struct net_device *dev = info->user_ptr[1]; |
16105 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
16106 | |
16107 | /* cannot remove if there's no link */ |
16108 | if (!info->attrs[NL80211_ATTR_MLO_LINK_ID]) |
16109 | return -EINVAL; |
16110 | |
16111 | switch (wdev->iftype) { |
16112 | case NL80211_IFTYPE_AP: |
16113 | break; |
16114 | default: |
16115 | return -EINVAL; |
16116 | } |
16117 | |
16118 | cfg80211_remove_link(wdev, link_id); |
16119 | |
16120 | return 0; |
16121 | } |
16122 | |
16123 | static int |
16124 | nl80211_add_mod_link_station(struct sk_buff *skb, struct genl_info *info, |
16125 | bool add) |
16126 | { |
16127 | struct link_station_parameters params = {}; |
16128 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
16129 | struct net_device *dev = info->user_ptr[1]; |
16130 | int err; |
16131 | |
16132 | if ((add && !rdev->ops->add_link_station) || |
16133 | (!add && !rdev->ops->mod_link_station)) |
16134 | return -EOPNOTSUPP; |
16135 | |
16136 | if (add && !info->attrs[NL80211_ATTR_MAC]) |
16137 | return -EINVAL; |
16138 | |
16139 | if (!info->attrs[NL80211_ATTR_MLD_ADDR]) |
16140 | return -EINVAL; |
16141 | |
16142 | if (add && !info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) |
16143 | return -EINVAL; |
16144 | |
16145 | params.mld_mac = nla_data(nla: info->attrs[NL80211_ATTR_MLD_ADDR]); |
16146 | |
16147 | if (info->attrs[NL80211_ATTR_MAC]) { |
16148 | params.link_mac = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
16149 | if (!is_valid_ether_addr(addr: params.link_mac)) |
16150 | return -EINVAL; |
16151 | } |
16152 | |
16153 | if (!info->attrs[NL80211_ATTR_MLO_LINK_ID]) |
16154 | return -EINVAL; |
16155 | |
16156 | params.link_id = nla_get_u8(nla: info->attrs[NL80211_ATTR_MLO_LINK_ID]); |
16157 | |
16158 | if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) { |
16159 | params.supported_rates = |
16160 | nla_data(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); |
16161 | params.supported_rates_len = |
16162 | nla_len(nla: info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); |
16163 | } |
16164 | |
16165 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) |
16166 | params.ht_capa = |
16167 | nla_data(nla: info->attrs[NL80211_ATTR_HT_CAPABILITY]); |
16168 | |
16169 | if (info->attrs[NL80211_ATTR_VHT_CAPABILITY]) |
16170 | params.vht_capa = |
16171 | nla_data(nla: info->attrs[NL80211_ATTR_VHT_CAPABILITY]); |
16172 | |
16173 | if (info->attrs[NL80211_ATTR_HE_CAPABILITY]) { |
16174 | params.he_capa = |
16175 | nla_data(nla: info->attrs[NL80211_ATTR_HE_CAPABILITY]); |
16176 | params.he_capa_len = |
16177 | nla_len(nla: info->attrs[NL80211_ATTR_HE_CAPABILITY]); |
16178 | |
16179 | if (info->attrs[NL80211_ATTR_EHT_CAPABILITY]) { |
16180 | params.eht_capa = |
16181 | nla_data(nla: info->attrs[NL80211_ATTR_EHT_CAPABILITY]); |
16182 | params.eht_capa_len = |
16183 | nla_len(nla: info->attrs[NL80211_ATTR_EHT_CAPABILITY]); |
16184 | |
16185 | if (!ieee80211_eht_capa_size_ok(he_capa: (const u8 *)params.he_capa, |
16186 | data: (const u8 *)params.eht_capa, |
16187 | len: params.eht_capa_len, |
16188 | from_ap: false)) |
16189 | return -EINVAL; |
16190 | } |
16191 | } |
16192 | |
16193 | if (info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]) |
16194 | params.he_6ghz_capa = |
16195 | nla_data(nla: info->attrs[NL80211_ATTR_HE_6GHZ_CAPABILITY]); |
16196 | |
16197 | if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) { |
16198 | params.opmode_notif_used = true; |
16199 | params.opmode_notif = |
16200 | nla_get_u8(nla: info->attrs[NL80211_ATTR_OPMODE_NOTIF]); |
16201 | } |
16202 | |
16203 | err = nl80211_parse_sta_txpower_setting(info, txpwr: ¶ms.txpwr, |
16204 | txpwr_set: ¶ms.txpwr_set); |
16205 | if (err) |
16206 | return err; |
16207 | |
16208 | if (add) |
16209 | return rdev_add_link_station(rdev, dev, params: ¶ms); |
16210 | |
16211 | return rdev_mod_link_station(rdev, dev, params: ¶ms); |
16212 | } |
16213 | |
16214 | static int |
16215 | nl80211_add_link_station(struct sk_buff *skb, struct genl_info *info) |
16216 | { |
16217 | return nl80211_add_mod_link_station(skb, info, add: true); |
16218 | } |
16219 | |
16220 | static int |
16221 | nl80211_modify_link_station(struct sk_buff *skb, struct genl_info *info) |
16222 | { |
16223 | return nl80211_add_mod_link_station(skb, info, add: false); |
16224 | } |
16225 | |
16226 | static int |
16227 | nl80211_remove_link_station(struct sk_buff *skb, struct genl_info *info) |
16228 | { |
16229 | struct link_station_del_parameters params = {}; |
16230 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
16231 | struct net_device *dev = info->user_ptr[1]; |
16232 | |
16233 | if (!rdev->ops->del_link_station) |
16234 | return -EOPNOTSUPP; |
16235 | |
16236 | if (!info->attrs[NL80211_ATTR_MLD_ADDR] || |
16237 | !info->attrs[NL80211_ATTR_MLO_LINK_ID]) |
16238 | return -EINVAL; |
16239 | |
16240 | params.mld_mac = nla_data(nla: info->attrs[NL80211_ATTR_MLD_ADDR]); |
16241 | params.link_id = nla_get_u8(nla: info->attrs[NL80211_ATTR_MLO_LINK_ID]); |
16242 | |
16243 | return rdev_del_link_station(rdev, dev, params: ¶ms); |
16244 | } |
16245 | |
16246 | static int nl80211_set_hw_timestamp(struct sk_buff *skb, |
16247 | struct genl_info *info) |
16248 | { |
16249 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
16250 | struct net_device *dev = info->user_ptr[1]; |
16251 | struct cfg80211_set_hw_timestamp hwts = {}; |
16252 | |
16253 | if (!rdev->wiphy.hw_timestamp_max_peers) |
16254 | return -EOPNOTSUPP; |
16255 | |
16256 | if (!info->attrs[NL80211_ATTR_MAC] && |
16257 | rdev->wiphy.hw_timestamp_max_peers != CFG80211_HW_TIMESTAMP_ALL_PEERS) |
16258 | return -EOPNOTSUPP; |
16259 | |
16260 | if (info->attrs[NL80211_ATTR_MAC]) |
16261 | hwts.macaddr = nla_data(nla: info->attrs[NL80211_ATTR_MAC]); |
16262 | |
16263 | hwts.enable = |
16264 | nla_get_flag(nla: info->attrs[NL80211_ATTR_HW_TIMESTAMP_ENABLED]); |
16265 | |
16266 | return rdev_set_hw_timestamp(rdev, dev, hwts: &hwts); |
16267 | } |
16268 | |
16269 | static int |
16270 | nl80211_set_ttlm(struct sk_buff *skb, struct genl_info *info) |
16271 | { |
16272 | struct cfg80211_ttlm_params params = {}; |
16273 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
16274 | struct net_device *dev = info->user_ptr[1]; |
16275 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
16276 | |
16277 | if (wdev->iftype != NL80211_IFTYPE_STATION && |
16278 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT) |
16279 | return -EOPNOTSUPP; |
16280 | |
16281 | if (!wdev->connected) |
16282 | return -ENOLINK; |
16283 | |
16284 | if (!info->attrs[NL80211_ATTR_MLO_TTLM_DLINK] || |
16285 | !info->attrs[NL80211_ATTR_MLO_TTLM_ULINK]) |
16286 | return -EINVAL; |
16287 | |
16288 | nla_memcpy(dest: params.dlink, |
16289 | src: info->attrs[NL80211_ATTR_MLO_TTLM_DLINK], |
16290 | count: sizeof(params.dlink)); |
16291 | nla_memcpy(dest: params.ulink, |
16292 | src: info->attrs[NL80211_ATTR_MLO_TTLM_ULINK], |
16293 | count: sizeof(params.ulink)); |
16294 | |
16295 | return rdev_set_ttlm(rdev, dev, params: ¶ms); |
16296 | } |
16297 | |
16298 | #define NL80211_FLAG_NEED_WIPHY 0x01 |
16299 | #define NL80211_FLAG_NEED_NETDEV 0x02 |
16300 | #define NL80211_FLAG_NEED_RTNL 0x04 |
16301 | #define NL80211_FLAG_CHECK_NETDEV_UP 0x08 |
16302 | #define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\ |
16303 | NL80211_FLAG_CHECK_NETDEV_UP) |
16304 | #define NL80211_FLAG_NEED_WDEV 0x10 |
16305 | /* If a netdev is associated, it must be UP, P2P must be started */ |
16306 | #define NL80211_FLAG_NEED_WDEV_UP (NL80211_FLAG_NEED_WDEV |\ |
16307 | NL80211_FLAG_CHECK_NETDEV_UP) |
16308 | #define NL80211_FLAG_CLEAR_SKB 0x20 |
16309 | #define NL80211_FLAG_NO_WIPHY_MTX 0x40 |
16310 | #define NL80211_FLAG_MLO_VALID_LINK_ID 0x80 |
16311 | #define NL80211_FLAG_MLO_UNSUPPORTED 0x100 |
16312 | |
16313 | #define INTERNAL_FLAG_SELECTORS(__sel) \ |
16314 | SELECTOR(__sel, NONE, 0) /* must be first */ \ |
16315 | SELECTOR(__sel, WIPHY, \ |
16316 | NL80211_FLAG_NEED_WIPHY) \ |
16317 | SELECTOR(__sel, WDEV, \ |
16318 | NL80211_FLAG_NEED_WDEV) \ |
16319 | SELECTOR(__sel, NETDEV, \ |
16320 | NL80211_FLAG_NEED_NETDEV) \ |
16321 | SELECTOR(__sel, NETDEV_LINK, \ |
16322 | NL80211_FLAG_NEED_NETDEV | \ |
16323 | NL80211_FLAG_MLO_VALID_LINK_ID) \ |
16324 | SELECTOR(__sel, NETDEV_NO_MLO, \ |
16325 | NL80211_FLAG_NEED_NETDEV | \ |
16326 | NL80211_FLAG_MLO_UNSUPPORTED) \ |
16327 | SELECTOR(__sel, WIPHY_RTNL, \ |
16328 | NL80211_FLAG_NEED_WIPHY | \ |
16329 | NL80211_FLAG_NEED_RTNL) \ |
16330 | SELECTOR(__sel, WIPHY_RTNL_NOMTX, \ |
16331 | NL80211_FLAG_NEED_WIPHY | \ |
16332 | NL80211_FLAG_NEED_RTNL | \ |
16333 | NL80211_FLAG_NO_WIPHY_MTX) \ |
16334 | SELECTOR(__sel, WDEV_RTNL, \ |
16335 | NL80211_FLAG_NEED_WDEV | \ |
16336 | NL80211_FLAG_NEED_RTNL) \ |
16337 | SELECTOR(__sel, NETDEV_RTNL, \ |
16338 | NL80211_FLAG_NEED_NETDEV | \ |
16339 | NL80211_FLAG_NEED_RTNL) \ |
16340 | SELECTOR(__sel, NETDEV_UP, \ |
16341 | NL80211_FLAG_NEED_NETDEV_UP) \ |
16342 | SELECTOR(__sel, NETDEV_UP_LINK, \ |
16343 | NL80211_FLAG_NEED_NETDEV_UP | \ |
16344 | NL80211_FLAG_MLO_VALID_LINK_ID) \ |
16345 | SELECTOR(__sel, NETDEV_UP_NO_MLO, \ |
16346 | NL80211_FLAG_NEED_NETDEV_UP | \ |
16347 | NL80211_FLAG_MLO_UNSUPPORTED) \ |
16348 | SELECTOR(__sel, NETDEV_UP_NO_MLO_CLEAR, \ |
16349 | NL80211_FLAG_NEED_NETDEV_UP | \ |
16350 | NL80211_FLAG_CLEAR_SKB | \ |
16351 | NL80211_FLAG_MLO_UNSUPPORTED) \ |
16352 | SELECTOR(__sel, NETDEV_UP_NOTMX, \ |
16353 | NL80211_FLAG_NEED_NETDEV_UP | \ |
16354 | NL80211_FLAG_NO_WIPHY_MTX) \ |
16355 | SELECTOR(__sel, NETDEV_UP_NOTMX_NOMLO, \ |
16356 | NL80211_FLAG_NEED_NETDEV_UP | \ |
16357 | NL80211_FLAG_NO_WIPHY_MTX | \ |
16358 | NL80211_FLAG_MLO_UNSUPPORTED) \ |
16359 | SELECTOR(__sel, NETDEV_UP_CLEAR, \ |
16360 | NL80211_FLAG_NEED_NETDEV_UP | \ |
16361 | NL80211_FLAG_CLEAR_SKB) \ |
16362 | SELECTOR(__sel, WDEV_UP, \ |
16363 | NL80211_FLAG_NEED_WDEV_UP) \ |
16364 | SELECTOR(__sel, WDEV_UP_LINK, \ |
16365 | NL80211_FLAG_NEED_WDEV_UP | \ |
16366 | NL80211_FLAG_MLO_VALID_LINK_ID) \ |
16367 | SELECTOR(__sel, WDEV_UP_RTNL, \ |
16368 | NL80211_FLAG_NEED_WDEV_UP | \ |
16369 | NL80211_FLAG_NEED_RTNL) \ |
16370 | SELECTOR(__sel, WIPHY_CLEAR, \ |
16371 | NL80211_FLAG_NEED_WIPHY | \ |
16372 | NL80211_FLAG_CLEAR_SKB) |
16373 | |
16374 | enum nl80211_internal_flags_selector { |
16375 | #define SELECTOR(_, name, value) NL80211_IFL_SEL_##name, |
16376 | INTERNAL_FLAG_SELECTORS(_) |
16377 | #undef SELECTOR |
16378 | }; |
16379 | |
16380 | static u32 nl80211_internal_flags[] = { |
16381 | #define SELECTOR(_, name, value) [NL80211_IFL_SEL_##name] = value, |
16382 | INTERNAL_FLAG_SELECTORS(_) |
16383 | #undef SELECTOR |
16384 | }; |
16385 | |
16386 | static int nl80211_pre_doit(const struct genl_split_ops *ops, |
16387 | struct sk_buff *skb, |
16388 | struct genl_info *info) |
16389 | { |
16390 | struct cfg80211_registered_device *rdev = NULL; |
16391 | struct wireless_dev *wdev = NULL; |
16392 | struct net_device *dev = NULL; |
16393 | u32 internal_flags; |
16394 | int err; |
16395 | |
16396 | if (WARN_ON(ops->internal_flags >= ARRAY_SIZE(nl80211_internal_flags))) |
16397 | return -EINVAL; |
16398 | |
16399 | internal_flags = nl80211_internal_flags[ops->internal_flags]; |
16400 | |
16401 | rtnl_lock(); |
16402 | if (internal_flags & NL80211_FLAG_NEED_WIPHY) { |
16403 | rdev = cfg80211_get_dev_from_info(netns: genl_info_net(info), info); |
16404 | if (IS_ERR(ptr: rdev)) { |
16405 | err = PTR_ERR(ptr: rdev); |
16406 | goto out_unlock; |
16407 | } |
16408 | info->user_ptr[0] = rdev; |
16409 | } else if (internal_flags & NL80211_FLAG_NEED_NETDEV || |
16410 | internal_flags & NL80211_FLAG_NEED_WDEV) { |
16411 | wdev = __cfg80211_wdev_from_attrs(NULL, netns: genl_info_net(info), |
16412 | attrs: info->attrs); |
16413 | if (IS_ERR(ptr: wdev)) { |
16414 | err = PTR_ERR(ptr: wdev); |
16415 | goto out_unlock; |
16416 | } |
16417 | |
16418 | dev = wdev->netdev; |
16419 | dev_hold(dev); |
16420 | rdev = wiphy_to_rdev(wiphy: wdev->wiphy); |
16421 | |
16422 | if (internal_flags & NL80211_FLAG_NEED_NETDEV) { |
16423 | if (!dev) { |
16424 | err = -EINVAL; |
16425 | goto out_unlock; |
16426 | } |
16427 | |
16428 | info->user_ptr[1] = dev; |
16429 | } else { |
16430 | info->user_ptr[1] = wdev; |
16431 | } |
16432 | |
16433 | if (internal_flags & NL80211_FLAG_CHECK_NETDEV_UP && |
16434 | !wdev_running(wdev)) { |
16435 | err = -ENETDOWN; |
16436 | goto out_unlock; |
16437 | } |
16438 | |
16439 | info->user_ptr[0] = rdev; |
16440 | } |
16441 | |
16442 | if (internal_flags & NL80211_FLAG_MLO_VALID_LINK_ID) { |
16443 | struct nlattr *link_id = info->attrs[NL80211_ATTR_MLO_LINK_ID]; |
16444 | |
16445 | if (!wdev) { |
16446 | err = -EINVAL; |
16447 | goto out_unlock; |
16448 | } |
16449 | |
16450 | /* MLO -> require valid link ID */ |
16451 | if (wdev->valid_links && |
16452 | (!link_id || |
16453 | !(wdev->valid_links & BIT(nla_get_u8(link_id))))) { |
16454 | err = -EINVAL; |
16455 | goto out_unlock; |
16456 | } |
16457 | |
16458 | /* non-MLO -> no link ID attribute accepted */ |
16459 | if (!wdev->valid_links && link_id) { |
16460 | err = -EINVAL; |
16461 | goto out_unlock; |
16462 | } |
16463 | } |
16464 | |
16465 | if (internal_flags & NL80211_FLAG_MLO_UNSUPPORTED) { |
16466 | if (info->attrs[NL80211_ATTR_MLO_LINK_ID] || |
16467 | (wdev && wdev->valid_links)) { |
16468 | err = -EINVAL; |
16469 | goto out_unlock; |
16470 | } |
16471 | } |
16472 | |
16473 | if (rdev && !(internal_flags & NL80211_FLAG_NO_WIPHY_MTX)) { |
16474 | wiphy_lock(wiphy: &rdev->wiphy); |
16475 | /* we keep the mutex locked until post_doit */ |
16476 | __release(&rdev->wiphy.mtx); |
16477 | } |
16478 | if (!(internal_flags & NL80211_FLAG_NEED_RTNL)) |
16479 | rtnl_unlock(); |
16480 | |
16481 | return 0; |
16482 | out_unlock: |
16483 | rtnl_unlock(); |
16484 | dev_put(dev); |
16485 | return err; |
16486 | } |
16487 | |
16488 | static void nl80211_post_doit(const struct genl_split_ops *ops, |
16489 | struct sk_buff *skb, |
16490 | struct genl_info *info) |
16491 | { |
16492 | u32 internal_flags = nl80211_internal_flags[ops->internal_flags]; |
16493 | |
16494 | if (info->user_ptr[1]) { |
16495 | if (internal_flags & NL80211_FLAG_NEED_WDEV) { |
16496 | struct wireless_dev *wdev = info->user_ptr[1]; |
16497 | |
16498 | dev_put(dev: wdev->netdev); |
16499 | } else { |
16500 | dev_put(dev: info->user_ptr[1]); |
16501 | } |
16502 | } |
16503 | |
16504 | if (info->user_ptr[0] && |
16505 | !(internal_flags & NL80211_FLAG_NO_WIPHY_MTX)) { |
16506 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
16507 | |
16508 | /* we kept the mutex locked since pre_doit */ |
16509 | __acquire(&rdev->wiphy.mtx); |
16510 | wiphy_unlock(wiphy: &rdev->wiphy); |
16511 | } |
16512 | |
16513 | if (internal_flags & NL80211_FLAG_NEED_RTNL) |
16514 | rtnl_unlock(); |
16515 | |
16516 | /* If needed, clear the netlink message payload from the SKB |
16517 | * as it might contain key data that shouldn't stick around on |
16518 | * the heap after the SKB is freed. The netlink message header |
16519 | * is still needed for further processing, so leave it intact. |
16520 | */ |
16521 | if (internal_flags & NL80211_FLAG_CLEAR_SKB) { |
16522 | struct nlmsghdr *nlh = nlmsg_hdr(skb); |
16523 | |
16524 | memset(nlmsg_data(nlh), 0, nlmsg_len(nlh)); |
16525 | } |
16526 | } |
16527 | |
16528 | static int nl80211_set_sar_sub_specs(struct cfg80211_registered_device *rdev, |
16529 | struct cfg80211_sar_specs *sar_specs, |
16530 | struct nlattr *spec[], int index) |
16531 | { |
16532 | u32 range_index, i; |
16533 | |
16534 | if (!sar_specs || !spec) |
16535 | return -EINVAL; |
16536 | |
16537 | if (!spec[NL80211_SAR_ATTR_SPECS_POWER] || |
16538 | !spec[NL80211_SAR_ATTR_SPECS_RANGE_INDEX]) |
16539 | return -EINVAL; |
16540 | |
16541 | range_index = nla_get_u32(nla: spec[NL80211_SAR_ATTR_SPECS_RANGE_INDEX]); |
16542 | |
16543 | /* check if range_index exceeds num_freq_ranges */ |
16544 | if (range_index >= rdev->wiphy.sar_capa->num_freq_ranges) |
16545 | return -EINVAL; |
16546 | |
16547 | /* check if range_index duplicates */ |
16548 | for (i = 0; i < index; i++) { |
16549 | if (sar_specs->sub_specs[i].freq_range_index == range_index) |
16550 | return -EINVAL; |
16551 | } |
16552 | |
16553 | sar_specs->sub_specs[index].power = |
16554 | nla_get_s32(nla: spec[NL80211_SAR_ATTR_SPECS_POWER]); |
16555 | |
16556 | sar_specs->sub_specs[index].freq_range_index = range_index; |
16557 | |
16558 | return 0; |
16559 | } |
16560 | |
16561 | static int nl80211_set_sar_specs(struct sk_buff *skb, struct genl_info *info) |
16562 | { |
16563 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
16564 | struct nlattr *spec[NL80211_SAR_ATTR_SPECS_MAX + 1]; |
16565 | struct nlattr *tb[NL80211_SAR_ATTR_MAX + 1]; |
16566 | struct cfg80211_sar_specs *sar_spec; |
16567 | enum nl80211_sar_type type; |
16568 | struct nlattr *spec_list; |
16569 | u32 specs; |
16570 | int rem, err; |
16571 | |
16572 | if (!rdev->wiphy.sar_capa || !rdev->ops->set_sar_specs) |
16573 | return -EOPNOTSUPP; |
16574 | |
16575 | if (!info->attrs[NL80211_ATTR_SAR_SPEC]) |
16576 | return -EINVAL; |
16577 | |
16578 | nla_parse_nested(tb, maxtype: NL80211_SAR_ATTR_MAX, |
16579 | nla: info->attrs[NL80211_ATTR_SAR_SPEC], |
16580 | NULL, NULL); |
16581 | |
16582 | if (!tb[NL80211_SAR_ATTR_TYPE] || !tb[NL80211_SAR_ATTR_SPECS]) |
16583 | return -EINVAL; |
16584 | |
16585 | type = nla_get_u32(nla: tb[NL80211_SAR_ATTR_TYPE]); |
16586 | if (type != rdev->wiphy.sar_capa->type) |
16587 | return -EINVAL; |
16588 | |
16589 | specs = 0; |
16590 | nla_for_each_nested(spec_list, tb[NL80211_SAR_ATTR_SPECS], rem) |
16591 | specs++; |
16592 | |
16593 | if (specs > rdev->wiphy.sar_capa->num_freq_ranges) |
16594 | return -EINVAL; |
16595 | |
16596 | sar_spec = kzalloc(struct_size(sar_spec, sub_specs, specs), GFP_KERNEL); |
16597 | if (!sar_spec) |
16598 | return -ENOMEM; |
16599 | |
16600 | sar_spec->type = type; |
16601 | specs = 0; |
16602 | nla_for_each_nested(spec_list, tb[NL80211_SAR_ATTR_SPECS], rem) { |
16603 | nla_parse_nested(tb: spec, maxtype: NL80211_SAR_ATTR_SPECS_MAX, |
16604 | nla: spec_list, NULL, NULL); |
16605 | |
16606 | switch (type) { |
16607 | case NL80211_SAR_TYPE_POWER: |
16608 | if (nl80211_set_sar_sub_specs(rdev, sar_specs: sar_spec, |
16609 | spec, index: specs)) { |
16610 | err = -EINVAL; |
16611 | goto error; |
16612 | } |
16613 | break; |
16614 | default: |
16615 | err = -EINVAL; |
16616 | goto error; |
16617 | } |
16618 | specs++; |
16619 | } |
16620 | |
16621 | sar_spec->num_sub_specs = specs; |
16622 | |
16623 | rdev->cur_cmd_info = info; |
16624 | err = rdev_set_sar_specs(rdev, sar: sar_spec); |
16625 | rdev->cur_cmd_info = NULL; |
16626 | error: |
16627 | kfree(objp: sar_spec); |
16628 | return err; |
16629 | } |
16630 | |
16631 | #define SELECTOR(__sel, name, value) \ |
16632 | ((__sel) == (value)) ? NL80211_IFL_SEL_##name : |
16633 | int __missing_selector(void); |
16634 | #define IFLAGS(__val) INTERNAL_FLAG_SELECTORS(__val) __missing_selector() |
16635 | |
16636 | static const struct genl_ops nl80211_ops[] = { |
16637 | { |
16638 | .cmd = NL80211_CMD_GET_WIPHY, |
16639 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16640 | .doit = nl80211_get_wiphy, |
16641 | .dumpit = nl80211_dump_wiphy, |
16642 | .done = nl80211_dump_wiphy_done, |
16643 | /* can be retrieved by unprivileged users */ |
16644 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY), |
16645 | }, |
16646 | }; |
16647 | |
16648 | static const struct genl_small_ops nl80211_small_ops[] = { |
16649 | { |
16650 | .cmd = NL80211_CMD_SET_WIPHY, |
16651 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16652 | .doit = nl80211_set_wiphy, |
16653 | .flags = GENL_UNS_ADMIN_PERM, |
16654 | }, |
16655 | { |
16656 | .cmd = NL80211_CMD_GET_INTERFACE, |
16657 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16658 | .doit = nl80211_get_interface, |
16659 | .dumpit = nl80211_dump_interface, |
16660 | /* can be retrieved by unprivileged users */ |
16661 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV), |
16662 | }, |
16663 | { |
16664 | .cmd = NL80211_CMD_SET_INTERFACE, |
16665 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16666 | .doit = nl80211_set_interface, |
16667 | .flags = GENL_UNS_ADMIN_PERM, |
16668 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV | |
16669 | NL80211_FLAG_NEED_RTNL), |
16670 | }, |
16671 | { |
16672 | .cmd = NL80211_CMD_NEW_INTERFACE, |
16673 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16674 | .doit = nl80211_new_interface, |
16675 | .flags = GENL_UNS_ADMIN_PERM, |
16676 | .internal_flags = |
16677 | IFLAGS(NL80211_FLAG_NEED_WIPHY | |
16678 | NL80211_FLAG_NEED_RTNL | |
16679 | /* we take the wiphy mutex later ourselves */ |
16680 | NL80211_FLAG_NO_WIPHY_MTX), |
16681 | }, |
16682 | { |
16683 | .cmd = NL80211_CMD_DEL_INTERFACE, |
16684 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16685 | .doit = nl80211_del_interface, |
16686 | .flags = GENL_UNS_ADMIN_PERM, |
16687 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV | |
16688 | NL80211_FLAG_NEED_RTNL), |
16689 | }, |
16690 | { |
16691 | .cmd = NL80211_CMD_GET_KEY, |
16692 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16693 | .doit = nl80211_get_key, |
16694 | .flags = GENL_UNS_ADMIN_PERM, |
16695 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
16696 | }, |
16697 | { |
16698 | .cmd = NL80211_CMD_SET_KEY, |
16699 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16700 | .doit = nl80211_set_key, |
16701 | .flags = GENL_UNS_ADMIN_PERM, |
16702 | /* cannot use NL80211_FLAG_MLO_VALID_LINK_ID, depends on key */ |
16703 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
16704 | NL80211_FLAG_CLEAR_SKB), |
16705 | }, |
16706 | { |
16707 | .cmd = NL80211_CMD_NEW_KEY, |
16708 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16709 | .doit = nl80211_new_key, |
16710 | .flags = GENL_UNS_ADMIN_PERM, |
16711 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
16712 | NL80211_FLAG_CLEAR_SKB), |
16713 | }, |
16714 | { |
16715 | .cmd = NL80211_CMD_DEL_KEY, |
16716 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16717 | .doit = nl80211_del_key, |
16718 | .flags = GENL_UNS_ADMIN_PERM, |
16719 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
16720 | }, |
16721 | { |
16722 | .cmd = NL80211_CMD_SET_BEACON, |
16723 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16724 | .flags = GENL_UNS_ADMIN_PERM, |
16725 | .doit = nl80211_set_beacon, |
16726 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
16727 | NL80211_FLAG_MLO_VALID_LINK_ID), |
16728 | }, |
16729 | { |
16730 | .cmd = NL80211_CMD_START_AP, |
16731 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16732 | .flags = GENL_UNS_ADMIN_PERM, |
16733 | .doit = nl80211_start_ap, |
16734 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
16735 | NL80211_FLAG_MLO_VALID_LINK_ID), |
16736 | }, |
16737 | { |
16738 | .cmd = NL80211_CMD_STOP_AP, |
16739 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16740 | .flags = GENL_UNS_ADMIN_PERM, |
16741 | .doit = nl80211_stop_ap, |
16742 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
16743 | NL80211_FLAG_MLO_VALID_LINK_ID), |
16744 | }, |
16745 | { |
16746 | .cmd = NL80211_CMD_GET_STATION, |
16747 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16748 | .doit = nl80211_get_station, |
16749 | .dumpit = nl80211_dump_station, |
16750 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV), |
16751 | }, |
16752 | { |
16753 | .cmd = NL80211_CMD_SET_STATION, |
16754 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16755 | .doit = nl80211_set_station, |
16756 | .flags = GENL_UNS_ADMIN_PERM, |
16757 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
16758 | }, |
16759 | { |
16760 | .cmd = NL80211_CMD_NEW_STATION, |
16761 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16762 | .doit = nl80211_new_station, |
16763 | .flags = GENL_UNS_ADMIN_PERM, |
16764 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
16765 | }, |
16766 | { |
16767 | .cmd = NL80211_CMD_DEL_STATION, |
16768 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16769 | .doit = nl80211_del_station, |
16770 | .flags = GENL_UNS_ADMIN_PERM, |
16771 | /* cannot use NL80211_FLAG_MLO_VALID_LINK_ID, depends on |
16772 | * whether MAC address is passed or not. If MAC address is |
16773 | * passed, then even during MLO, link ID is not required. |
16774 | */ |
16775 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
16776 | }, |
16777 | { |
16778 | .cmd = NL80211_CMD_GET_MPATH, |
16779 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16780 | .doit = nl80211_get_mpath, |
16781 | .dumpit = nl80211_dump_mpath, |
16782 | .flags = GENL_UNS_ADMIN_PERM, |
16783 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
16784 | }, |
16785 | { |
16786 | .cmd = NL80211_CMD_GET_MPP, |
16787 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16788 | .doit = nl80211_get_mpp, |
16789 | .dumpit = nl80211_dump_mpp, |
16790 | .flags = GENL_UNS_ADMIN_PERM, |
16791 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
16792 | }, |
16793 | { |
16794 | .cmd = NL80211_CMD_SET_MPATH, |
16795 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16796 | .doit = nl80211_set_mpath, |
16797 | .flags = GENL_UNS_ADMIN_PERM, |
16798 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
16799 | }, |
16800 | { |
16801 | .cmd = NL80211_CMD_NEW_MPATH, |
16802 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16803 | .doit = nl80211_new_mpath, |
16804 | .flags = GENL_UNS_ADMIN_PERM, |
16805 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
16806 | }, |
16807 | { |
16808 | .cmd = NL80211_CMD_DEL_MPATH, |
16809 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16810 | .doit = nl80211_del_mpath, |
16811 | .flags = GENL_UNS_ADMIN_PERM, |
16812 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
16813 | }, |
16814 | { |
16815 | .cmd = NL80211_CMD_SET_BSS, |
16816 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16817 | .doit = nl80211_set_bss, |
16818 | .flags = GENL_UNS_ADMIN_PERM, |
16819 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
16820 | NL80211_FLAG_MLO_VALID_LINK_ID), |
16821 | }, |
16822 | { |
16823 | .cmd = NL80211_CMD_GET_REG, |
16824 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16825 | .doit = nl80211_get_reg_do, |
16826 | .dumpit = nl80211_get_reg_dump, |
16827 | /* can be retrieved by unprivileged users */ |
16828 | }, |
16829 | #ifdef CONFIG_CFG80211_CRDA_SUPPORT |
16830 | { |
16831 | .cmd = NL80211_CMD_SET_REG, |
16832 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16833 | .doit = nl80211_set_reg, |
16834 | .flags = GENL_ADMIN_PERM, |
16835 | }, |
16836 | #endif |
16837 | { |
16838 | .cmd = NL80211_CMD_REQ_SET_REG, |
16839 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16840 | .doit = nl80211_req_set_reg, |
16841 | .flags = GENL_ADMIN_PERM, |
16842 | }, |
16843 | { |
16844 | .cmd = NL80211_CMD_RELOAD_REGDB, |
16845 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16846 | .doit = nl80211_reload_regdb, |
16847 | .flags = GENL_ADMIN_PERM, |
16848 | }, |
16849 | { |
16850 | .cmd = NL80211_CMD_GET_MESH_CONFIG, |
16851 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16852 | .doit = nl80211_get_mesh_config, |
16853 | /* can be retrieved by unprivileged users */ |
16854 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
16855 | }, |
16856 | { |
16857 | .cmd = NL80211_CMD_SET_MESH_CONFIG, |
16858 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16859 | .doit = nl80211_update_mesh_config, |
16860 | .flags = GENL_UNS_ADMIN_PERM, |
16861 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
16862 | }, |
16863 | { |
16864 | .cmd = NL80211_CMD_TRIGGER_SCAN, |
16865 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16866 | .doit = nl80211_trigger_scan, |
16867 | .flags = GENL_UNS_ADMIN_PERM, |
16868 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), |
16869 | }, |
16870 | { |
16871 | .cmd = NL80211_CMD_ABORT_SCAN, |
16872 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16873 | .doit = nl80211_abort_scan, |
16874 | .flags = GENL_UNS_ADMIN_PERM, |
16875 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), |
16876 | }, |
16877 | { |
16878 | .cmd = NL80211_CMD_GET_SCAN, |
16879 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16880 | .dumpit = nl80211_dump_scan, |
16881 | }, |
16882 | { |
16883 | .cmd = NL80211_CMD_START_SCHED_SCAN, |
16884 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16885 | .doit = nl80211_start_sched_scan, |
16886 | .flags = GENL_UNS_ADMIN_PERM, |
16887 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
16888 | }, |
16889 | { |
16890 | .cmd = NL80211_CMD_STOP_SCHED_SCAN, |
16891 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16892 | .doit = nl80211_stop_sched_scan, |
16893 | .flags = GENL_UNS_ADMIN_PERM, |
16894 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
16895 | }, |
16896 | { |
16897 | .cmd = NL80211_CMD_AUTHENTICATE, |
16898 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16899 | .doit = nl80211_authenticate, |
16900 | .flags = GENL_UNS_ADMIN_PERM, |
16901 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
16902 | NL80211_FLAG_CLEAR_SKB), |
16903 | }, |
16904 | { |
16905 | .cmd = NL80211_CMD_ASSOCIATE, |
16906 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16907 | .doit = nl80211_associate, |
16908 | .flags = GENL_UNS_ADMIN_PERM, |
16909 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
16910 | NL80211_FLAG_CLEAR_SKB), |
16911 | }, |
16912 | { |
16913 | .cmd = NL80211_CMD_DEAUTHENTICATE, |
16914 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16915 | .doit = nl80211_deauthenticate, |
16916 | .flags = GENL_UNS_ADMIN_PERM, |
16917 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
16918 | }, |
16919 | { |
16920 | .cmd = NL80211_CMD_DISASSOCIATE, |
16921 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16922 | .doit = nl80211_disassociate, |
16923 | .flags = GENL_UNS_ADMIN_PERM, |
16924 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
16925 | }, |
16926 | { |
16927 | .cmd = NL80211_CMD_JOIN_IBSS, |
16928 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16929 | .doit = nl80211_join_ibss, |
16930 | .flags = GENL_UNS_ADMIN_PERM, |
16931 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
16932 | }, |
16933 | { |
16934 | .cmd = NL80211_CMD_LEAVE_IBSS, |
16935 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16936 | .doit = nl80211_leave_ibss, |
16937 | .flags = GENL_UNS_ADMIN_PERM, |
16938 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
16939 | }, |
16940 | #ifdef CONFIG_NL80211_TESTMODE |
16941 | { |
16942 | .cmd = NL80211_CMD_TESTMODE, |
16943 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16944 | .doit = nl80211_testmode_do, |
16945 | .dumpit = nl80211_testmode_dump, |
16946 | .flags = GENL_UNS_ADMIN_PERM, |
16947 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY), |
16948 | }, |
16949 | #endif |
16950 | { |
16951 | .cmd = NL80211_CMD_CONNECT, |
16952 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16953 | .doit = nl80211_connect, |
16954 | .flags = GENL_UNS_ADMIN_PERM, |
16955 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
16956 | NL80211_FLAG_CLEAR_SKB), |
16957 | }, |
16958 | { |
16959 | .cmd = NL80211_CMD_UPDATE_CONNECT_PARAMS, |
16960 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16961 | .doit = nl80211_update_connect_params, |
16962 | .flags = GENL_ADMIN_PERM, |
16963 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
16964 | NL80211_FLAG_CLEAR_SKB), |
16965 | }, |
16966 | { |
16967 | .cmd = NL80211_CMD_DISCONNECT, |
16968 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16969 | .doit = nl80211_disconnect, |
16970 | .flags = GENL_UNS_ADMIN_PERM, |
16971 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
16972 | }, |
16973 | { |
16974 | .cmd = NL80211_CMD_SET_WIPHY_NETNS, |
16975 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16976 | .doit = nl80211_wiphy_netns, |
16977 | .flags = GENL_UNS_ADMIN_PERM, |
16978 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY | |
16979 | NL80211_FLAG_NEED_RTNL | |
16980 | NL80211_FLAG_NO_WIPHY_MTX), |
16981 | }, |
16982 | { |
16983 | .cmd = NL80211_CMD_GET_SURVEY, |
16984 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16985 | .dumpit = nl80211_dump_survey, |
16986 | }, |
16987 | { |
16988 | .cmd = NL80211_CMD_SET_PMKSA, |
16989 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16990 | .doit = nl80211_set_pmksa, |
16991 | .flags = GENL_UNS_ADMIN_PERM, |
16992 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
16993 | NL80211_FLAG_CLEAR_SKB), |
16994 | }, |
16995 | { |
16996 | .cmd = NL80211_CMD_DEL_PMKSA, |
16997 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
16998 | .doit = nl80211_del_pmksa, |
16999 | .flags = GENL_UNS_ADMIN_PERM, |
17000 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17001 | }, |
17002 | { |
17003 | .cmd = NL80211_CMD_FLUSH_PMKSA, |
17004 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17005 | .doit = nl80211_flush_pmksa, |
17006 | .flags = GENL_UNS_ADMIN_PERM, |
17007 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17008 | }, |
17009 | { |
17010 | .cmd = NL80211_CMD_REMAIN_ON_CHANNEL, |
17011 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17012 | .doit = nl80211_remain_on_channel, |
17013 | .flags = GENL_UNS_ADMIN_PERM, |
17014 | /* FIXME: requiring a link ID here is probably not good */ |
17015 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP | |
17016 | NL80211_FLAG_MLO_VALID_LINK_ID), |
17017 | }, |
17018 | { |
17019 | .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, |
17020 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17021 | .doit = nl80211_cancel_remain_on_channel, |
17022 | .flags = GENL_UNS_ADMIN_PERM, |
17023 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), |
17024 | }, |
17025 | { |
17026 | .cmd = NL80211_CMD_SET_TX_BITRATE_MASK, |
17027 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17028 | .doit = nl80211_set_tx_bitrate_mask, |
17029 | .flags = GENL_UNS_ADMIN_PERM, |
17030 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV | |
17031 | NL80211_FLAG_MLO_VALID_LINK_ID), |
17032 | }, |
17033 | { |
17034 | .cmd = NL80211_CMD_REGISTER_FRAME, |
17035 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17036 | .doit = nl80211_register_mgmt, |
17037 | .flags = GENL_UNS_ADMIN_PERM, |
17038 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV), |
17039 | }, |
17040 | { |
17041 | .cmd = NL80211_CMD_FRAME, |
17042 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17043 | .doit = nl80211_tx_mgmt, |
17044 | .flags = GENL_UNS_ADMIN_PERM, |
17045 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), |
17046 | }, |
17047 | { |
17048 | .cmd = NL80211_CMD_FRAME_WAIT_CANCEL, |
17049 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17050 | .doit = nl80211_tx_mgmt_cancel_wait, |
17051 | .flags = GENL_UNS_ADMIN_PERM, |
17052 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), |
17053 | }, |
17054 | { |
17055 | .cmd = NL80211_CMD_SET_POWER_SAVE, |
17056 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17057 | .doit = nl80211_set_power_save, |
17058 | .flags = GENL_UNS_ADMIN_PERM, |
17059 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV), |
17060 | }, |
17061 | { |
17062 | .cmd = NL80211_CMD_GET_POWER_SAVE, |
17063 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17064 | .doit = nl80211_get_power_save, |
17065 | /* can be retrieved by unprivileged users */ |
17066 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV), |
17067 | }, |
17068 | { |
17069 | .cmd = NL80211_CMD_SET_CQM, |
17070 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17071 | .doit = nl80211_set_cqm, |
17072 | .flags = GENL_UNS_ADMIN_PERM, |
17073 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV), |
17074 | }, |
17075 | { |
17076 | .cmd = NL80211_CMD_SET_CHANNEL, |
17077 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17078 | .doit = nl80211_set_channel, |
17079 | .flags = GENL_UNS_ADMIN_PERM, |
17080 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV | |
17081 | NL80211_FLAG_MLO_VALID_LINK_ID), |
17082 | }, |
17083 | { |
17084 | .cmd = NL80211_CMD_JOIN_MESH, |
17085 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17086 | .doit = nl80211_join_mesh, |
17087 | .flags = GENL_UNS_ADMIN_PERM, |
17088 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17089 | }, |
17090 | { |
17091 | .cmd = NL80211_CMD_LEAVE_MESH, |
17092 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17093 | .doit = nl80211_leave_mesh, |
17094 | .flags = GENL_UNS_ADMIN_PERM, |
17095 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17096 | }, |
17097 | { |
17098 | .cmd = NL80211_CMD_JOIN_OCB, |
17099 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17100 | .doit = nl80211_join_ocb, |
17101 | .flags = GENL_UNS_ADMIN_PERM, |
17102 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17103 | }, |
17104 | { |
17105 | .cmd = NL80211_CMD_LEAVE_OCB, |
17106 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17107 | .doit = nl80211_leave_ocb, |
17108 | .flags = GENL_UNS_ADMIN_PERM, |
17109 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17110 | }, |
17111 | #ifdef CONFIG_PM |
17112 | { |
17113 | .cmd = NL80211_CMD_GET_WOWLAN, |
17114 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17115 | .doit = nl80211_get_wowlan, |
17116 | /* can be retrieved by unprivileged users */ |
17117 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY), |
17118 | }, |
17119 | { |
17120 | .cmd = NL80211_CMD_SET_WOWLAN, |
17121 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17122 | .doit = nl80211_set_wowlan, |
17123 | .flags = GENL_UNS_ADMIN_PERM, |
17124 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY), |
17125 | }, |
17126 | #endif |
17127 | { |
17128 | .cmd = NL80211_CMD_SET_REKEY_OFFLOAD, |
17129 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17130 | .doit = nl80211_set_rekey_data, |
17131 | .flags = GENL_UNS_ADMIN_PERM, |
17132 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
17133 | NL80211_FLAG_CLEAR_SKB), |
17134 | }, |
17135 | { |
17136 | .cmd = NL80211_CMD_TDLS_MGMT, |
17137 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17138 | .doit = nl80211_tdls_mgmt, |
17139 | .flags = GENL_UNS_ADMIN_PERM, |
17140 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
17141 | NL80211_FLAG_MLO_VALID_LINK_ID), |
17142 | }, |
17143 | { |
17144 | .cmd = NL80211_CMD_TDLS_OPER, |
17145 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17146 | .doit = nl80211_tdls_oper, |
17147 | .flags = GENL_UNS_ADMIN_PERM, |
17148 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17149 | }, |
17150 | { |
17151 | .cmd = NL80211_CMD_UNEXPECTED_FRAME, |
17152 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17153 | .doit = nl80211_register_unexpected_frame, |
17154 | .flags = GENL_UNS_ADMIN_PERM, |
17155 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV), |
17156 | }, |
17157 | { |
17158 | .cmd = NL80211_CMD_PROBE_CLIENT, |
17159 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17160 | .doit = nl80211_probe_client, |
17161 | .flags = GENL_UNS_ADMIN_PERM, |
17162 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17163 | }, |
17164 | { |
17165 | .cmd = NL80211_CMD_REGISTER_BEACONS, |
17166 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17167 | .doit = nl80211_register_beacons, |
17168 | .flags = GENL_UNS_ADMIN_PERM, |
17169 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY), |
17170 | }, |
17171 | { |
17172 | .cmd = NL80211_CMD_SET_NOACK_MAP, |
17173 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17174 | .doit = nl80211_set_noack_map, |
17175 | .flags = GENL_UNS_ADMIN_PERM, |
17176 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV), |
17177 | }, |
17178 | { |
17179 | .cmd = NL80211_CMD_START_P2P_DEVICE, |
17180 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17181 | .doit = nl80211_start_p2p_device, |
17182 | .flags = GENL_UNS_ADMIN_PERM, |
17183 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV | |
17184 | NL80211_FLAG_NEED_RTNL), |
17185 | }, |
17186 | { |
17187 | .cmd = NL80211_CMD_STOP_P2P_DEVICE, |
17188 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17189 | .doit = nl80211_stop_p2p_device, |
17190 | .flags = GENL_UNS_ADMIN_PERM, |
17191 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP | |
17192 | NL80211_FLAG_NEED_RTNL), |
17193 | }, |
17194 | { |
17195 | .cmd = NL80211_CMD_START_NAN, |
17196 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17197 | .doit = nl80211_start_nan, |
17198 | .flags = GENL_ADMIN_PERM, |
17199 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV | |
17200 | NL80211_FLAG_NEED_RTNL), |
17201 | }, |
17202 | { |
17203 | .cmd = NL80211_CMD_STOP_NAN, |
17204 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17205 | .doit = nl80211_stop_nan, |
17206 | .flags = GENL_ADMIN_PERM, |
17207 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP | |
17208 | NL80211_FLAG_NEED_RTNL), |
17209 | }, |
17210 | { |
17211 | .cmd = NL80211_CMD_ADD_NAN_FUNCTION, |
17212 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17213 | .doit = nl80211_nan_add_func, |
17214 | .flags = GENL_ADMIN_PERM, |
17215 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), |
17216 | }, |
17217 | { |
17218 | .cmd = NL80211_CMD_DEL_NAN_FUNCTION, |
17219 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17220 | .doit = nl80211_nan_del_func, |
17221 | .flags = GENL_ADMIN_PERM, |
17222 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), |
17223 | }, |
17224 | { |
17225 | .cmd = NL80211_CMD_CHANGE_NAN_CONFIG, |
17226 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17227 | .doit = nl80211_nan_change_config, |
17228 | .flags = GENL_ADMIN_PERM, |
17229 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), |
17230 | }, |
17231 | { |
17232 | .cmd = NL80211_CMD_SET_MCAST_RATE, |
17233 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17234 | .doit = nl80211_set_mcast_rate, |
17235 | .flags = GENL_UNS_ADMIN_PERM, |
17236 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV), |
17237 | }, |
17238 | { |
17239 | .cmd = NL80211_CMD_SET_MAC_ACL, |
17240 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17241 | .doit = nl80211_set_mac_acl, |
17242 | .flags = GENL_UNS_ADMIN_PERM, |
17243 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV | |
17244 | NL80211_FLAG_MLO_UNSUPPORTED), |
17245 | }, |
17246 | { |
17247 | .cmd = NL80211_CMD_RADAR_DETECT, |
17248 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17249 | .doit = nl80211_start_radar_detection, |
17250 | .flags = GENL_UNS_ADMIN_PERM, |
17251 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
17252 | NL80211_FLAG_NO_WIPHY_MTX | |
17253 | NL80211_FLAG_MLO_UNSUPPORTED), |
17254 | }, |
17255 | { |
17256 | .cmd = NL80211_CMD_GET_PROTOCOL_FEATURES, |
17257 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17258 | .doit = nl80211_get_protocol_features, |
17259 | }, |
17260 | { |
17261 | .cmd = NL80211_CMD_UPDATE_FT_IES, |
17262 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17263 | .doit = nl80211_update_ft_ies, |
17264 | .flags = GENL_UNS_ADMIN_PERM, |
17265 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17266 | }, |
17267 | { |
17268 | .cmd = NL80211_CMD_CRIT_PROTOCOL_START, |
17269 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17270 | .doit = nl80211_crit_protocol_start, |
17271 | .flags = GENL_UNS_ADMIN_PERM, |
17272 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), |
17273 | }, |
17274 | { |
17275 | .cmd = NL80211_CMD_CRIT_PROTOCOL_STOP, |
17276 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17277 | .doit = nl80211_crit_protocol_stop, |
17278 | .flags = GENL_UNS_ADMIN_PERM, |
17279 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), |
17280 | }, |
17281 | { |
17282 | .cmd = NL80211_CMD_GET_COALESCE, |
17283 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17284 | .doit = nl80211_get_coalesce, |
17285 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY), |
17286 | }, |
17287 | { |
17288 | .cmd = NL80211_CMD_SET_COALESCE, |
17289 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17290 | .doit = nl80211_set_coalesce, |
17291 | .flags = GENL_UNS_ADMIN_PERM, |
17292 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY), |
17293 | }, |
17294 | { |
17295 | .cmd = NL80211_CMD_CHANNEL_SWITCH, |
17296 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17297 | .doit = nl80211_channel_switch, |
17298 | .flags = GENL_UNS_ADMIN_PERM, |
17299 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
17300 | NL80211_FLAG_MLO_VALID_LINK_ID), |
17301 | }, |
17302 | { |
17303 | .cmd = NL80211_CMD_VENDOR, |
17304 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17305 | .doit = nl80211_vendor_cmd, |
17306 | .dumpit = nl80211_vendor_cmd_dump, |
17307 | .flags = GENL_UNS_ADMIN_PERM, |
17308 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY | |
17309 | NL80211_FLAG_CLEAR_SKB), |
17310 | }, |
17311 | { |
17312 | .cmd = NL80211_CMD_SET_QOS_MAP, |
17313 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17314 | .doit = nl80211_set_qos_map, |
17315 | .flags = GENL_UNS_ADMIN_PERM, |
17316 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17317 | }, |
17318 | { |
17319 | .cmd = NL80211_CMD_ADD_TX_TS, |
17320 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17321 | .doit = nl80211_add_tx_ts, |
17322 | .flags = GENL_UNS_ADMIN_PERM, |
17323 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
17324 | NL80211_FLAG_MLO_UNSUPPORTED), |
17325 | }, |
17326 | { |
17327 | .cmd = NL80211_CMD_DEL_TX_TS, |
17328 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17329 | .doit = nl80211_del_tx_ts, |
17330 | .flags = GENL_UNS_ADMIN_PERM, |
17331 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17332 | }, |
17333 | { |
17334 | .cmd = NL80211_CMD_TDLS_CHANNEL_SWITCH, |
17335 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17336 | .doit = nl80211_tdls_channel_switch, |
17337 | .flags = GENL_UNS_ADMIN_PERM, |
17338 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17339 | }, |
17340 | { |
17341 | .cmd = NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH, |
17342 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17343 | .doit = nl80211_tdls_cancel_channel_switch, |
17344 | .flags = GENL_UNS_ADMIN_PERM, |
17345 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17346 | }, |
17347 | { |
17348 | .cmd = NL80211_CMD_SET_MULTICAST_TO_UNICAST, |
17349 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17350 | .doit = nl80211_set_multicast_to_unicast, |
17351 | .flags = GENL_UNS_ADMIN_PERM, |
17352 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV), |
17353 | }, |
17354 | { |
17355 | .cmd = NL80211_CMD_SET_PMK, |
17356 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17357 | .doit = nl80211_set_pmk, |
17358 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
17359 | NL80211_FLAG_CLEAR_SKB), |
17360 | }, |
17361 | { |
17362 | .cmd = NL80211_CMD_DEL_PMK, |
17363 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17364 | .doit = nl80211_del_pmk, |
17365 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17366 | }, |
17367 | { |
17368 | .cmd = NL80211_CMD_EXTERNAL_AUTH, |
17369 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17370 | .doit = nl80211_external_auth, |
17371 | .flags = GENL_ADMIN_PERM, |
17372 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17373 | }, |
17374 | { |
17375 | .cmd = NL80211_CMD_CONTROL_PORT_FRAME, |
17376 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17377 | .doit = nl80211_tx_control_port, |
17378 | .flags = GENL_UNS_ADMIN_PERM, |
17379 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17380 | }, |
17381 | { |
17382 | .cmd = NL80211_CMD_GET_FTM_RESPONDER_STATS, |
17383 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17384 | .doit = nl80211_get_ftm_responder_stats, |
17385 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV | |
17386 | NL80211_FLAG_MLO_VALID_LINK_ID), |
17387 | }, |
17388 | { |
17389 | .cmd = NL80211_CMD_PEER_MEASUREMENT_START, |
17390 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17391 | .doit = nl80211_pmsr_start, |
17392 | .flags = GENL_UNS_ADMIN_PERM, |
17393 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WDEV_UP), |
17394 | }, |
17395 | { |
17396 | .cmd = NL80211_CMD_NOTIFY_RADAR, |
17397 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17398 | .doit = nl80211_notify_radar_detection, |
17399 | .flags = GENL_UNS_ADMIN_PERM, |
17400 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17401 | }, |
17402 | { |
17403 | .cmd = NL80211_CMD_UPDATE_OWE_INFO, |
17404 | .doit = nl80211_update_owe_info, |
17405 | .flags = GENL_ADMIN_PERM, |
17406 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17407 | }, |
17408 | { |
17409 | .cmd = NL80211_CMD_PROBE_MESH_LINK, |
17410 | .doit = nl80211_probe_mesh_link, |
17411 | .flags = GENL_UNS_ADMIN_PERM, |
17412 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17413 | }, |
17414 | { |
17415 | .cmd = NL80211_CMD_SET_TID_CONFIG, |
17416 | .doit = nl80211_set_tid_config, |
17417 | .flags = GENL_UNS_ADMIN_PERM, |
17418 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV | |
17419 | NL80211_FLAG_MLO_VALID_LINK_ID), |
17420 | }, |
17421 | { |
17422 | .cmd = NL80211_CMD_SET_SAR_SPECS, |
17423 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17424 | .doit = nl80211_set_sar_specs, |
17425 | .flags = GENL_UNS_ADMIN_PERM, |
17426 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_WIPHY | |
17427 | NL80211_FLAG_NEED_RTNL), |
17428 | }, |
17429 | { |
17430 | .cmd = NL80211_CMD_COLOR_CHANGE_REQUEST, |
17431 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17432 | .doit = nl80211_color_change, |
17433 | .flags = GENL_UNS_ADMIN_PERM, |
17434 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17435 | }, |
17436 | { |
17437 | .cmd = NL80211_CMD_SET_FILS_AAD, |
17438 | .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, |
17439 | .doit = nl80211_set_fils_aad, |
17440 | .flags = GENL_UNS_ADMIN_PERM, |
17441 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17442 | }, |
17443 | { |
17444 | .cmd = NL80211_CMD_ADD_LINK, |
17445 | .doit = nl80211_add_link, |
17446 | .flags = GENL_UNS_ADMIN_PERM, |
17447 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17448 | }, |
17449 | { |
17450 | .cmd = NL80211_CMD_REMOVE_LINK, |
17451 | .doit = nl80211_remove_link, |
17452 | .flags = GENL_UNS_ADMIN_PERM, |
17453 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
17454 | NL80211_FLAG_MLO_VALID_LINK_ID), |
17455 | }, |
17456 | { |
17457 | .cmd = NL80211_CMD_ADD_LINK_STA, |
17458 | .doit = nl80211_add_link_station, |
17459 | .flags = GENL_UNS_ADMIN_PERM, |
17460 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
17461 | NL80211_FLAG_MLO_VALID_LINK_ID), |
17462 | }, |
17463 | { |
17464 | .cmd = NL80211_CMD_MODIFY_LINK_STA, |
17465 | .doit = nl80211_modify_link_station, |
17466 | .flags = GENL_UNS_ADMIN_PERM, |
17467 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
17468 | NL80211_FLAG_MLO_VALID_LINK_ID), |
17469 | }, |
17470 | { |
17471 | .cmd = NL80211_CMD_REMOVE_LINK_STA, |
17472 | .doit = nl80211_remove_link_station, |
17473 | .flags = GENL_UNS_ADMIN_PERM, |
17474 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP | |
17475 | NL80211_FLAG_MLO_VALID_LINK_ID), |
17476 | }, |
17477 | { |
17478 | .cmd = NL80211_CMD_SET_HW_TIMESTAMP, |
17479 | .doit = nl80211_set_hw_timestamp, |
17480 | .flags = GENL_UNS_ADMIN_PERM, |
17481 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17482 | }, |
17483 | { |
17484 | .cmd = NL80211_CMD_SET_TID_TO_LINK_MAPPING, |
17485 | .doit = nl80211_set_ttlm, |
17486 | .flags = GENL_UNS_ADMIN_PERM, |
17487 | .internal_flags = IFLAGS(NL80211_FLAG_NEED_NETDEV_UP), |
17488 | }, |
17489 | }; |
17490 | |
17491 | static struct genl_family nl80211_fam __ro_after_init = { |
17492 | .name = NL80211_GENL_NAME, /* have users key off the name instead */ |
17493 | .hdrsize = 0, /* no private header */ |
17494 | .version = 1, /* no particular meaning now */ |
17495 | .maxattr = NL80211_ATTR_MAX, |
17496 | .policy = nl80211_policy, |
17497 | .netnsok = true, |
17498 | .pre_doit = nl80211_pre_doit, |
17499 | .post_doit = nl80211_post_doit, |
17500 | .module = THIS_MODULE, |
17501 | .ops = nl80211_ops, |
17502 | .n_ops = ARRAY_SIZE(nl80211_ops), |
17503 | .small_ops = nl80211_small_ops, |
17504 | .n_small_ops = ARRAY_SIZE(nl80211_small_ops), |
17505 | .resv_start_op = NL80211_CMD_REMOVE_LINK_STA + 1, |
17506 | .mcgrps = nl80211_mcgrps, |
17507 | .n_mcgrps = ARRAY_SIZE(nl80211_mcgrps), |
17508 | .parallel_ops = true, |
17509 | }; |
17510 | |
17511 | /* notification functions */ |
17512 | |
17513 | void nl80211_notify_wiphy(struct cfg80211_registered_device *rdev, |
17514 | enum nl80211_commands cmd) |
17515 | { |
17516 | struct sk_buff *msg; |
17517 | struct nl80211_dump_wiphy_state state = {}; |
17518 | |
17519 | WARN_ON(cmd != NL80211_CMD_NEW_WIPHY && |
17520 | cmd != NL80211_CMD_DEL_WIPHY); |
17521 | |
17522 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
17523 | if (!msg) |
17524 | return; |
17525 | |
17526 | if (nl80211_send_wiphy(rdev, cmd, msg, portid: 0, seq: 0, flags: 0, state: &state) < 0) { |
17527 | nlmsg_free(skb: msg); |
17528 | return; |
17529 | } |
17530 | |
17531 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
17532 | group: NL80211_MCGRP_CONFIG, GFP_KERNEL); |
17533 | } |
17534 | |
17535 | void nl80211_notify_iface(struct cfg80211_registered_device *rdev, |
17536 | struct wireless_dev *wdev, |
17537 | enum nl80211_commands cmd) |
17538 | { |
17539 | struct sk_buff *msg; |
17540 | |
17541 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
17542 | if (!msg) |
17543 | return; |
17544 | |
17545 | if (nl80211_send_iface(msg, portid: 0, seq: 0, flags: 0, rdev, wdev, cmd) < 0) { |
17546 | nlmsg_free(skb: msg); |
17547 | return; |
17548 | } |
17549 | |
17550 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
17551 | group: NL80211_MCGRP_CONFIG, GFP_KERNEL); |
17552 | } |
17553 | |
17554 | static int nl80211_add_scan_req(struct sk_buff *msg, |
17555 | struct cfg80211_registered_device *rdev) |
17556 | { |
17557 | struct cfg80211_scan_request *req = rdev->scan_req; |
17558 | struct nlattr *nest; |
17559 | int i; |
17560 | struct cfg80211_scan_info *info; |
17561 | |
17562 | if (WARN_ON(!req)) |
17563 | return 0; |
17564 | |
17565 | nest = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_SCAN_SSIDS); |
17566 | if (!nest) |
17567 | goto nla_put_failure; |
17568 | for (i = 0; i < req->n_ssids; i++) { |
17569 | if (nla_put(skb: msg, attrtype: i, attrlen: req->ssids[i].ssid_len, data: req->ssids[i].ssid)) |
17570 | goto nla_put_failure; |
17571 | } |
17572 | nla_nest_end(skb: msg, start: nest); |
17573 | |
17574 | if (req->flags & NL80211_SCAN_FLAG_FREQ_KHZ) { |
17575 | nest = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_SCAN_FREQ_KHZ); |
17576 | if (!nest) |
17577 | goto nla_put_failure; |
17578 | for (i = 0; i < req->n_channels; i++) { |
17579 | if (nla_put_u32(skb: msg, attrtype: i, |
17580 | value: ieee80211_channel_to_khz(chan: req->channels[i]))) |
17581 | goto nla_put_failure; |
17582 | } |
17583 | nla_nest_end(skb: msg, start: nest); |
17584 | } else { |
17585 | nest = nla_nest_start_noflag(skb: msg, |
17586 | attrtype: NL80211_ATTR_SCAN_FREQUENCIES); |
17587 | if (!nest) |
17588 | goto nla_put_failure; |
17589 | for (i = 0; i < req->n_channels; i++) { |
17590 | if (nla_put_u32(skb: msg, attrtype: i, value: req->channels[i]->center_freq)) |
17591 | goto nla_put_failure; |
17592 | } |
17593 | nla_nest_end(skb: msg, start: nest); |
17594 | } |
17595 | |
17596 | if (req->ie && |
17597 | nla_put(skb: msg, NL80211_ATTR_IE, attrlen: req->ie_len, data: req->ie)) |
17598 | goto nla_put_failure; |
17599 | |
17600 | if (req->flags && |
17601 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_SCAN_FLAGS, value: req->flags)) |
17602 | goto nla_put_failure; |
17603 | |
17604 | info = rdev->int_scan_req ? &rdev->int_scan_req->info : |
17605 | &rdev->scan_req->info; |
17606 | if (info->scan_start_tsf && |
17607 | (nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_SCAN_START_TIME_TSF, |
17608 | value: info->scan_start_tsf, padattr: NL80211_BSS_PAD) || |
17609 | nla_put(skb: msg, attrtype: NL80211_ATTR_SCAN_START_TIME_TSF_BSSID, ETH_ALEN, |
17610 | data: info->tsf_bssid))) |
17611 | goto nla_put_failure; |
17612 | |
17613 | return 0; |
17614 | nla_put_failure: |
17615 | return -ENOBUFS; |
17616 | } |
17617 | |
17618 | static int nl80211_prep_scan_msg(struct sk_buff *msg, |
17619 | struct cfg80211_registered_device *rdev, |
17620 | struct wireless_dev *wdev, |
17621 | u32 portid, u32 seq, int flags, |
17622 | u32 cmd) |
17623 | { |
17624 | void *hdr; |
17625 | |
17626 | hdr = nl80211hdr_put(skb: msg, portid, seq, flags, cmd); |
17627 | if (!hdr) |
17628 | return -1; |
17629 | |
17630 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
17631 | (wdev->netdev && nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, |
17632 | value: wdev->netdev->ifindex)) || |
17633 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), |
17634 | padattr: NL80211_ATTR_PAD)) |
17635 | goto nla_put_failure; |
17636 | |
17637 | /* ignore errors and send incomplete event anyway */ |
17638 | nl80211_add_scan_req(msg, rdev); |
17639 | |
17640 | genlmsg_end(skb: msg, hdr); |
17641 | return 0; |
17642 | |
17643 | nla_put_failure: |
17644 | genlmsg_cancel(skb: msg, hdr); |
17645 | return -EMSGSIZE; |
17646 | } |
17647 | |
17648 | static int |
17649 | nl80211_prep_sched_scan_msg(struct sk_buff *msg, |
17650 | struct cfg80211_sched_scan_request *req, u32 cmd) |
17651 | { |
17652 | void *hdr; |
17653 | |
17654 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd); |
17655 | if (!hdr) |
17656 | return -1; |
17657 | |
17658 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, |
17659 | value: wiphy_to_rdev(wiphy: req->wiphy)->wiphy_idx) || |
17660 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: req->dev->ifindex) || |
17661 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: req->reqid, |
17662 | padattr: NL80211_ATTR_PAD)) |
17663 | goto nla_put_failure; |
17664 | |
17665 | genlmsg_end(skb: msg, hdr); |
17666 | return 0; |
17667 | |
17668 | nla_put_failure: |
17669 | genlmsg_cancel(skb: msg, hdr); |
17670 | return -EMSGSIZE; |
17671 | } |
17672 | |
17673 | void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, |
17674 | struct wireless_dev *wdev) |
17675 | { |
17676 | struct sk_buff *msg; |
17677 | |
17678 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
17679 | if (!msg) |
17680 | return; |
17681 | |
17682 | if (nl80211_prep_scan_msg(msg, rdev, wdev, portid: 0, seq: 0, flags: 0, |
17683 | cmd: NL80211_CMD_TRIGGER_SCAN) < 0) { |
17684 | nlmsg_free(skb: msg); |
17685 | return; |
17686 | } |
17687 | |
17688 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
17689 | group: NL80211_MCGRP_SCAN, GFP_KERNEL); |
17690 | } |
17691 | |
17692 | struct sk_buff *nl80211_build_scan_msg(struct cfg80211_registered_device *rdev, |
17693 | struct wireless_dev *wdev, bool aborted) |
17694 | { |
17695 | struct sk_buff *msg; |
17696 | |
17697 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
17698 | if (!msg) |
17699 | return NULL; |
17700 | |
17701 | if (nl80211_prep_scan_msg(msg, rdev, wdev, portid: 0, seq: 0, flags: 0, |
17702 | cmd: aborted ? NL80211_CMD_SCAN_ABORTED : |
17703 | NL80211_CMD_NEW_SCAN_RESULTS) < 0) { |
17704 | nlmsg_free(skb: msg); |
17705 | return NULL; |
17706 | } |
17707 | |
17708 | return msg; |
17709 | } |
17710 | |
17711 | /* send message created by nl80211_build_scan_msg() */ |
17712 | void nl80211_send_scan_msg(struct cfg80211_registered_device *rdev, |
17713 | struct sk_buff *msg) |
17714 | { |
17715 | if (!msg) |
17716 | return; |
17717 | |
17718 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
17719 | group: NL80211_MCGRP_SCAN, GFP_KERNEL); |
17720 | } |
17721 | |
17722 | void nl80211_send_sched_scan(struct cfg80211_sched_scan_request *req, u32 cmd) |
17723 | { |
17724 | struct sk_buff *msg; |
17725 | |
17726 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
17727 | if (!msg) |
17728 | return; |
17729 | |
17730 | if (nl80211_prep_sched_scan_msg(msg, req, cmd) < 0) { |
17731 | nlmsg_free(skb: msg); |
17732 | return; |
17733 | } |
17734 | |
17735 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: req->wiphy), skb: msg, portid: 0, |
17736 | group: NL80211_MCGRP_SCAN, GFP_KERNEL); |
17737 | } |
17738 | |
17739 | static bool nl80211_reg_change_event_fill(struct sk_buff *msg, |
17740 | struct regulatory_request *request) |
17741 | { |
17742 | /* Userspace can always count this one always being set */ |
17743 | if (nla_put_u8(skb: msg, NL80211_ATTR_REG_INITIATOR, value: request->initiator)) |
17744 | goto nla_put_failure; |
17745 | |
17746 | if (request->alpha2[0] == '0' && request->alpha2[1] == '0') { |
17747 | if (nla_put_u8(skb: msg, NL80211_ATTR_REG_TYPE, |
17748 | value: NL80211_REGDOM_TYPE_WORLD)) |
17749 | goto nla_put_failure; |
17750 | } else if (request->alpha2[0] == '9' && request->alpha2[1] == '9') { |
17751 | if (nla_put_u8(skb: msg, NL80211_ATTR_REG_TYPE, |
17752 | value: NL80211_REGDOM_TYPE_CUSTOM_WORLD)) |
17753 | goto nla_put_failure; |
17754 | } else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') || |
17755 | request->intersect) { |
17756 | if (nla_put_u8(skb: msg, NL80211_ATTR_REG_TYPE, |
17757 | value: NL80211_REGDOM_TYPE_INTERSECTION)) |
17758 | goto nla_put_failure; |
17759 | } else { |
17760 | if (nla_put_u8(skb: msg, NL80211_ATTR_REG_TYPE, |
17761 | value: NL80211_REGDOM_TYPE_COUNTRY) || |
17762 | nla_put_string(skb: msg, attrtype: NL80211_ATTR_REG_ALPHA2, |
17763 | str: request->alpha2)) |
17764 | goto nla_put_failure; |
17765 | } |
17766 | |
17767 | if (request->wiphy_idx != WIPHY_IDX_INVALID) { |
17768 | struct wiphy *wiphy = wiphy_idx_to_wiphy(wiphy_idx: request->wiphy_idx); |
17769 | |
17770 | if (wiphy && |
17771 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: request->wiphy_idx)) |
17772 | goto nla_put_failure; |
17773 | |
17774 | if (wiphy && |
17775 | wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED && |
17776 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_WIPHY_SELF_MANAGED_REG)) |
17777 | goto nla_put_failure; |
17778 | } |
17779 | |
17780 | return true; |
17781 | |
17782 | nla_put_failure: |
17783 | return false; |
17784 | } |
17785 | |
17786 | /* |
17787 | * This can happen on global regulatory changes or device specific settings |
17788 | * based on custom regulatory domains. |
17789 | */ |
17790 | void nl80211_common_reg_change_event(enum nl80211_commands cmd_id, |
17791 | struct regulatory_request *request) |
17792 | { |
17793 | struct sk_buff *msg; |
17794 | void *hdr; |
17795 | |
17796 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
17797 | if (!msg) |
17798 | return; |
17799 | |
17800 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: cmd_id); |
17801 | if (!hdr) |
17802 | goto nla_put_failure; |
17803 | |
17804 | if (!nl80211_reg_change_event_fill(msg, request)) |
17805 | goto nla_put_failure; |
17806 | |
17807 | genlmsg_end(skb: msg, hdr); |
17808 | |
17809 | rcu_read_lock(); |
17810 | genlmsg_multicast_allns(family: &nl80211_fam, skb: msg, portid: 0, |
17811 | group: NL80211_MCGRP_REGULATORY, GFP_ATOMIC); |
17812 | rcu_read_unlock(); |
17813 | |
17814 | return; |
17815 | |
17816 | nla_put_failure: |
17817 | nlmsg_free(skb: msg); |
17818 | } |
17819 | |
17820 | struct nl80211_mlme_event { |
17821 | enum nl80211_commands cmd; |
17822 | const u8 *buf; |
17823 | size_t buf_len; |
17824 | int uapsd_queues; |
17825 | const u8 *req_ies; |
17826 | size_t req_ies_len; |
17827 | bool reconnect; |
17828 | }; |
17829 | |
17830 | static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, |
17831 | struct net_device *netdev, |
17832 | const struct nl80211_mlme_event *event, |
17833 | gfp_t gfp) |
17834 | { |
17835 | struct sk_buff *msg; |
17836 | void *hdr; |
17837 | |
17838 | msg = nlmsg_new(payload: 100 + event->buf_len + event->req_ies_len, flags: gfp); |
17839 | if (!msg) |
17840 | return; |
17841 | |
17842 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: event->cmd); |
17843 | if (!hdr) { |
17844 | nlmsg_free(skb: msg); |
17845 | return; |
17846 | } |
17847 | |
17848 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
17849 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || |
17850 | nla_put(skb: msg, NL80211_ATTR_FRAME, attrlen: event->buf_len, data: event->buf) || |
17851 | (event->req_ies && |
17852 | nla_put(skb: msg, attrtype: NL80211_ATTR_REQ_IE, attrlen: event->req_ies_len, |
17853 | data: event->req_ies))) |
17854 | goto nla_put_failure; |
17855 | |
17856 | if (event->reconnect && |
17857 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_RECONNECT_REQUESTED)) |
17858 | goto nla_put_failure; |
17859 | |
17860 | if (event->uapsd_queues >= 0) { |
17861 | struct nlattr *nla_wmm = |
17862 | nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_STA_WME); |
17863 | if (!nla_wmm) |
17864 | goto nla_put_failure; |
17865 | |
17866 | if (nla_put_u8(skb: msg, attrtype: NL80211_STA_WME_UAPSD_QUEUES, |
17867 | value: event->uapsd_queues)) |
17868 | goto nla_put_failure; |
17869 | |
17870 | nla_nest_end(skb: msg, start: nla_wmm); |
17871 | } |
17872 | |
17873 | genlmsg_end(skb: msg, hdr); |
17874 | |
17875 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
17876 | group: NL80211_MCGRP_MLME, flags: gfp); |
17877 | return; |
17878 | |
17879 | nla_put_failure: |
17880 | nlmsg_free(skb: msg); |
17881 | } |
17882 | |
17883 | void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev, |
17884 | struct net_device *netdev, const u8 *buf, |
17885 | size_t len, gfp_t gfp) |
17886 | { |
17887 | struct nl80211_mlme_event event = { |
17888 | .cmd = NL80211_CMD_AUTHENTICATE, |
17889 | .buf = buf, |
17890 | .buf_len = len, |
17891 | .uapsd_queues = -1, |
17892 | }; |
17893 | |
17894 | nl80211_send_mlme_event(rdev, netdev, event: &event, gfp); |
17895 | } |
17896 | |
17897 | void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev, |
17898 | struct net_device *netdev, |
17899 | const struct cfg80211_rx_assoc_resp_data *data) |
17900 | { |
17901 | struct nl80211_mlme_event event = { |
17902 | .cmd = NL80211_CMD_ASSOCIATE, |
17903 | .buf = data->buf, |
17904 | .buf_len = data->len, |
17905 | .uapsd_queues = data->uapsd_queues, |
17906 | .req_ies = data->req_ies, |
17907 | .req_ies_len = data->req_ies_len, |
17908 | }; |
17909 | |
17910 | nl80211_send_mlme_event(rdev, netdev, event: &event, GFP_KERNEL); |
17911 | } |
17912 | |
17913 | void nl80211_send_deauth(struct cfg80211_registered_device *rdev, |
17914 | struct net_device *netdev, const u8 *buf, |
17915 | size_t len, bool reconnect, gfp_t gfp) |
17916 | { |
17917 | struct nl80211_mlme_event event = { |
17918 | .cmd = NL80211_CMD_DEAUTHENTICATE, |
17919 | .buf = buf, |
17920 | .buf_len = len, |
17921 | .reconnect = reconnect, |
17922 | .uapsd_queues = -1, |
17923 | }; |
17924 | |
17925 | nl80211_send_mlme_event(rdev, netdev, event: &event, gfp); |
17926 | } |
17927 | |
17928 | void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, |
17929 | struct net_device *netdev, const u8 *buf, |
17930 | size_t len, bool reconnect, gfp_t gfp) |
17931 | { |
17932 | struct nl80211_mlme_event event = { |
17933 | .cmd = NL80211_CMD_DISASSOCIATE, |
17934 | .buf = buf, |
17935 | .buf_len = len, |
17936 | .reconnect = reconnect, |
17937 | .uapsd_queues = -1, |
17938 | }; |
17939 | |
17940 | nl80211_send_mlme_event(rdev, netdev, event: &event, gfp); |
17941 | } |
17942 | |
17943 | void cfg80211_rx_unprot_mlme_mgmt(struct net_device *dev, const u8 *buf, |
17944 | size_t len) |
17945 | { |
17946 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
17947 | struct wiphy *wiphy = wdev->wiphy; |
17948 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
17949 | const struct ieee80211_mgmt *mgmt = (void *)buf; |
17950 | struct nl80211_mlme_event event = { |
17951 | .buf = buf, |
17952 | .buf_len = len, |
17953 | .uapsd_queues = -1, |
17954 | }; |
17955 | |
17956 | if (WARN_ON(len < 2)) |
17957 | return; |
17958 | |
17959 | if (ieee80211_is_deauth(fc: mgmt->frame_control)) { |
17960 | event.cmd = NL80211_CMD_UNPROT_DEAUTHENTICATE; |
17961 | } else if (ieee80211_is_disassoc(fc: mgmt->frame_control)) { |
17962 | event.cmd = NL80211_CMD_UNPROT_DISASSOCIATE; |
17963 | } else if (ieee80211_is_beacon(fc: mgmt->frame_control)) { |
17964 | if (wdev->unprot_beacon_reported && |
17965 | elapsed_jiffies_msecs(start: wdev->unprot_beacon_reported) < 10000) |
17966 | return; |
17967 | event.cmd = NL80211_CMD_UNPROT_BEACON; |
17968 | wdev->unprot_beacon_reported = jiffies; |
17969 | } else { |
17970 | return; |
17971 | } |
17972 | |
17973 | trace_cfg80211_rx_unprot_mlme_mgmt(netdev: dev, buf, len); |
17974 | nl80211_send_mlme_event(rdev, netdev: dev, event: &event, GFP_ATOMIC); |
17975 | } |
17976 | EXPORT_SYMBOL(cfg80211_rx_unprot_mlme_mgmt); |
17977 | |
17978 | static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev, |
17979 | struct net_device *netdev, int cmd, |
17980 | const u8 *addr, gfp_t gfp) |
17981 | { |
17982 | struct sk_buff *msg; |
17983 | void *hdr; |
17984 | |
17985 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); |
17986 | if (!msg) |
17987 | return; |
17988 | |
17989 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd); |
17990 | if (!hdr) { |
17991 | nlmsg_free(skb: msg); |
17992 | return; |
17993 | } |
17994 | |
17995 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
17996 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || |
17997 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_TIMED_OUT) || |
17998 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: addr)) |
17999 | goto nla_put_failure; |
18000 | |
18001 | genlmsg_end(skb: msg, hdr); |
18002 | |
18003 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
18004 | group: NL80211_MCGRP_MLME, flags: gfp); |
18005 | return; |
18006 | |
18007 | nla_put_failure: |
18008 | nlmsg_free(skb: msg); |
18009 | } |
18010 | |
18011 | void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, |
18012 | struct net_device *netdev, const u8 *addr, |
18013 | gfp_t gfp) |
18014 | { |
18015 | nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE, |
18016 | addr, gfp); |
18017 | } |
18018 | |
18019 | void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, |
18020 | struct net_device *netdev, const u8 *addr, |
18021 | gfp_t gfp) |
18022 | { |
18023 | nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE, |
18024 | addr, gfp); |
18025 | } |
18026 | |
18027 | void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, |
18028 | struct net_device *netdev, |
18029 | struct cfg80211_connect_resp_params *cr, |
18030 | gfp_t gfp) |
18031 | { |
18032 | struct sk_buff *msg; |
18033 | void *hdr; |
18034 | unsigned int link; |
18035 | size_t link_info_size = 0; |
18036 | const u8 *connected_addr = cr->valid_links ? |
18037 | cr->ap_mld_addr : cr->links[0].bssid; |
18038 | |
18039 | if (cr->valid_links) { |
18040 | for_each_valid_link(cr, link) { |
18041 | /* Nested attribute header */ |
18042 | link_info_size += NLA_HDRLEN; |
18043 | /* Link ID */ |
18044 | link_info_size += nla_total_size(payload: sizeof(u8)); |
18045 | link_info_size += cr->links[link].addr ? |
18046 | nla_total_size(ETH_ALEN) : 0; |
18047 | link_info_size += (cr->links[link].bssid || |
18048 | cr->links[link].bss) ? |
18049 | nla_total_size(ETH_ALEN) : 0; |
18050 | link_info_size += nla_total_size(payload: sizeof(u16)); |
18051 | } |
18052 | } |
18053 | |
18054 | msg = nlmsg_new(payload: 100 + cr->req_ie_len + cr->resp_ie_len + |
18055 | cr->fils.kek_len + cr->fils.pmk_len + |
18056 | (cr->fils.pmkid ? WLAN_PMKID_LEN : 0) + link_info_size, |
18057 | flags: gfp); |
18058 | if (!msg) |
18059 | return; |
18060 | |
18061 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, NL80211_CMD_CONNECT); |
18062 | if (!hdr) { |
18063 | nlmsg_free(skb: msg); |
18064 | return; |
18065 | } |
18066 | |
18067 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
18068 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || |
18069 | (connected_addr && |
18070 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: connected_addr)) || |
18071 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_STATUS_CODE, |
18072 | value: cr->status < 0 ? WLAN_STATUS_UNSPECIFIED_FAILURE : |
18073 | cr->status) || |
18074 | (cr->status < 0 && |
18075 | (nla_put_flag(skb: msg, attrtype: NL80211_ATTR_TIMED_OUT) || |
18076 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_TIMEOUT_REASON, |
18077 | value: cr->timeout_reason))) || |
18078 | (cr->req_ie && |
18079 | nla_put(skb: msg, attrtype: NL80211_ATTR_REQ_IE, attrlen: cr->req_ie_len, data: cr->req_ie)) || |
18080 | (cr->resp_ie && |
18081 | nla_put(skb: msg, attrtype: NL80211_ATTR_RESP_IE, attrlen: cr->resp_ie_len, |
18082 | data: cr->resp_ie)) || |
18083 | (cr->fils.update_erp_next_seq_num && |
18084 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, |
18085 | value: cr->fils.erp_next_seq_num)) || |
18086 | (cr->status == WLAN_STATUS_SUCCESS && |
18087 | ((cr->fils.kek && |
18088 | nla_put(skb: msg, attrtype: NL80211_ATTR_FILS_KEK, attrlen: cr->fils.kek_len, |
18089 | data: cr->fils.kek)) || |
18090 | (cr->fils.pmk && |
18091 | nla_put(skb: msg, attrtype: NL80211_ATTR_PMK, attrlen: cr->fils.pmk_len, data: cr->fils.pmk)) || |
18092 | (cr->fils.pmkid && |
18093 | nla_put(skb: msg, attrtype: NL80211_ATTR_PMKID, WLAN_PMKID_LEN, data: cr->fils.pmkid))))) |
18094 | goto nla_put_failure; |
18095 | |
18096 | if (cr->valid_links) { |
18097 | int i = 1; |
18098 | struct nlattr *nested; |
18099 | |
18100 | nested = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_MLO_LINKS); |
18101 | if (!nested) |
18102 | goto nla_put_failure; |
18103 | |
18104 | for_each_valid_link(cr, link) { |
18105 | struct nlattr *nested_mlo_links; |
18106 | const u8 *bssid = cr->links[link].bss ? |
18107 | cr->links[link].bss->bssid : |
18108 | cr->links[link].bssid; |
18109 | |
18110 | nested_mlo_links = nla_nest_start(skb: msg, attrtype: i); |
18111 | if (!nested_mlo_links) |
18112 | goto nla_put_failure; |
18113 | |
18114 | if (nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: link) || |
18115 | (bssid && |
18116 | nla_put(skb: msg, attrtype: NL80211_ATTR_BSSID, ETH_ALEN, data: bssid)) || |
18117 | (cr->links[link].addr && |
18118 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, |
18119 | data: cr->links[link].addr)) || |
18120 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_STATUS_CODE, |
18121 | value: cr->links[link].status)) |
18122 | goto nla_put_failure; |
18123 | |
18124 | nla_nest_end(skb: msg, start: nested_mlo_links); |
18125 | i++; |
18126 | } |
18127 | nla_nest_end(skb: msg, start: nested); |
18128 | } |
18129 | |
18130 | genlmsg_end(skb: msg, hdr); |
18131 | |
18132 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
18133 | group: NL80211_MCGRP_MLME, flags: gfp); |
18134 | return; |
18135 | |
18136 | nla_put_failure: |
18137 | nlmsg_free(skb: msg); |
18138 | } |
18139 | |
18140 | void nl80211_send_roamed(struct cfg80211_registered_device *rdev, |
18141 | struct net_device *netdev, |
18142 | struct cfg80211_roam_info *info, gfp_t gfp) |
18143 | { |
18144 | struct sk_buff *msg; |
18145 | void *hdr; |
18146 | size_t link_info_size = 0; |
18147 | unsigned int link; |
18148 | const u8 *connected_addr = info->ap_mld_addr ? |
18149 | info->ap_mld_addr : |
18150 | (info->links[0].bss ? |
18151 | info->links[0].bss->bssid : |
18152 | info->links[0].bssid); |
18153 | |
18154 | if (info->valid_links) { |
18155 | for_each_valid_link(info, link) { |
18156 | /* Nested attribute header */ |
18157 | link_info_size += NLA_HDRLEN; |
18158 | /* Link ID */ |
18159 | link_info_size += nla_total_size(payload: sizeof(u8)); |
18160 | link_info_size += info->links[link].addr ? |
18161 | nla_total_size(ETH_ALEN) : 0; |
18162 | link_info_size += (info->links[link].bssid || |
18163 | info->links[link].bss) ? |
18164 | nla_total_size(ETH_ALEN) : 0; |
18165 | } |
18166 | } |
18167 | |
18168 | msg = nlmsg_new(payload: 100 + info->req_ie_len + info->resp_ie_len + |
18169 | info->fils.kek_len + info->fils.pmk_len + |
18170 | (info->fils.pmkid ? WLAN_PMKID_LEN : 0) + |
18171 | link_info_size, flags: gfp); |
18172 | if (!msg) |
18173 | return; |
18174 | |
18175 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_ROAM); |
18176 | if (!hdr) { |
18177 | nlmsg_free(skb: msg); |
18178 | return; |
18179 | } |
18180 | |
18181 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
18182 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || |
18183 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: connected_addr) || |
18184 | (info->req_ie && |
18185 | nla_put(skb: msg, attrtype: NL80211_ATTR_REQ_IE, attrlen: info->req_ie_len, |
18186 | data: info->req_ie)) || |
18187 | (info->resp_ie && |
18188 | nla_put(skb: msg, attrtype: NL80211_ATTR_RESP_IE, attrlen: info->resp_ie_len, |
18189 | data: info->resp_ie)) || |
18190 | (info->fils.update_erp_next_seq_num && |
18191 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM, |
18192 | value: info->fils.erp_next_seq_num)) || |
18193 | (info->fils.kek && |
18194 | nla_put(skb: msg, attrtype: NL80211_ATTR_FILS_KEK, attrlen: info->fils.kek_len, |
18195 | data: info->fils.kek)) || |
18196 | (info->fils.pmk && |
18197 | nla_put(skb: msg, attrtype: NL80211_ATTR_PMK, attrlen: info->fils.pmk_len, data: info->fils.pmk)) || |
18198 | (info->fils.pmkid && |
18199 | nla_put(skb: msg, attrtype: NL80211_ATTR_PMKID, WLAN_PMKID_LEN, data: info->fils.pmkid))) |
18200 | goto nla_put_failure; |
18201 | |
18202 | if (info->valid_links) { |
18203 | int i = 1; |
18204 | struct nlattr *nested; |
18205 | |
18206 | nested = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_MLO_LINKS); |
18207 | if (!nested) |
18208 | goto nla_put_failure; |
18209 | |
18210 | for_each_valid_link(info, link) { |
18211 | struct nlattr *nested_mlo_links; |
18212 | const u8 *bssid = info->links[link].bss ? |
18213 | info->links[link].bss->bssid : |
18214 | info->links[link].bssid; |
18215 | |
18216 | nested_mlo_links = nla_nest_start(skb: msg, attrtype: i); |
18217 | if (!nested_mlo_links) |
18218 | goto nla_put_failure; |
18219 | |
18220 | if (nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: link) || |
18221 | (bssid && |
18222 | nla_put(skb: msg, attrtype: NL80211_ATTR_BSSID, ETH_ALEN, data: bssid)) || |
18223 | (info->links[link].addr && |
18224 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, |
18225 | data: info->links[link].addr))) |
18226 | goto nla_put_failure; |
18227 | |
18228 | nla_nest_end(skb: msg, start: nested_mlo_links); |
18229 | i++; |
18230 | } |
18231 | nla_nest_end(skb: msg, start: nested); |
18232 | } |
18233 | |
18234 | genlmsg_end(skb: msg, hdr); |
18235 | |
18236 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
18237 | group: NL80211_MCGRP_MLME, flags: gfp); |
18238 | return; |
18239 | |
18240 | nla_put_failure: |
18241 | nlmsg_free(skb: msg); |
18242 | } |
18243 | |
18244 | void nl80211_send_port_authorized(struct cfg80211_registered_device *rdev, |
18245 | struct net_device *netdev, const u8 *peer_addr, |
18246 | const u8 *td_bitmap, u8 td_bitmap_len) |
18247 | { |
18248 | struct sk_buff *msg; |
18249 | void *hdr; |
18250 | |
18251 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
18252 | if (!msg) |
18253 | return; |
18254 | |
18255 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_PORT_AUTHORIZED); |
18256 | if (!hdr) { |
18257 | nlmsg_free(skb: msg); |
18258 | return; |
18259 | } |
18260 | |
18261 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
18262 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || |
18263 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: peer_addr)) |
18264 | goto nla_put_failure; |
18265 | |
18266 | if ((td_bitmap_len > 0) && td_bitmap) |
18267 | if (nla_put(skb: msg, attrtype: NL80211_ATTR_TD_BITMAP, |
18268 | attrlen: td_bitmap_len, data: td_bitmap)) |
18269 | goto nla_put_failure; |
18270 | |
18271 | genlmsg_end(skb: msg, hdr); |
18272 | |
18273 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
18274 | group: NL80211_MCGRP_MLME, GFP_KERNEL); |
18275 | return; |
18276 | |
18277 | nla_put_failure: |
18278 | nlmsg_free(skb: msg); |
18279 | } |
18280 | |
18281 | void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, |
18282 | struct net_device *netdev, u16 reason, |
18283 | const u8 *ie, size_t ie_len, bool from_ap) |
18284 | { |
18285 | struct sk_buff *msg; |
18286 | void *hdr; |
18287 | |
18288 | msg = nlmsg_new(payload: 100 + ie_len, GFP_KERNEL); |
18289 | if (!msg) |
18290 | return; |
18291 | |
18292 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_DISCONNECT); |
18293 | if (!hdr) { |
18294 | nlmsg_free(skb: msg); |
18295 | return; |
18296 | } |
18297 | |
18298 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
18299 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || |
18300 | (reason && |
18301 | nla_put_u16(skb: msg, NL80211_ATTR_REASON_CODE, value: reason)) || |
18302 | (from_ap && |
18303 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_DISCONNECTED_BY_AP)) || |
18304 | (ie && nla_put(skb: msg, NL80211_ATTR_IE, attrlen: ie_len, data: ie))) |
18305 | goto nla_put_failure; |
18306 | |
18307 | genlmsg_end(skb: msg, hdr); |
18308 | |
18309 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
18310 | group: NL80211_MCGRP_MLME, GFP_KERNEL); |
18311 | return; |
18312 | |
18313 | nla_put_failure: |
18314 | nlmsg_free(skb: msg); |
18315 | } |
18316 | |
18317 | void cfg80211_links_removed(struct net_device *dev, u16 link_mask) |
18318 | { |
18319 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
18320 | struct wiphy *wiphy = wdev->wiphy; |
18321 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
18322 | struct sk_buff *msg; |
18323 | struct nlattr *links; |
18324 | void *hdr; |
18325 | |
18326 | lockdep_assert_wiphy(wdev->wiphy); |
18327 | trace_cfg80211_links_removed(netdev: dev, link_mask); |
18328 | |
18329 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION && |
18330 | wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)) |
18331 | return; |
18332 | |
18333 | if (WARN_ON(!wdev->valid_links || !link_mask || |
18334 | (wdev->valid_links & link_mask) != link_mask || |
18335 | wdev->valid_links == link_mask)) |
18336 | return; |
18337 | |
18338 | cfg80211_wdev_release_link_bsses(wdev, link_mask); |
18339 | wdev->valid_links &= ~link_mask; |
18340 | |
18341 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
18342 | if (!msg) |
18343 | return; |
18344 | |
18345 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_LINKS_REMOVED); |
18346 | if (!hdr) { |
18347 | nlmsg_free(skb: msg); |
18348 | return; |
18349 | } |
18350 | |
18351 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
18352 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex)) |
18353 | goto nla_put_failure; |
18354 | |
18355 | links = nla_nest_start(skb: msg, attrtype: NL80211_ATTR_MLO_LINKS); |
18356 | if (!links) |
18357 | goto nla_put_failure; |
18358 | |
18359 | while (link_mask) { |
18360 | struct nlattr *link; |
18361 | int link_id = __ffs(link_mask); |
18362 | |
18363 | link = nla_nest_start(skb: msg, attrtype: link_id + 1); |
18364 | if (!link) |
18365 | goto nla_put_failure; |
18366 | |
18367 | if (nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: link_id)) |
18368 | goto nla_put_failure; |
18369 | |
18370 | nla_nest_end(skb: msg, start: link); |
18371 | link_mask &= ~(1 << link_id); |
18372 | } |
18373 | |
18374 | nla_nest_end(skb: msg, start: links); |
18375 | |
18376 | genlmsg_end(skb: msg, hdr); |
18377 | |
18378 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
18379 | group: NL80211_MCGRP_MLME, GFP_KERNEL); |
18380 | return; |
18381 | |
18382 | nla_put_failure: |
18383 | nlmsg_free(skb: msg); |
18384 | } |
18385 | EXPORT_SYMBOL(cfg80211_links_removed); |
18386 | |
18387 | void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, |
18388 | struct net_device *netdev, const u8 *bssid, |
18389 | gfp_t gfp) |
18390 | { |
18391 | struct sk_buff *msg; |
18392 | void *hdr; |
18393 | |
18394 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); |
18395 | if (!msg) |
18396 | return; |
18397 | |
18398 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_JOIN_IBSS); |
18399 | if (!hdr) { |
18400 | nlmsg_free(skb: msg); |
18401 | return; |
18402 | } |
18403 | |
18404 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
18405 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || |
18406 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: bssid)) |
18407 | goto nla_put_failure; |
18408 | |
18409 | genlmsg_end(skb: msg, hdr); |
18410 | |
18411 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
18412 | group: NL80211_MCGRP_MLME, flags: gfp); |
18413 | return; |
18414 | |
18415 | nla_put_failure: |
18416 | nlmsg_free(skb: msg); |
18417 | } |
18418 | |
18419 | void cfg80211_notify_new_peer_candidate(struct net_device *dev, const u8 *addr, |
18420 | const u8 *ie, u8 ie_len, |
18421 | int sig_dbm, gfp_t gfp) |
18422 | { |
18423 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
18424 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); |
18425 | struct sk_buff *msg; |
18426 | void *hdr; |
18427 | |
18428 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_MESH_POINT)) |
18429 | return; |
18430 | |
18431 | trace_cfg80211_notify_new_peer_candidate(netdev: dev, macaddr: addr); |
18432 | |
18433 | msg = nlmsg_new(payload: 100 + ie_len, flags: gfp); |
18434 | if (!msg) |
18435 | return; |
18436 | |
18437 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_NEW_PEER_CANDIDATE); |
18438 | if (!hdr) { |
18439 | nlmsg_free(skb: msg); |
18440 | return; |
18441 | } |
18442 | |
18443 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
18444 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || |
18445 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: addr) || |
18446 | (ie_len && ie && |
18447 | nla_put(skb: msg, NL80211_ATTR_IE, attrlen: ie_len, data: ie)) || |
18448 | (sig_dbm && |
18449 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_RX_SIGNAL_DBM, value: sig_dbm))) |
18450 | goto nla_put_failure; |
18451 | |
18452 | genlmsg_end(skb: msg, hdr); |
18453 | |
18454 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
18455 | group: NL80211_MCGRP_MLME, flags: gfp); |
18456 | return; |
18457 | |
18458 | nla_put_failure: |
18459 | nlmsg_free(skb: msg); |
18460 | } |
18461 | EXPORT_SYMBOL(cfg80211_notify_new_peer_candidate); |
18462 | |
18463 | void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, |
18464 | struct net_device *netdev, const u8 *addr, |
18465 | enum nl80211_key_type key_type, int key_id, |
18466 | const u8 *tsc, gfp_t gfp) |
18467 | { |
18468 | struct sk_buff *msg; |
18469 | void *hdr; |
18470 | |
18471 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); |
18472 | if (!msg) |
18473 | return; |
18474 | |
18475 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_MICHAEL_MIC_FAILURE); |
18476 | if (!hdr) { |
18477 | nlmsg_free(skb: msg); |
18478 | return; |
18479 | } |
18480 | |
18481 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
18482 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || |
18483 | (addr && nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: addr)) || |
18484 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_KEY_TYPE, value: key_type) || |
18485 | (key_id != -1 && |
18486 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_KEY_IDX, value: key_id)) || |
18487 | (tsc && nla_put(skb: msg, attrtype: NL80211_ATTR_KEY_SEQ, attrlen: 6, data: tsc))) |
18488 | goto nla_put_failure; |
18489 | |
18490 | genlmsg_end(skb: msg, hdr); |
18491 | |
18492 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
18493 | group: NL80211_MCGRP_MLME, flags: gfp); |
18494 | return; |
18495 | |
18496 | nla_put_failure: |
18497 | nlmsg_free(skb: msg); |
18498 | } |
18499 | |
18500 | void nl80211_send_beacon_hint_event(struct wiphy *wiphy, |
18501 | struct ieee80211_channel *channel_before, |
18502 | struct ieee80211_channel *channel_after) |
18503 | { |
18504 | struct sk_buff *msg; |
18505 | void *hdr; |
18506 | struct nlattr *nl_freq; |
18507 | |
18508 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); |
18509 | if (!msg) |
18510 | return; |
18511 | |
18512 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, NL80211_CMD_REG_BEACON_HINT); |
18513 | if (!hdr) { |
18514 | nlmsg_free(skb: msg); |
18515 | return; |
18516 | } |
18517 | |
18518 | /* |
18519 | * Since we are applying the beacon hint to a wiphy we know its |
18520 | * wiphy_idx is valid |
18521 | */ |
18522 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: get_wiphy_idx(wiphy))) |
18523 | goto nla_put_failure; |
18524 | |
18525 | /* Before */ |
18526 | nl_freq = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_FREQ_BEFORE); |
18527 | if (!nl_freq) |
18528 | goto nla_put_failure; |
18529 | |
18530 | if (nl80211_msg_put_channel(msg, wiphy, chan: channel_before, large: false)) |
18531 | goto nla_put_failure; |
18532 | nla_nest_end(skb: msg, start: nl_freq); |
18533 | |
18534 | /* After */ |
18535 | nl_freq = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_FREQ_AFTER); |
18536 | if (!nl_freq) |
18537 | goto nla_put_failure; |
18538 | |
18539 | if (nl80211_msg_put_channel(msg, wiphy, chan: channel_after, large: false)) |
18540 | goto nla_put_failure; |
18541 | nla_nest_end(skb: msg, start: nl_freq); |
18542 | |
18543 | genlmsg_end(skb: msg, hdr); |
18544 | |
18545 | rcu_read_lock(); |
18546 | genlmsg_multicast_allns(family: &nl80211_fam, skb: msg, portid: 0, |
18547 | group: NL80211_MCGRP_REGULATORY, GFP_ATOMIC); |
18548 | rcu_read_unlock(); |
18549 | |
18550 | return; |
18551 | |
18552 | nla_put_failure: |
18553 | nlmsg_free(skb: msg); |
18554 | } |
18555 | |
18556 | static void nl80211_send_remain_on_chan_event( |
18557 | int cmd, struct cfg80211_registered_device *rdev, |
18558 | struct wireless_dev *wdev, u64 cookie, |
18559 | struct ieee80211_channel *chan, |
18560 | unsigned int duration, gfp_t gfp) |
18561 | { |
18562 | struct sk_buff *msg; |
18563 | void *hdr; |
18564 | |
18565 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); |
18566 | if (!msg) |
18567 | return; |
18568 | |
18569 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd); |
18570 | if (!hdr) { |
18571 | nlmsg_free(skb: msg); |
18572 | return; |
18573 | } |
18574 | |
18575 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
18576 | (wdev->netdev && nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, |
18577 | value: wdev->netdev->ifindex)) || |
18578 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), |
18579 | padattr: NL80211_ATTR_PAD) || |
18580 | nla_put_u32(skb: msg, NL80211_ATTR_WIPHY_FREQ, value: chan->center_freq) || |
18581 | nla_put_u32(skb: msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, |
18582 | value: NL80211_CHAN_NO_HT) || |
18583 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: cookie, |
18584 | padattr: NL80211_ATTR_PAD)) |
18585 | goto nla_put_failure; |
18586 | |
18587 | if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL && |
18588 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_DURATION, value: duration)) |
18589 | goto nla_put_failure; |
18590 | |
18591 | genlmsg_end(skb: msg, hdr); |
18592 | |
18593 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
18594 | group: NL80211_MCGRP_MLME, flags: gfp); |
18595 | return; |
18596 | |
18597 | nla_put_failure: |
18598 | nlmsg_free(skb: msg); |
18599 | } |
18600 | |
18601 | void cfg80211_assoc_comeback(struct net_device *netdev, |
18602 | const u8 *ap_addr, u32 timeout) |
18603 | { |
18604 | struct wireless_dev *wdev = netdev->ieee80211_ptr; |
18605 | struct wiphy *wiphy = wdev->wiphy; |
18606 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
18607 | struct sk_buff *msg; |
18608 | void *hdr; |
18609 | |
18610 | trace_cfg80211_assoc_comeback(wdev, ap_addr, timeout); |
18611 | |
18612 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
18613 | if (!msg) |
18614 | return; |
18615 | |
18616 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_ASSOC_COMEBACK); |
18617 | if (!hdr) { |
18618 | nlmsg_free(skb: msg); |
18619 | return; |
18620 | } |
18621 | |
18622 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
18623 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || |
18624 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: ap_addr) || |
18625 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_TIMEOUT, value: timeout)) |
18626 | goto nla_put_failure; |
18627 | |
18628 | genlmsg_end(skb: msg, hdr); |
18629 | |
18630 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
18631 | group: NL80211_MCGRP_MLME, GFP_KERNEL); |
18632 | return; |
18633 | |
18634 | nla_put_failure: |
18635 | nlmsg_free(skb: msg); |
18636 | } |
18637 | EXPORT_SYMBOL(cfg80211_assoc_comeback); |
18638 | |
18639 | void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie, |
18640 | struct ieee80211_channel *chan, |
18641 | unsigned int duration, gfp_t gfp) |
18642 | { |
18643 | struct wiphy *wiphy = wdev->wiphy; |
18644 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
18645 | |
18646 | trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration); |
18647 | nl80211_send_remain_on_chan_event(cmd: NL80211_CMD_REMAIN_ON_CHANNEL, |
18648 | rdev, wdev, cookie, chan, |
18649 | duration, gfp); |
18650 | } |
18651 | EXPORT_SYMBOL(cfg80211_ready_on_channel); |
18652 | |
18653 | void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie, |
18654 | struct ieee80211_channel *chan, |
18655 | gfp_t gfp) |
18656 | { |
18657 | struct wiphy *wiphy = wdev->wiphy; |
18658 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
18659 | |
18660 | trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan); |
18661 | nl80211_send_remain_on_chan_event(cmd: NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, |
18662 | rdev, wdev, cookie, chan, duration: 0, gfp); |
18663 | } |
18664 | EXPORT_SYMBOL(cfg80211_remain_on_channel_expired); |
18665 | |
18666 | void cfg80211_tx_mgmt_expired(struct wireless_dev *wdev, u64 cookie, |
18667 | struct ieee80211_channel *chan, |
18668 | gfp_t gfp) |
18669 | { |
18670 | struct wiphy *wiphy = wdev->wiphy; |
18671 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
18672 | |
18673 | trace_cfg80211_tx_mgmt_expired(wdev, cookie, chan); |
18674 | nl80211_send_remain_on_chan_event(cmd: NL80211_CMD_FRAME_WAIT_CANCEL, |
18675 | rdev, wdev, cookie, chan, duration: 0, gfp); |
18676 | } |
18677 | EXPORT_SYMBOL(cfg80211_tx_mgmt_expired); |
18678 | |
18679 | void cfg80211_new_sta(struct net_device *dev, const u8 *mac_addr, |
18680 | struct station_info *sinfo, gfp_t gfp) |
18681 | { |
18682 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; |
18683 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
18684 | struct sk_buff *msg; |
18685 | |
18686 | trace_cfg80211_new_sta(netdev: dev, mac_addr, sinfo); |
18687 | |
18688 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); |
18689 | if (!msg) |
18690 | return; |
18691 | |
18692 | if (nl80211_send_station(msg, cmd: NL80211_CMD_NEW_STATION, portid: 0, seq: 0, flags: 0, |
18693 | rdev, dev, mac_addr, sinfo) < 0) { |
18694 | nlmsg_free(skb: msg); |
18695 | return; |
18696 | } |
18697 | |
18698 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
18699 | group: NL80211_MCGRP_MLME, flags: gfp); |
18700 | } |
18701 | EXPORT_SYMBOL(cfg80211_new_sta); |
18702 | |
18703 | void cfg80211_del_sta_sinfo(struct net_device *dev, const u8 *mac_addr, |
18704 | struct station_info *sinfo, gfp_t gfp) |
18705 | { |
18706 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; |
18707 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
18708 | struct sk_buff *msg; |
18709 | struct station_info empty_sinfo = {}; |
18710 | |
18711 | if (!sinfo) |
18712 | sinfo = &empty_sinfo; |
18713 | |
18714 | trace_cfg80211_del_sta(netdev: dev, macaddr: mac_addr); |
18715 | |
18716 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); |
18717 | if (!msg) { |
18718 | cfg80211_sinfo_release_content(sinfo); |
18719 | return; |
18720 | } |
18721 | |
18722 | if (nl80211_send_station(msg, cmd: NL80211_CMD_DEL_STATION, portid: 0, seq: 0, flags: 0, |
18723 | rdev, dev, mac_addr, sinfo) < 0) { |
18724 | nlmsg_free(skb: msg); |
18725 | return; |
18726 | } |
18727 | |
18728 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
18729 | group: NL80211_MCGRP_MLME, flags: gfp); |
18730 | } |
18731 | EXPORT_SYMBOL(cfg80211_del_sta_sinfo); |
18732 | |
18733 | void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr, |
18734 | enum nl80211_connect_failed_reason reason, |
18735 | gfp_t gfp) |
18736 | { |
18737 | struct wiphy *wiphy = dev->ieee80211_ptr->wiphy; |
18738 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
18739 | struct sk_buff *msg; |
18740 | void *hdr; |
18741 | |
18742 | msg = nlmsg_new(NLMSG_GOODSIZE, flags: gfp); |
18743 | if (!msg) |
18744 | return; |
18745 | |
18746 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_CONN_FAILED); |
18747 | if (!hdr) { |
18748 | nlmsg_free(skb: msg); |
18749 | return; |
18750 | } |
18751 | |
18752 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || |
18753 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: mac_addr) || |
18754 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CONN_FAILED_REASON, value: reason)) |
18755 | goto nla_put_failure; |
18756 | |
18757 | genlmsg_end(skb: msg, hdr); |
18758 | |
18759 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
18760 | group: NL80211_MCGRP_MLME, flags: gfp); |
18761 | return; |
18762 | |
18763 | nla_put_failure: |
18764 | nlmsg_free(skb: msg); |
18765 | } |
18766 | EXPORT_SYMBOL(cfg80211_conn_failed); |
18767 | |
18768 | static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd, |
18769 | const u8 *addr, gfp_t gfp) |
18770 | { |
18771 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
18772 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); |
18773 | struct sk_buff *msg; |
18774 | void *hdr; |
18775 | u32 nlportid = READ_ONCE(wdev->ap_unexpected_nlportid); |
18776 | |
18777 | if (!nlportid) |
18778 | return false; |
18779 | |
18780 | msg = nlmsg_new(payload: 100, flags: gfp); |
18781 | if (!msg) |
18782 | return true; |
18783 | |
18784 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd); |
18785 | if (!hdr) { |
18786 | nlmsg_free(skb: msg); |
18787 | return true; |
18788 | } |
18789 | |
18790 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
18791 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || |
18792 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: addr)) |
18793 | goto nla_put_failure; |
18794 | |
18795 | genlmsg_end(skb: msg, hdr); |
18796 | genlmsg_unicast(net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: nlportid); |
18797 | return true; |
18798 | |
18799 | nla_put_failure: |
18800 | nlmsg_free(skb: msg); |
18801 | return true; |
18802 | } |
18803 | |
18804 | bool cfg80211_rx_spurious_frame(struct net_device *dev, |
18805 | const u8 *addr, gfp_t gfp) |
18806 | { |
18807 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
18808 | bool ret; |
18809 | |
18810 | trace_cfg80211_rx_spurious_frame(netdev: dev, addr); |
18811 | |
18812 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && |
18813 | wdev->iftype != NL80211_IFTYPE_P2P_GO)) { |
18814 | trace_cfg80211_return_bool(ret: false); |
18815 | return false; |
18816 | } |
18817 | ret = __nl80211_unexpected_frame(dev, cmd: NL80211_CMD_UNEXPECTED_FRAME, |
18818 | addr, gfp); |
18819 | trace_cfg80211_return_bool(ret); |
18820 | return ret; |
18821 | } |
18822 | EXPORT_SYMBOL(cfg80211_rx_spurious_frame); |
18823 | |
18824 | bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev, |
18825 | const u8 *addr, gfp_t gfp) |
18826 | { |
18827 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
18828 | bool ret; |
18829 | |
18830 | trace_cfg80211_rx_unexpected_4addr_frame(netdev: dev, addr); |
18831 | |
18832 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP && |
18833 | wdev->iftype != NL80211_IFTYPE_P2P_GO && |
18834 | wdev->iftype != NL80211_IFTYPE_AP_VLAN)) { |
18835 | trace_cfg80211_return_bool(ret: false); |
18836 | return false; |
18837 | } |
18838 | ret = __nl80211_unexpected_frame(dev, |
18839 | cmd: NL80211_CMD_UNEXPECTED_4ADDR_FRAME, |
18840 | addr, gfp); |
18841 | trace_cfg80211_return_bool(ret); |
18842 | return ret; |
18843 | } |
18844 | EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame); |
18845 | |
18846 | int nl80211_send_mgmt(struct cfg80211_registered_device *rdev, |
18847 | struct wireless_dev *wdev, u32 nlportid, |
18848 | struct cfg80211_rx_info *info, gfp_t gfp) |
18849 | { |
18850 | struct net_device *netdev = wdev->netdev; |
18851 | struct sk_buff *msg; |
18852 | void *hdr; |
18853 | |
18854 | msg = nlmsg_new(payload: 100 + info->len, flags: gfp); |
18855 | if (!msg) |
18856 | return -ENOMEM; |
18857 | |
18858 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_FRAME); |
18859 | if (!hdr) { |
18860 | nlmsg_free(skb: msg); |
18861 | return -ENOMEM; |
18862 | } |
18863 | |
18864 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
18865 | (netdev && nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, |
18866 | value: netdev->ifindex)) || |
18867 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), |
18868 | padattr: NL80211_ATTR_PAD) || |
18869 | (info->have_link_id && |
18870 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: info->link_id)) || |
18871 | nla_put_u32(skb: msg, NL80211_ATTR_WIPHY_FREQ, KHZ_TO_MHZ(info->freq)) || |
18872 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY_FREQ_OFFSET, value: info->freq % 1000) || |
18873 | (info->sig_dbm && |
18874 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_RX_SIGNAL_DBM, value: info->sig_dbm)) || |
18875 | nla_put(skb: msg, NL80211_ATTR_FRAME, attrlen: info->len, data: info->buf) || |
18876 | (info->flags && |
18877 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_RXMGMT_FLAGS, value: info->flags)) || |
18878 | (info->rx_tstamp && nla_put_u64_64bit(skb: msg, |
18879 | attrtype: NL80211_ATTR_RX_HW_TIMESTAMP, |
18880 | value: info->rx_tstamp, |
18881 | padattr: NL80211_ATTR_PAD)) || |
18882 | (info->ack_tstamp && nla_put_u64_64bit(skb: msg, |
18883 | attrtype: NL80211_ATTR_TX_HW_TIMESTAMP, |
18884 | value: info->ack_tstamp, |
18885 | padattr: NL80211_ATTR_PAD))) |
18886 | goto nla_put_failure; |
18887 | |
18888 | genlmsg_end(skb: msg, hdr); |
18889 | |
18890 | return genlmsg_unicast(net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: nlportid); |
18891 | |
18892 | nla_put_failure: |
18893 | nlmsg_free(skb: msg); |
18894 | return -ENOBUFS; |
18895 | } |
18896 | |
18897 | static void nl80211_frame_tx_status(struct wireless_dev *wdev, |
18898 | struct cfg80211_tx_status *status, |
18899 | gfp_t gfp, enum nl80211_commands command) |
18900 | { |
18901 | struct wiphy *wiphy = wdev->wiphy; |
18902 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
18903 | struct net_device *netdev = wdev->netdev; |
18904 | struct sk_buff *msg; |
18905 | void *hdr; |
18906 | |
18907 | if (command == NL80211_CMD_FRAME_TX_STATUS) |
18908 | trace_cfg80211_mgmt_tx_status(wdev, cookie: status->cookie, |
18909 | ack: status->ack); |
18910 | else |
18911 | trace_cfg80211_control_port_tx_status(wdev, cookie: status->cookie, |
18912 | ack: status->ack); |
18913 | |
18914 | msg = nlmsg_new(payload: 100 + status->len, flags: gfp); |
18915 | if (!msg) |
18916 | return; |
18917 | |
18918 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: command); |
18919 | if (!hdr) { |
18920 | nlmsg_free(skb: msg); |
18921 | return; |
18922 | } |
18923 | |
18924 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
18925 | (netdev && nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, |
18926 | value: netdev->ifindex)) || |
18927 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), |
18928 | padattr: NL80211_ATTR_PAD) || |
18929 | nla_put(skb: msg, NL80211_ATTR_FRAME, attrlen: status->len, data: status->buf) || |
18930 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: status->cookie, |
18931 | padattr: NL80211_ATTR_PAD) || |
18932 | (status->ack && nla_put_flag(skb: msg, attrtype: NL80211_ATTR_ACK)) || |
18933 | (status->tx_tstamp && |
18934 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_TX_HW_TIMESTAMP, |
18935 | value: status->tx_tstamp, padattr: NL80211_ATTR_PAD)) || |
18936 | (status->ack_tstamp && |
18937 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_RX_HW_TIMESTAMP, |
18938 | value: status->ack_tstamp, padattr: NL80211_ATTR_PAD))) |
18939 | goto nla_put_failure; |
18940 | |
18941 | genlmsg_end(skb: msg, hdr); |
18942 | |
18943 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
18944 | group: NL80211_MCGRP_MLME, flags: gfp); |
18945 | return; |
18946 | |
18947 | nla_put_failure: |
18948 | nlmsg_free(skb: msg); |
18949 | } |
18950 | |
18951 | void cfg80211_control_port_tx_status(struct wireless_dev *wdev, u64 cookie, |
18952 | const u8 *buf, size_t len, bool ack, |
18953 | gfp_t gfp) |
18954 | { |
18955 | struct cfg80211_tx_status status = { |
18956 | .cookie = cookie, |
18957 | .buf = buf, |
18958 | .len = len, |
18959 | .ack = ack |
18960 | }; |
18961 | |
18962 | nl80211_frame_tx_status(wdev, status: &status, gfp, |
18963 | command: NL80211_CMD_CONTROL_PORT_FRAME_TX_STATUS); |
18964 | } |
18965 | EXPORT_SYMBOL(cfg80211_control_port_tx_status); |
18966 | |
18967 | void cfg80211_mgmt_tx_status_ext(struct wireless_dev *wdev, |
18968 | struct cfg80211_tx_status *status, gfp_t gfp) |
18969 | { |
18970 | nl80211_frame_tx_status(wdev, status, gfp, command: NL80211_CMD_FRAME_TX_STATUS); |
18971 | } |
18972 | EXPORT_SYMBOL(cfg80211_mgmt_tx_status_ext); |
18973 | |
18974 | static int __nl80211_rx_control_port(struct net_device *dev, |
18975 | struct sk_buff *skb, |
18976 | bool unencrypted, |
18977 | int link_id, |
18978 | gfp_t gfp) |
18979 | { |
18980 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
18981 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); |
18982 | struct ethhdr *ehdr = eth_hdr(skb); |
18983 | const u8 *addr = ehdr->h_source; |
18984 | u16 proto = be16_to_cpu(skb->protocol); |
18985 | struct sk_buff *msg; |
18986 | void *hdr; |
18987 | struct nlattr *frame; |
18988 | |
18989 | u32 nlportid = READ_ONCE(wdev->conn_owner_nlportid); |
18990 | |
18991 | if (!nlportid) |
18992 | return -ENOENT; |
18993 | |
18994 | msg = nlmsg_new(payload: 100 + skb->len, flags: gfp); |
18995 | if (!msg) |
18996 | return -ENOMEM; |
18997 | |
18998 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_CONTROL_PORT_FRAME); |
18999 | if (!hdr) { |
19000 | nlmsg_free(skb: msg); |
19001 | return -ENOBUFS; |
19002 | } |
19003 | |
19004 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
19005 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || |
19006 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), |
19007 | padattr: NL80211_ATTR_PAD) || |
19008 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: addr) || |
19009 | nla_put_u16(skb: msg, attrtype: NL80211_ATTR_CONTROL_PORT_ETHERTYPE, value: proto) || |
19010 | (link_id >= 0 && |
19011 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: link_id)) || |
19012 | (unencrypted && nla_put_flag(skb: msg, |
19013 | attrtype: NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))) |
19014 | goto nla_put_failure; |
19015 | |
19016 | frame = nla_reserve(skb: msg, NL80211_ATTR_FRAME, attrlen: skb->len); |
19017 | if (!frame) |
19018 | goto nla_put_failure; |
19019 | |
19020 | skb_copy_bits(skb, offset: 0, to: nla_data(nla: frame), len: skb->len); |
19021 | genlmsg_end(skb: msg, hdr); |
19022 | |
19023 | return genlmsg_unicast(net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: nlportid); |
19024 | |
19025 | nla_put_failure: |
19026 | nlmsg_free(skb: msg); |
19027 | return -ENOBUFS; |
19028 | } |
19029 | |
19030 | bool cfg80211_rx_control_port(struct net_device *dev, struct sk_buff *skb, |
19031 | bool unencrypted, int link_id) |
19032 | { |
19033 | int ret; |
19034 | |
19035 | trace_cfg80211_rx_control_port(netdev: dev, skb, unencrypted, link_id); |
19036 | ret = __nl80211_rx_control_port(dev, skb, unencrypted, link_id, |
19037 | GFP_ATOMIC); |
19038 | trace_cfg80211_return_bool(ret: ret == 0); |
19039 | return ret == 0; |
19040 | } |
19041 | EXPORT_SYMBOL(cfg80211_rx_control_port); |
19042 | |
19043 | static struct sk_buff *cfg80211_prepare_cqm(struct net_device *dev, |
19044 | const char *mac, gfp_t gfp) |
19045 | { |
19046 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
19047 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); |
19048 | struct sk_buff *msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); |
19049 | void **cb; |
19050 | |
19051 | if (!msg) |
19052 | return NULL; |
19053 | |
19054 | cb = (void **)msg->cb; |
19055 | |
19056 | cb[0] = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_NOTIFY_CQM); |
19057 | if (!cb[0]) { |
19058 | nlmsg_free(skb: msg); |
19059 | return NULL; |
19060 | } |
19061 | |
19062 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
19063 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex)) |
19064 | goto nla_put_failure; |
19065 | |
19066 | if (mac && nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: mac)) |
19067 | goto nla_put_failure; |
19068 | |
19069 | cb[1] = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_CQM); |
19070 | if (!cb[1]) |
19071 | goto nla_put_failure; |
19072 | |
19073 | cb[2] = rdev; |
19074 | |
19075 | return msg; |
19076 | nla_put_failure: |
19077 | nlmsg_free(skb: msg); |
19078 | return NULL; |
19079 | } |
19080 | |
19081 | static void cfg80211_send_cqm(struct sk_buff *msg, gfp_t gfp) |
19082 | { |
19083 | void **cb = (void **)msg->cb; |
19084 | struct cfg80211_registered_device *rdev = cb[2]; |
19085 | |
19086 | nla_nest_end(skb: msg, start: cb[1]); |
19087 | genlmsg_end(skb: msg, hdr: cb[0]); |
19088 | |
19089 | memset(msg->cb, 0, sizeof(msg->cb)); |
19090 | |
19091 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
19092 | group: NL80211_MCGRP_MLME, flags: gfp); |
19093 | } |
19094 | |
19095 | void (struct net_device *dev, |
19096 | enum nl80211_cqm_rssi_threshold_event , |
19097 | s32 , gfp_t gfp) |
19098 | { |
19099 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
19100 | struct cfg80211_cqm_config *cqm_config; |
19101 | |
19102 | trace_cfg80211_cqm_rssi_notify(netdev: dev, rssi_event, rssi_level); |
19103 | |
19104 | if (WARN_ON(rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW && |
19105 | rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH)) |
19106 | return; |
19107 | |
19108 | rcu_read_lock(); |
19109 | cqm_config = rcu_dereference(wdev->cqm_config); |
19110 | if (cqm_config) { |
19111 | cqm_config->last_rssi_event_value = rssi_level; |
19112 | cqm_config->last_rssi_event_type = rssi_event; |
19113 | wiphy_work_queue(wiphy: wdev->wiphy, work: &wdev->cqm_rssi_work); |
19114 | } |
19115 | rcu_read_unlock(); |
19116 | } |
19117 | EXPORT_SYMBOL(cfg80211_cqm_rssi_notify); |
19118 | |
19119 | void (struct wiphy *wiphy, struct wiphy_work *work) |
19120 | { |
19121 | struct wireless_dev *wdev = container_of(work, struct wireless_dev, |
19122 | cqm_rssi_work); |
19123 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
19124 | enum nl80211_cqm_rssi_threshold_event ; |
19125 | struct cfg80211_cqm_config *cqm_config; |
19126 | struct sk_buff *msg; |
19127 | s32 ; |
19128 | |
19129 | cqm_config = wiphy_dereference(wdev->wiphy, wdev->cqm_config); |
19130 | if (!cqm_config) |
19131 | return; |
19132 | |
19133 | if (cqm_config->use_range_api) |
19134 | cfg80211_cqm_rssi_update(rdev, dev: wdev->netdev, cqm_config); |
19135 | |
19136 | rssi_level = cqm_config->last_rssi_event_value; |
19137 | rssi_event = cqm_config->last_rssi_event_type; |
19138 | |
19139 | msg = cfg80211_prepare_cqm(dev: wdev->netdev, NULL, GFP_KERNEL); |
19140 | if (!msg) |
19141 | return; |
19142 | |
19143 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, |
19144 | value: rssi_event)) |
19145 | goto nla_put_failure; |
19146 | |
19147 | if (rssi_level && nla_put_s32(skb: msg, attrtype: NL80211_ATTR_CQM_RSSI_LEVEL, |
19148 | value: rssi_level)) |
19149 | goto nla_put_failure; |
19150 | |
19151 | cfg80211_send_cqm(msg, GFP_KERNEL); |
19152 | |
19153 | return; |
19154 | |
19155 | nla_put_failure: |
19156 | nlmsg_free(skb: msg); |
19157 | } |
19158 | |
19159 | void cfg80211_cqm_txe_notify(struct net_device *dev, |
19160 | const u8 *peer, u32 num_packets, |
19161 | u32 rate, u32 intvl, gfp_t gfp) |
19162 | { |
19163 | struct sk_buff *msg; |
19164 | |
19165 | msg = cfg80211_prepare_cqm(dev, mac: peer, gfp); |
19166 | if (!msg) |
19167 | return; |
19168 | |
19169 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CQM_TXE_PKTS, value: num_packets)) |
19170 | goto nla_put_failure; |
19171 | |
19172 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CQM_TXE_RATE, value: rate)) |
19173 | goto nla_put_failure; |
19174 | |
19175 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CQM_TXE_INTVL, value: intvl)) |
19176 | goto nla_put_failure; |
19177 | |
19178 | cfg80211_send_cqm(msg, gfp); |
19179 | return; |
19180 | |
19181 | nla_put_failure: |
19182 | nlmsg_free(skb: msg); |
19183 | } |
19184 | EXPORT_SYMBOL(cfg80211_cqm_txe_notify); |
19185 | |
19186 | void cfg80211_cqm_pktloss_notify(struct net_device *dev, |
19187 | const u8 *peer, u32 num_packets, gfp_t gfp) |
19188 | { |
19189 | struct sk_buff *msg; |
19190 | |
19191 | trace_cfg80211_cqm_pktloss_notify(netdev: dev, peer, num_packets); |
19192 | |
19193 | msg = cfg80211_prepare_cqm(dev, mac: peer, gfp); |
19194 | if (!msg) |
19195 | return; |
19196 | |
19197 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CQM_PKT_LOSS_EVENT, value: num_packets)) |
19198 | goto nla_put_failure; |
19199 | |
19200 | cfg80211_send_cqm(msg, gfp); |
19201 | return; |
19202 | |
19203 | nla_put_failure: |
19204 | nlmsg_free(skb: msg); |
19205 | } |
19206 | EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify); |
19207 | |
19208 | void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp) |
19209 | { |
19210 | struct sk_buff *msg; |
19211 | |
19212 | msg = cfg80211_prepare_cqm(dev, NULL, gfp); |
19213 | if (!msg) |
19214 | return; |
19215 | |
19216 | if (nla_put_flag(skb: msg, attrtype: NL80211_ATTR_CQM_BEACON_LOSS_EVENT)) |
19217 | goto nla_put_failure; |
19218 | |
19219 | cfg80211_send_cqm(msg, gfp); |
19220 | return; |
19221 | |
19222 | nla_put_failure: |
19223 | nlmsg_free(skb: msg); |
19224 | } |
19225 | EXPORT_SYMBOL(cfg80211_cqm_beacon_loss_notify); |
19226 | |
19227 | static void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev, |
19228 | struct net_device *netdev, const u8 *bssid, |
19229 | const u8 *replay_ctr, gfp_t gfp) |
19230 | { |
19231 | struct sk_buff *msg; |
19232 | struct nlattr *rekey_attr; |
19233 | void *hdr; |
19234 | |
19235 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); |
19236 | if (!msg) |
19237 | return; |
19238 | |
19239 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_SET_REKEY_OFFLOAD); |
19240 | if (!hdr) { |
19241 | nlmsg_free(skb: msg); |
19242 | return; |
19243 | } |
19244 | |
19245 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
19246 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || |
19247 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: bssid)) |
19248 | goto nla_put_failure; |
19249 | |
19250 | rekey_attr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_REKEY_DATA); |
19251 | if (!rekey_attr) |
19252 | goto nla_put_failure; |
19253 | |
19254 | if (nla_put(skb: msg, attrtype: NL80211_REKEY_DATA_REPLAY_CTR, |
19255 | NL80211_REPLAY_CTR_LEN, data: replay_ctr)) |
19256 | goto nla_put_failure; |
19257 | |
19258 | nla_nest_end(skb: msg, start: rekey_attr); |
19259 | |
19260 | genlmsg_end(skb: msg, hdr); |
19261 | |
19262 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
19263 | group: NL80211_MCGRP_MLME, flags: gfp); |
19264 | return; |
19265 | |
19266 | nla_put_failure: |
19267 | nlmsg_free(skb: msg); |
19268 | } |
19269 | |
19270 | void cfg80211_gtk_rekey_notify(struct net_device *dev, const u8 *bssid, |
19271 | const u8 *replay_ctr, gfp_t gfp) |
19272 | { |
19273 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
19274 | struct wiphy *wiphy = wdev->wiphy; |
19275 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
19276 | |
19277 | trace_cfg80211_gtk_rekey_notify(netdev: dev, macaddr: bssid); |
19278 | nl80211_gtk_rekey_notify(rdev, netdev: dev, bssid, replay_ctr, gfp); |
19279 | } |
19280 | EXPORT_SYMBOL(cfg80211_gtk_rekey_notify); |
19281 | |
19282 | static void |
19283 | nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, |
19284 | struct net_device *netdev, int index, |
19285 | const u8 *bssid, bool preauth, gfp_t gfp) |
19286 | { |
19287 | struct sk_buff *msg; |
19288 | struct nlattr *attr; |
19289 | void *hdr; |
19290 | |
19291 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); |
19292 | if (!msg) |
19293 | return; |
19294 | |
19295 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_PMKSA_CANDIDATE); |
19296 | if (!hdr) { |
19297 | nlmsg_free(skb: msg); |
19298 | return; |
19299 | } |
19300 | |
19301 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
19302 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex)) |
19303 | goto nla_put_failure; |
19304 | |
19305 | attr = nla_nest_start_noflag(skb: msg, attrtype: NL80211_ATTR_PMKSA_CANDIDATE); |
19306 | if (!attr) |
19307 | goto nla_put_failure; |
19308 | |
19309 | if (nla_put_u32(skb: msg, attrtype: NL80211_PMKSA_CANDIDATE_INDEX, value: index) || |
19310 | nla_put(skb: msg, attrtype: NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, data: bssid) || |
19311 | (preauth && |
19312 | nla_put_flag(skb: msg, attrtype: NL80211_PMKSA_CANDIDATE_PREAUTH))) |
19313 | goto nla_put_failure; |
19314 | |
19315 | nla_nest_end(skb: msg, start: attr); |
19316 | |
19317 | genlmsg_end(skb: msg, hdr); |
19318 | |
19319 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
19320 | group: NL80211_MCGRP_MLME, flags: gfp); |
19321 | return; |
19322 | |
19323 | nla_put_failure: |
19324 | nlmsg_free(skb: msg); |
19325 | } |
19326 | |
19327 | void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, |
19328 | const u8 *bssid, bool preauth, gfp_t gfp) |
19329 | { |
19330 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
19331 | struct wiphy *wiphy = wdev->wiphy; |
19332 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
19333 | |
19334 | trace_cfg80211_pmksa_candidate_notify(netdev: dev, index, bssid, preauth); |
19335 | nl80211_pmksa_candidate_notify(rdev, netdev: dev, index, bssid, preauth, gfp); |
19336 | } |
19337 | EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); |
19338 | |
19339 | static void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, |
19340 | struct net_device *netdev, |
19341 | unsigned int link_id, |
19342 | struct cfg80211_chan_def *chandef, |
19343 | gfp_t gfp, |
19344 | enum nl80211_commands notif, |
19345 | u8 count, bool quiet) |
19346 | { |
19347 | struct wireless_dev *wdev = netdev->ieee80211_ptr; |
19348 | struct sk_buff *msg; |
19349 | void *hdr; |
19350 | |
19351 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); |
19352 | if (!msg) |
19353 | return; |
19354 | |
19355 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: notif); |
19356 | if (!hdr) { |
19357 | nlmsg_free(skb: msg); |
19358 | return; |
19359 | } |
19360 | |
19361 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex)) |
19362 | goto nla_put_failure; |
19363 | |
19364 | if (wdev->valid_links && |
19365 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: link_id)) |
19366 | goto nla_put_failure; |
19367 | |
19368 | if (nl80211_send_chandef(msg, chandef)) |
19369 | goto nla_put_failure; |
19370 | |
19371 | if (notif == NL80211_CMD_CH_SWITCH_STARTED_NOTIFY) { |
19372 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CH_SWITCH_COUNT, value: count)) |
19373 | goto nla_put_failure; |
19374 | if (quiet && |
19375 | nla_put_flag(skb: msg, attrtype: NL80211_ATTR_CH_SWITCH_BLOCK_TX)) |
19376 | goto nla_put_failure; |
19377 | } |
19378 | |
19379 | genlmsg_end(skb: msg, hdr); |
19380 | |
19381 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
19382 | group: NL80211_MCGRP_MLME, flags: gfp); |
19383 | return; |
19384 | |
19385 | nla_put_failure: |
19386 | nlmsg_free(skb: msg); |
19387 | } |
19388 | |
19389 | void cfg80211_ch_switch_notify(struct net_device *dev, |
19390 | struct cfg80211_chan_def *chandef, |
19391 | unsigned int link_id) |
19392 | { |
19393 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
19394 | struct wiphy *wiphy = wdev->wiphy; |
19395 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
19396 | |
19397 | lockdep_assert_wiphy(wdev->wiphy); |
19398 | WARN_INVALID_LINK_ID(wdev, link_id); |
19399 | |
19400 | trace_cfg80211_ch_switch_notify(netdev: dev, chandef, link_id); |
19401 | |
19402 | switch (wdev->iftype) { |
19403 | case NL80211_IFTYPE_STATION: |
19404 | case NL80211_IFTYPE_P2P_CLIENT: |
19405 | if (!WARN_ON(!wdev->links[link_id].client.current_bss)) |
19406 | cfg80211_update_assoc_bss_entry(wdev, link: link_id, |
19407 | channel: chandef->chan); |
19408 | break; |
19409 | case NL80211_IFTYPE_MESH_POINT: |
19410 | wdev->u.mesh.chandef = *chandef; |
19411 | wdev->u.mesh.preset_chandef = *chandef; |
19412 | break; |
19413 | case NL80211_IFTYPE_AP: |
19414 | case NL80211_IFTYPE_P2P_GO: |
19415 | wdev->links[link_id].ap.chandef = *chandef; |
19416 | break; |
19417 | case NL80211_IFTYPE_ADHOC: |
19418 | wdev->u.ibss.chandef = *chandef; |
19419 | break; |
19420 | default: |
19421 | WARN_ON(1); |
19422 | break; |
19423 | } |
19424 | |
19425 | cfg80211_schedule_channels_check(wdev); |
19426 | cfg80211_sched_dfs_chan_update(rdev); |
19427 | |
19428 | nl80211_ch_switch_notify(rdev, netdev: dev, link_id, chandef, GFP_KERNEL, |
19429 | notif: NL80211_CMD_CH_SWITCH_NOTIFY, count: 0, quiet: false); |
19430 | } |
19431 | EXPORT_SYMBOL(cfg80211_ch_switch_notify); |
19432 | |
19433 | void cfg80211_ch_switch_started_notify(struct net_device *dev, |
19434 | struct cfg80211_chan_def *chandef, |
19435 | unsigned int link_id, u8 count, |
19436 | bool quiet) |
19437 | { |
19438 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
19439 | struct wiphy *wiphy = wdev->wiphy; |
19440 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
19441 | |
19442 | lockdep_assert_wiphy(wdev->wiphy); |
19443 | WARN_INVALID_LINK_ID(wdev, link_id); |
19444 | |
19445 | trace_cfg80211_ch_switch_started_notify(netdev: dev, chandef, link_id); |
19446 | |
19447 | |
19448 | nl80211_ch_switch_notify(rdev, netdev: dev, link_id, chandef, GFP_KERNEL, |
19449 | notif: NL80211_CMD_CH_SWITCH_STARTED_NOTIFY, |
19450 | count, quiet); |
19451 | } |
19452 | EXPORT_SYMBOL(cfg80211_ch_switch_started_notify); |
19453 | |
19454 | int cfg80211_bss_color_notify(struct net_device *dev, |
19455 | enum nl80211_commands cmd, u8 count, |
19456 | u64 color_bitmap) |
19457 | { |
19458 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
19459 | struct wiphy *wiphy = wdev->wiphy; |
19460 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
19461 | struct sk_buff *msg; |
19462 | void *hdr; |
19463 | |
19464 | lockdep_assert_wiphy(wdev->wiphy); |
19465 | |
19466 | trace_cfg80211_bss_color_notify(netdev: dev, cmd, count, color_bitmap); |
19467 | |
19468 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
19469 | if (!msg) |
19470 | return -ENOMEM; |
19471 | |
19472 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd); |
19473 | if (!hdr) |
19474 | goto nla_put_failure; |
19475 | |
19476 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex)) |
19477 | goto nla_put_failure; |
19478 | |
19479 | if (cmd == NL80211_CMD_COLOR_CHANGE_STARTED && |
19480 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_COLOR_CHANGE_COUNT, value: count)) |
19481 | goto nla_put_failure; |
19482 | |
19483 | if (cmd == NL80211_CMD_OBSS_COLOR_COLLISION && |
19484 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_OBSS_COLOR_BITMAP, |
19485 | value: color_bitmap, padattr: NL80211_ATTR_PAD)) |
19486 | goto nla_put_failure; |
19487 | |
19488 | genlmsg_end(skb: msg, hdr); |
19489 | |
19490 | return genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), |
19491 | skb: msg, portid: 0, group: NL80211_MCGRP_MLME, GFP_KERNEL); |
19492 | |
19493 | nla_put_failure: |
19494 | nlmsg_free(skb: msg); |
19495 | return -EINVAL; |
19496 | } |
19497 | EXPORT_SYMBOL(cfg80211_bss_color_notify); |
19498 | |
19499 | void |
19500 | nl80211_radar_notify(struct cfg80211_registered_device *rdev, |
19501 | const struct cfg80211_chan_def *chandef, |
19502 | enum nl80211_radar_event event, |
19503 | struct net_device *netdev, gfp_t gfp) |
19504 | { |
19505 | struct sk_buff *msg; |
19506 | void *hdr; |
19507 | |
19508 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); |
19509 | if (!msg) |
19510 | return; |
19511 | |
19512 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_RADAR_DETECT); |
19513 | if (!hdr) { |
19514 | nlmsg_free(skb: msg); |
19515 | return; |
19516 | } |
19517 | |
19518 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx)) |
19519 | goto nla_put_failure; |
19520 | |
19521 | /* NOP and radar events don't need a netdev parameter */ |
19522 | if (netdev) { |
19523 | struct wireless_dev *wdev = netdev->ieee80211_ptr; |
19524 | |
19525 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || |
19526 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), |
19527 | padattr: NL80211_ATTR_PAD)) |
19528 | goto nla_put_failure; |
19529 | } |
19530 | |
19531 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_RADAR_EVENT, value: event)) |
19532 | goto nla_put_failure; |
19533 | |
19534 | if (nl80211_send_chandef(msg, chandef)) |
19535 | goto nla_put_failure; |
19536 | |
19537 | genlmsg_end(skb: msg, hdr); |
19538 | |
19539 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
19540 | group: NL80211_MCGRP_MLME, flags: gfp); |
19541 | return; |
19542 | |
19543 | nla_put_failure: |
19544 | nlmsg_free(skb: msg); |
19545 | } |
19546 | |
19547 | void cfg80211_sta_opmode_change_notify(struct net_device *dev, const u8 *mac, |
19548 | struct sta_opmode_info *sta_opmode, |
19549 | gfp_t gfp) |
19550 | { |
19551 | struct sk_buff *msg; |
19552 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
19553 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); |
19554 | void *hdr; |
19555 | |
19556 | if (WARN_ON(!mac)) |
19557 | return; |
19558 | |
19559 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); |
19560 | if (!msg) |
19561 | return; |
19562 | |
19563 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_STA_OPMODE_CHANGED); |
19564 | if (!hdr) { |
19565 | nlmsg_free(skb: msg); |
19566 | return; |
19567 | } |
19568 | |
19569 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx)) |
19570 | goto nla_put_failure; |
19571 | |
19572 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex)) |
19573 | goto nla_put_failure; |
19574 | |
19575 | if (nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: mac)) |
19576 | goto nla_put_failure; |
19577 | |
19578 | if ((sta_opmode->changed & STA_OPMODE_SMPS_MODE_CHANGED) && |
19579 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_SMPS_MODE, value: sta_opmode->smps_mode)) |
19580 | goto nla_put_failure; |
19581 | |
19582 | if ((sta_opmode->changed & STA_OPMODE_MAX_BW_CHANGED) && |
19583 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_CHANNEL_WIDTH, value: sta_opmode->bw)) |
19584 | goto nla_put_failure; |
19585 | |
19586 | if ((sta_opmode->changed & STA_OPMODE_N_SS_CHANGED) && |
19587 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_NSS, value: sta_opmode->rx_nss)) |
19588 | goto nla_put_failure; |
19589 | |
19590 | genlmsg_end(skb: msg, hdr); |
19591 | |
19592 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
19593 | group: NL80211_MCGRP_MLME, flags: gfp); |
19594 | |
19595 | return; |
19596 | |
19597 | nla_put_failure: |
19598 | nlmsg_free(skb: msg); |
19599 | } |
19600 | EXPORT_SYMBOL(cfg80211_sta_opmode_change_notify); |
19601 | |
19602 | void cfg80211_probe_status(struct net_device *dev, const u8 *addr, |
19603 | u64 cookie, bool acked, s32 ack_signal, |
19604 | bool is_valid_ack_signal, gfp_t gfp) |
19605 | { |
19606 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
19607 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); |
19608 | struct sk_buff *msg; |
19609 | void *hdr; |
19610 | |
19611 | trace_cfg80211_probe_status(netdev: dev, addr, cookie, acked); |
19612 | |
19613 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); |
19614 | |
19615 | if (!msg) |
19616 | return; |
19617 | |
19618 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_PROBE_CLIENT); |
19619 | if (!hdr) { |
19620 | nlmsg_free(skb: msg); |
19621 | return; |
19622 | } |
19623 | |
19624 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
19625 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || |
19626 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: addr) || |
19627 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_COOKIE, value: cookie, |
19628 | padattr: NL80211_ATTR_PAD) || |
19629 | (acked && nla_put_flag(skb: msg, attrtype: NL80211_ATTR_ACK)) || |
19630 | (is_valid_ack_signal && nla_put_s32(skb: msg, attrtype: NL80211_ATTR_ACK_SIGNAL, |
19631 | value: ack_signal))) |
19632 | goto nla_put_failure; |
19633 | |
19634 | genlmsg_end(skb: msg, hdr); |
19635 | |
19636 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
19637 | group: NL80211_MCGRP_MLME, flags: gfp); |
19638 | return; |
19639 | |
19640 | nla_put_failure: |
19641 | nlmsg_free(skb: msg); |
19642 | } |
19643 | EXPORT_SYMBOL(cfg80211_probe_status); |
19644 | |
19645 | void cfg80211_report_obss_beacon_khz(struct wiphy *wiphy, const u8 *frame, |
19646 | size_t len, int freq, int sig_dbm) |
19647 | { |
19648 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
19649 | struct sk_buff *msg; |
19650 | void *hdr; |
19651 | struct cfg80211_beacon_registration *reg; |
19652 | |
19653 | trace_cfg80211_report_obss_beacon(wiphy, frame, len, freq, sig_dbm); |
19654 | |
19655 | spin_lock_bh(lock: &rdev->beacon_registrations_lock); |
19656 | list_for_each_entry(reg, &rdev->beacon_registrations, list) { |
19657 | msg = nlmsg_new(payload: len + 100, GFP_ATOMIC); |
19658 | if (!msg) { |
19659 | spin_unlock_bh(lock: &rdev->beacon_registrations_lock); |
19660 | return; |
19661 | } |
19662 | |
19663 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_FRAME); |
19664 | if (!hdr) |
19665 | goto nla_put_failure; |
19666 | |
19667 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
19668 | (freq && |
19669 | (nla_put_u32(skb: msg, NL80211_ATTR_WIPHY_FREQ, |
19670 | KHZ_TO_MHZ(freq)) || |
19671 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY_FREQ_OFFSET, |
19672 | value: freq % 1000))) || |
19673 | (sig_dbm && |
19674 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_RX_SIGNAL_DBM, value: sig_dbm)) || |
19675 | nla_put(skb: msg, NL80211_ATTR_FRAME, attrlen: len, data: frame)) |
19676 | goto nla_put_failure; |
19677 | |
19678 | genlmsg_end(skb: msg, hdr); |
19679 | |
19680 | genlmsg_unicast(net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: reg->nlportid); |
19681 | } |
19682 | spin_unlock_bh(lock: &rdev->beacon_registrations_lock); |
19683 | return; |
19684 | |
19685 | nla_put_failure: |
19686 | spin_unlock_bh(lock: &rdev->beacon_registrations_lock); |
19687 | nlmsg_free(skb: msg); |
19688 | } |
19689 | EXPORT_SYMBOL(cfg80211_report_obss_beacon_khz); |
19690 | |
19691 | #ifdef CONFIG_PM |
19692 | static int cfg80211_net_detect_results(struct sk_buff *msg, |
19693 | struct cfg80211_wowlan_wakeup *wakeup) |
19694 | { |
19695 | struct cfg80211_wowlan_nd_info *nd = wakeup->net_detect; |
19696 | struct nlattr *nl_results, *nl_match, *nl_freqs; |
19697 | int i, j; |
19698 | |
19699 | nl_results = nla_nest_start_noflag(skb: msg, |
19700 | attrtype: NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS); |
19701 | if (!nl_results) |
19702 | return -EMSGSIZE; |
19703 | |
19704 | for (i = 0; i < nd->n_matches; i++) { |
19705 | struct cfg80211_wowlan_nd_match *match = nd->matches[i]; |
19706 | |
19707 | nl_match = nla_nest_start_noflag(skb: msg, attrtype: i); |
19708 | if (!nl_match) |
19709 | break; |
19710 | |
19711 | /* The SSID attribute is optional in nl80211, but for |
19712 | * simplicity reasons it's always present in the |
19713 | * cfg80211 structure. If a driver can't pass the |
19714 | * SSID, that needs to be changed. A zero length SSID |
19715 | * is still a valid SSID (wildcard), so it cannot be |
19716 | * used for this purpose. |
19717 | */ |
19718 | if (nla_put(skb: msg, NL80211_ATTR_SSID, attrlen: match->ssid.ssid_len, |
19719 | data: match->ssid.ssid)) { |
19720 | nla_nest_cancel(skb: msg, start: nl_match); |
19721 | goto out; |
19722 | } |
19723 | |
19724 | if (match->n_channels) { |
19725 | nl_freqs = nla_nest_start_noflag(skb: msg, |
19726 | attrtype: NL80211_ATTR_SCAN_FREQUENCIES); |
19727 | if (!nl_freqs) { |
19728 | nla_nest_cancel(skb: msg, start: nl_match); |
19729 | goto out; |
19730 | } |
19731 | |
19732 | for (j = 0; j < match->n_channels; j++) { |
19733 | if (nla_put_u32(skb: msg, attrtype: j, value: match->channels[j])) { |
19734 | nla_nest_cancel(skb: msg, start: nl_freqs); |
19735 | nla_nest_cancel(skb: msg, start: nl_match); |
19736 | goto out; |
19737 | } |
19738 | } |
19739 | |
19740 | nla_nest_end(skb: msg, start: nl_freqs); |
19741 | } |
19742 | |
19743 | nla_nest_end(skb: msg, start: nl_match); |
19744 | } |
19745 | |
19746 | out: |
19747 | nla_nest_end(skb: msg, start: nl_results); |
19748 | return 0; |
19749 | } |
19750 | |
19751 | void cfg80211_report_wowlan_wakeup(struct wireless_dev *wdev, |
19752 | struct cfg80211_wowlan_wakeup *wakeup, |
19753 | gfp_t gfp) |
19754 | { |
19755 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); |
19756 | struct sk_buff *msg; |
19757 | void *hdr; |
19758 | int size = 200; |
19759 | |
19760 | trace_cfg80211_report_wowlan_wakeup(wiphy: wdev->wiphy, wdev, wakeup); |
19761 | |
19762 | if (wakeup) |
19763 | size += wakeup->packet_present_len; |
19764 | |
19765 | msg = nlmsg_new(payload: size, flags: gfp); |
19766 | if (!msg) |
19767 | return; |
19768 | |
19769 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_SET_WOWLAN); |
19770 | if (!hdr) |
19771 | goto free_msg; |
19772 | |
19773 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
19774 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), |
19775 | padattr: NL80211_ATTR_PAD)) |
19776 | goto free_msg; |
19777 | |
19778 | if (wdev->netdev && nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, |
19779 | value: wdev->netdev->ifindex)) |
19780 | goto free_msg; |
19781 | |
19782 | if (wakeup) { |
19783 | struct nlattr *reasons; |
19784 | |
19785 | reasons = nla_nest_start_noflag(skb: msg, |
19786 | attrtype: NL80211_ATTR_WOWLAN_TRIGGERS); |
19787 | if (!reasons) |
19788 | goto free_msg; |
19789 | |
19790 | if (wakeup->disconnect && |
19791 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_DISCONNECT)) |
19792 | goto free_msg; |
19793 | if (wakeup->magic_pkt && |
19794 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_MAGIC_PKT)) |
19795 | goto free_msg; |
19796 | if (wakeup->gtk_rekey_failure && |
19797 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) |
19798 | goto free_msg; |
19799 | if (wakeup->eap_identity_req && |
19800 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) |
19801 | goto free_msg; |
19802 | if (wakeup->four_way_handshake && |
19803 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) |
19804 | goto free_msg; |
19805 | if (wakeup->rfkill_release && |
19806 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_RFKILL_RELEASE)) |
19807 | goto free_msg; |
19808 | |
19809 | if (wakeup->pattern_idx >= 0 && |
19810 | nla_put_u32(skb: msg, attrtype: NL80211_WOWLAN_TRIG_PKT_PATTERN, |
19811 | value: wakeup->pattern_idx)) |
19812 | goto free_msg; |
19813 | |
19814 | if (wakeup->tcp_match && |
19815 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH)) |
19816 | goto free_msg; |
19817 | |
19818 | if (wakeup->tcp_connlost && |
19819 | nla_put_flag(skb: msg, attrtype: NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST)) |
19820 | goto free_msg; |
19821 | |
19822 | if (wakeup->tcp_nomoretokens && |
19823 | nla_put_flag(skb: msg, |
19824 | attrtype: NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS)) |
19825 | goto free_msg; |
19826 | |
19827 | if (wakeup->unprot_deauth_disassoc && |
19828 | nla_put_flag(skb: msg, |
19829 | attrtype: NL80211_WOWLAN_TRIG_UNPROTECTED_DEAUTH_DISASSOC)) |
19830 | goto free_msg; |
19831 | |
19832 | if (wakeup->packet) { |
19833 | u32 pkt_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211; |
19834 | u32 len_attr = NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN; |
19835 | |
19836 | if (!wakeup->packet_80211) { |
19837 | pkt_attr = |
19838 | NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023; |
19839 | len_attr = |
19840 | NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN; |
19841 | } |
19842 | |
19843 | if (wakeup->packet_len && |
19844 | nla_put_u32(skb: msg, attrtype: len_attr, value: wakeup->packet_len)) |
19845 | goto free_msg; |
19846 | |
19847 | if (nla_put(skb: msg, attrtype: pkt_attr, attrlen: wakeup->packet_present_len, |
19848 | data: wakeup->packet)) |
19849 | goto free_msg; |
19850 | } |
19851 | |
19852 | if (wakeup->net_detect && |
19853 | cfg80211_net_detect_results(msg, wakeup)) |
19854 | goto free_msg; |
19855 | |
19856 | nla_nest_end(skb: msg, start: reasons); |
19857 | } |
19858 | |
19859 | genlmsg_end(skb: msg, hdr); |
19860 | |
19861 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
19862 | group: NL80211_MCGRP_MLME, flags: gfp); |
19863 | return; |
19864 | |
19865 | free_msg: |
19866 | nlmsg_free(skb: msg); |
19867 | } |
19868 | EXPORT_SYMBOL(cfg80211_report_wowlan_wakeup); |
19869 | #endif |
19870 | |
19871 | void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer, |
19872 | enum nl80211_tdls_operation oper, |
19873 | u16 reason_code, gfp_t gfp) |
19874 | { |
19875 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
19876 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); |
19877 | struct sk_buff *msg; |
19878 | void *hdr; |
19879 | |
19880 | trace_cfg80211_tdls_oper_request(wiphy: wdev->wiphy, netdev: dev, peer, oper, |
19881 | reason_code); |
19882 | |
19883 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); |
19884 | if (!msg) |
19885 | return; |
19886 | |
19887 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_TDLS_OPER); |
19888 | if (!hdr) { |
19889 | nlmsg_free(skb: msg); |
19890 | return; |
19891 | } |
19892 | |
19893 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
19894 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || |
19895 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_TDLS_OPERATION, value: oper) || |
19896 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: peer) || |
19897 | (reason_code > 0 && |
19898 | nla_put_u16(skb: msg, NL80211_ATTR_REASON_CODE, value: reason_code))) |
19899 | goto nla_put_failure; |
19900 | |
19901 | genlmsg_end(skb: msg, hdr); |
19902 | |
19903 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
19904 | group: NL80211_MCGRP_MLME, flags: gfp); |
19905 | return; |
19906 | |
19907 | nla_put_failure: |
19908 | nlmsg_free(skb: msg); |
19909 | } |
19910 | EXPORT_SYMBOL(cfg80211_tdls_oper_request); |
19911 | |
19912 | static int nl80211_netlink_notify(struct notifier_block * nb, |
19913 | unsigned long state, |
19914 | void *_notify) |
19915 | { |
19916 | struct netlink_notify *notify = _notify; |
19917 | struct cfg80211_registered_device *rdev; |
19918 | struct wireless_dev *wdev; |
19919 | struct cfg80211_beacon_registration *reg, *tmp; |
19920 | |
19921 | if (state != NETLINK_URELEASE || notify->protocol != NETLINK_GENERIC) |
19922 | return NOTIFY_DONE; |
19923 | |
19924 | rcu_read_lock(); |
19925 | |
19926 | list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { |
19927 | struct cfg80211_sched_scan_request *sched_scan_req; |
19928 | |
19929 | list_for_each_entry_rcu(sched_scan_req, |
19930 | &rdev->sched_scan_req_list, |
19931 | list) { |
19932 | if (sched_scan_req->owner_nlportid == notify->portid) { |
19933 | sched_scan_req->nl_owner_dead = true; |
19934 | wiphy_work_queue(wiphy: &rdev->wiphy, |
19935 | work: &rdev->sched_scan_stop_wk); |
19936 | } |
19937 | } |
19938 | |
19939 | list_for_each_entry_rcu(wdev, &rdev->wiphy.wdev_list, list) { |
19940 | cfg80211_mlme_unregister_socket(wdev, nlpid: notify->portid); |
19941 | |
19942 | if (wdev->owner_nlportid == notify->portid) { |
19943 | wdev->nl_owner_dead = true; |
19944 | schedule_work(work: &rdev->destroy_work); |
19945 | } else if (wdev->conn_owner_nlportid == notify->portid) { |
19946 | schedule_work(work: &wdev->disconnect_wk); |
19947 | } |
19948 | |
19949 | cfg80211_release_pmsr(wdev, portid: notify->portid); |
19950 | } |
19951 | |
19952 | spin_lock_bh(lock: &rdev->beacon_registrations_lock); |
19953 | list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations, |
19954 | list) { |
19955 | if (reg->nlportid == notify->portid) { |
19956 | list_del(entry: ®->list); |
19957 | kfree(objp: reg); |
19958 | break; |
19959 | } |
19960 | } |
19961 | spin_unlock_bh(lock: &rdev->beacon_registrations_lock); |
19962 | } |
19963 | |
19964 | rcu_read_unlock(); |
19965 | |
19966 | /* |
19967 | * It is possible that the user space process that is controlling the |
19968 | * indoor setting disappeared, so notify the regulatory core. |
19969 | */ |
19970 | regulatory_netlink_notify(portid: notify->portid); |
19971 | return NOTIFY_OK; |
19972 | } |
19973 | |
19974 | static struct notifier_block nl80211_netlink_notifier = { |
19975 | .notifier_call = nl80211_netlink_notify, |
19976 | }; |
19977 | |
19978 | void cfg80211_ft_event(struct net_device *netdev, |
19979 | struct cfg80211_ft_event_params *ft_event) |
19980 | { |
19981 | struct wiphy *wiphy = netdev->ieee80211_ptr->wiphy; |
19982 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
19983 | struct sk_buff *msg; |
19984 | void *hdr; |
19985 | |
19986 | trace_cfg80211_ft_event(wiphy, netdev, ft_event); |
19987 | |
19988 | if (!ft_event->target_ap) |
19989 | return; |
19990 | |
19991 | msg = nlmsg_new(payload: 100 + ft_event->ies_len + ft_event->ric_ies_len, |
19992 | GFP_KERNEL); |
19993 | if (!msg) |
19994 | return; |
19995 | |
19996 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_FT_EVENT); |
19997 | if (!hdr) |
19998 | goto out; |
19999 | |
20000 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
20001 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || |
20002 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: ft_event->target_ap)) |
20003 | goto out; |
20004 | |
20005 | if (ft_event->ies && |
20006 | nla_put(skb: msg, NL80211_ATTR_IE, attrlen: ft_event->ies_len, data: ft_event->ies)) |
20007 | goto out; |
20008 | if (ft_event->ric_ies && |
20009 | nla_put(skb: msg, attrtype: NL80211_ATTR_IE_RIC, attrlen: ft_event->ric_ies_len, |
20010 | data: ft_event->ric_ies)) |
20011 | goto out; |
20012 | |
20013 | genlmsg_end(skb: msg, hdr); |
20014 | |
20015 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
20016 | group: NL80211_MCGRP_MLME, GFP_KERNEL); |
20017 | return; |
20018 | out: |
20019 | nlmsg_free(skb: msg); |
20020 | } |
20021 | EXPORT_SYMBOL(cfg80211_ft_event); |
20022 | |
20023 | void cfg80211_crit_proto_stopped(struct wireless_dev *wdev, gfp_t gfp) |
20024 | { |
20025 | struct cfg80211_registered_device *rdev; |
20026 | struct sk_buff *msg; |
20027 | void *hdr; |
20028 | u32 nlportid; |
20029 | |
20030 | rdev = wiphy_to_rdev(wiphy: wdev->wiphy); |
20031 | if (!rdev->crit_proto_nlportid) |
20032 | return; |
20033 | |
20034 | nlportid = rdev->crit_proto_nlportid; |
20035 | rdev->crit_proto_nlportid = 0; |
20036 | |
20037 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); |
20038 | if (!msg) |
20039 | return; |
20040 | |
20041 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_CRIT_PROTOCOL_STOP); |
20042 | if (!hdr) |
20043 | goto nla_put_failure; |
20044 | |
20045 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
20046 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), |
20047 | padattr: NL80211_ATTR_PAD)) |
20048 | goto nla_put_failure; |
20049 | |
20050 | genlmsg_end(skb: msg, hdr); |
20051 | |
20052 | genlmsg_unicast(net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: nlportid); |
20053 | return; |
20054 | |
20055 | nla_put_failure: |
20056 | nlmsg_free(skb: msg); |
20057 | } |
20058 | EXPORT_SYMBOL(cfg80211_crit_proto_stopped); |
20059 | |
20060 | void nl80211_send_ap_stopped(struct wireless_dev *wdev, unsigned int link_id) |
20061 | { |
20062 | struct wiphy *wiphy = wdev->wiphy; |
20063 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
20064 | struct sk_buff *msg; |
20065 | void *hdr; |
20066 | |
20067 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
20068 | if (!msg) |
20069 | return; |
20070 | |
20071 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_STOP_AP); |
20072 | if (!hdr) |
20073 | goto out; |
20074 | |
20075 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
20076 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: wdev->netdev->ifindex) || |
20077 | nla_put_u64_64bit(skb: msg, attrtype: NL80211_ATTR_WDEV, value: wdev_id(wdev), |
20078 | padattr: NL80211_ATTR_PAD) || |
20079 | (wdev->valid_links && |
20080 | nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, value: link_id))) |
20081 | goto out; |
20082 | |
20083 | genlmsg_end(skb: msg, hdr); |
20084 | |
20085 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy), skb: msg, portid: 0, |
20086 | group: NL80211_MCGRP_MLME, GFP_KERNEL); |
20087 | return; |
20088 | out: |
20089 | nlmsg_free(skb: msg); |
20090 | } |
20091 | |
20092 | int cfg80211_external_auth_request(struct net_device *dev, |
20093 | struct cfg80211_external_auth_params *params, |
20094 | gfp_t gfp) |
20095 | { |
20096 | struct wireless_dev *wdev = dev->ieee80211_ptr; |
20097 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy: wdev->wiphy); |
20098 | struct sk_buff *msg; |
20099 | void *hdr; |
20100 | |
20101 | if (!wdev->conn_owner_nlportid) |
20102 | return -EINVAL; |
20103 | |
20104 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); |
20105 | if (!msg) |
20106 | return -ENOMEM; |
20107 | |
20108 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_EXTERNAL_AUTH); |
20109 | if (!hdr) |
20110 | goto nla_put_failure; |
20111 | |
20112 | /* Some historical mistakes in drivers <-> userspace interface (notably |
20113 | * between drivers and wpa_supplicant) led to a big-endian conversion |
20114 | * being needed on NL80211_ATTR_AKM_SUITES _only_ when its value is |
20115 | * WLAN_AKM_SUITE_SAE. This is now fixed on userspace side, but for the |
20116 | * benefit of older wpa_supplicant versions, send this particular value |
20117 | * in big-endian. Note that newer wpa_supplicant will also detect this |
20118 | * particular value in big endian still, so it all continues to work. |
20119 | */ |
20120 | if (params->key_mgmt_suite == WLAN_AKM_SUITE_SAE) { |
20121 | if (nla_put_be32(skb: msg, NL80211_ATTR_AKM_SUITES, |
20122 | cpu_to_be32(WLAN_AKM_SUITE_SAE))) |
20123 | goto nla_put_failure; |
20124 | } else { |
20125 | if (nla_put_u32(skb: msg, NL80211_ATTR_AKM_SUITES, |
20126 | value: params->key_mgmt_suite)) |
20127 | goto nla_put_failure; |
20128 | } |
20129 | |
20130 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
20131 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: dev->ifindex) || |
20132 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_EXTERNAL_AUTH_ACTION, |
20133 | value: params->action) || |
20134 | nla_put(skb: msg, attrtype: NL80211_ATTR_BSSID, ETH_ALEN, data: params->bssid) || |
20135 | nla_put(skb: msg, NL80211_ATTR_SSID, attrlen: params->ssid.ssid_len, |
20136 | data: params->ssid.ssid) || |
20137 | (!is_zero_ether_addr(addr: params->mld_addr) && |
20138 | nla_put(skb: msg, attrtype: NL80211_ATTR_MLD_ADDR, ETH_ALEN, data: params->mld_addr))) |
20139 | goto nla_put_failure; |
20140 | |
20141 | genlmsg_end(skb: msg, hdr); |
20142 | genlmsg_unicast(net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, |
20143 | portid: wdev->conn_owner_nlportid); |
20144 | return 0; |
20145 | |
20146 | nla_put_failure: |
20147 | nlmsg_free(skb: msg); |
20148 | return -ENOBUFS; |
20149 | } |
20150 | EXPORT_SYMBOL(cfg80211_external_auth_request); |
20151 | |
20152 | void cfg80211_update_owe_info_event(struct net_device *netdev, |
20153 | struct cfg80211_update_owe_info *owe_info, |
20154 | gfp_t gfp) |
20155 | { |
20156 | struct wiphy *wiphy = netdev->ieee80211_ptr->wiphy; |
20157 | struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); |
20158 | struct sk_buff *msg; |
20159 | void *hdr; |
20160 | |
20161 | trace_cfg80211_update_owe_info_event(wiphy, netdev, owe_info); |
20162 | |
20163 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, flags: gfp); |
20164 | if (!msg) |
20165 | return; |
20166 | |
20167 | hdr = nl80211hdr_put(skb: msg, portid: 0, seq: 0, flags: 0, cmd: NL80211_CMD_UPDATE_OWE_INFO); |
20168 | if (!hdr) |
20169 | goto nla_put_failure; |
20170 | |
20171 | if (nla_put_u32(skb: msg, attrtype: NL80211_ATTR_WIPHY, value: rdev->wiphy_idx) || |
20172 | nla_put_u32(skb: msg, attrtype: NL80211_ATTR_IFINDEX, value: netdev->ifindex) || |
20173 | nla_put(skb: msg, attrtype: NL80211_ATTR_MAC, ETH_ALEN, data: owe_info->peer)) |
20174 | goto nla_put_failure; |
20175 | |
20176 | if (!owe_info->ie_len || |
20177 | nla_put(skb: msg, NL80211_ATTR_IE, attrlen: owe_info->ie_len, data: owe_info->ie)) |
20178 | goto nla_put_failure; |
20179 | |
20180 | if (owe_info->assoc_link_id != -1) { |
20181 | if (nla_put_u8(skb: msg, attrtype: NL80211_ATTR_MLO_LINK_ID, |
20182 | value: owe_info->assoc_link_id)) |
20183 | goto nla_put_failure; |
20184 | |
20185 | if (!is_zero_ether_addr(addr: owe_info->peer_mld_addr) && |
20186 | nla_put(skb: msg, attrtype: NL80211_ATTR_MLD_ADDR, ETH_ALEN, |
20187 | data: owe_info->peer_mld_addr)) |
20188 | goto nla_put_failure; |
20189 | } |
20190 | |
20191 | genlmsg_end(skb: msg, hdr); |
20192 | |
20193 | genlmsg_multicast_netns(family: &nl80211_fam, net: wiphy_net(wiphy: &rdev->wiphy), skb: msg, portid: 0, |
20194 | group: NL80211_MCGRP_MLME, flags: gfp); |
20195 | return; |
20196 | |
20197 | nla_put_failure: |
20198 | genlmsg_cancel(skb: msg, hdr); |
20199 | nlmsg_free(skb: msg); |
20200 | } |
20201 | EXPORT_SYMBOL(cfg80211_update_owe_info_event); |
20202 | |
20203 | void cfg80211_schedule_channels_check(struct wireless_dev *wdev) |
20204 | { |
20205 | struct wiphy *wiphy = wdev->wiphy; |
20206 | |
20207 | /* Schedule channels check if NO_IR or DFS relaxations are supported */ |
20208 | if (wdev->iftype == NL80211_IFTYPE_STATION && |
20209 | (wiphy_ext_feature_isset(wiphy, |
20210 | ftidx: NL80211_EXT_FEATURE_DFS_CONCURRENT) || |
20211 | (IS_ENABLED(CONFIG_CFG80211_REG_RELAX_NO_IR) && |
20212 | wiphy->regulatory_flags & REGULATORY_ENABLE_RELAX_NO_IR))) |
20213 | reg_check_channels(); |
20214 | } |
20215 | EXPORT_SYMBOL(cfg80211_schedule_channels_check); |
20216 | |
20217 | /* initialisation/exit functions */ |
20218 | |
20219 | int __init nl80211_init(void) |
20220 | { |
20221 | int err; |
20222 | |
20223 | err = genl_register_family(family: &nl80211_fam); |
20224 | if (err) |
20225 | return err; |
20226 | |
20227 | err = netlink_register_notifier(nb: &nl80211_netlink_notifier); |
20228 | if (err) |
20229 | goto err_out; |
20230 | |
20231 | return 0; |
20232 | err_out: |
20233 | genl_unregister_family(family: &nl80211_fam); |
20234 | return err; |
20235 | } |
20236 | |
20237 | void nl80211_exit(void) |
20238 | { |
20239 | netlink_unregister_notifier(nb: &nl80211_netlink_notifier); |
20240 | genl_unregister_family(family: &nl80211_fam); |
20241 | } |
20242 | |