]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - net/ieee802154/nl802154.c
nl802154: fix cca mode wpan phy flag
[mirror_ubuntu-jammy-kernel.git] / net / ieee802154 / nl802154.c
CommitLineData
79fe1a2a
AA
1/* This program is free software; you can redistribute it and/or modify
2 * it under the terms of the GNU General Public License version 2
3 * as published by the Free Software Foundation.
4 *
5 * This program is distributed in the hope that it will be useful,
6 * but WITHOUT ANY WARRANTY; without even the implied warranty of
7 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8 * GNU General Public License for more details.
9 *
10 * Authors:
11 * Alexander Aring <aar@pengutronix.de>
12 *
13 * Based on: net/wireless/nl80211.c
14 */
15
16#include <linux/rtnetlink.h>
17
18#include <net/cfg802154.h>
19#include <net/genetlink.h>
20#include <net/mac802154.h>
21#include <net/netlink.h>
22#include <net/nl802154.h>
23#include <net/sock.h>
24
25#include "nl802154.h"
ab0bd561 26#include "rdev-ops.h"
79fe1a2a
AA
27#include "core.h"
28
29static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
30 struct genl_info *info);
31
32static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
33 struct genl_info *info);
34
35/* the netlink family */
36static struct genl_family nl802154_fam = {
37 .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
38 .name = NL802154_GENL_NAME, /* have users key off the name instead */
39 .hdrsize = 0, /* no private header */
40 .version = 1, /* no particular meaning now */
41 .maxattr = NL802154_ATTR_MAX,
42 .netnsok = true,
43 .pre_doit = nl802154_pre_doit,
44 .post_doit = nl802154_post_doit,
45};
46
47/* multicast groups */
48enum nl802154_multicast_groups {
49 NL802154_MCGRP_CONFIG,
50};
51
52static const struct genl_multicast_group nl802154_mcgrps[] = {
53 [NL802154_MCGRP_CONFIG] = { .name = "config", },
54};
55
56/* returns ERR_PTR values */
57static struct wpan_dev *
58__cfg802154_wpan_dev_from_attrs(struct net *netns, struct nlattr **attrs)
59{
60 struct cfg802154_registered_device *rdev;
61 struct wpan_dev *result = NULL;
62 bool have_ifidx = attrs[NL802154_ATTR_IFINDEX];
63 bool have_wpan_dev_id = attrs[NL802154_ATTR_WPAN_DEV];
64 u64 wpan_dev_id;
65 int wpan_phy_idx = -1;
66 int ifidx = -1;
67
68 ASSERT_RTNL();
69
70 if (!have_ifidx && !have_wpan_dev_id)
71 return ERR_PTR(-EINVAL);
72
73 if (have_ifidx)
74 ifidx = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
75 if (have_wpan_dev_id) {
76 wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
77 wpan_phy_idx = wpan_dev_id >> 32;
78 }
79
80 list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
81 struct wpan_dev *wpan_dev;
82
83 /* TODO netns compare */
84
85 if (have_wpan_dev_id && rdev->wpan_phy_idx != wpan_phy_idx)
86 continue;
87
88 list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
89 if (have_ifidx && wpan_dev->netdev &&
90 wpan_dev->netdev->ifindex == ifidx) {
91 result = wpan_dev;
92 break;
93 }
94 if (have_wpan_dev_id &&
95 wpan_dev->identifier == (u32)wpan_dev_id) {
96 result = wpan_dev;
97 break;
98 }
99 }
100
101 if (result)
102 break;
103 }
104
105 if (result)
106 return result;
107
108 return ERR_PTR(-ENODEV);
109}
110
111static struct cfg802154_registered_device *
112__cfg802154_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
113{
114 struct cfg802154_registered_device *rdev = NULL, *tmp;
115 struct net_device *netdev;
116
117 ASSERT_RTNL();
118
119 if (!attrs[NL802154_ATTR_WPAN_PHY] &&
120 !attrs[NL802154_ATTR_IFINDEX] &&
121 !attrs[NL802154_ATTR_WPAN_DEV])
122 return ERR_PTR(-EINVAL);
123
124 if (attrs[NL802154_ATTR_WPAN_PHY])
125 rdev = cfg802154_rdev_by_wpan_phy_idx(
126 nla_get_u32(attrs[NL802154_ATTR_WPAN_PHY]));
127
128 if (attrs[NL802154_ATTR_WPAN_DEV]) {
129 u64 wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
130 struct wpan_dev *wpan_dev;
131 bool found = false;
132
133 tmp = cfg802154_rdev_by_wpan_phy_idx(wpan_dev_id >> 32);
134 if (tmp) {
135 /* make sure wpan_dev exists */
136 list_for_each_entry(wpan_dev, &tmp->wpan_dev_list, list) {
137 if (wpan_dev->identifier != (u32)wpan_dev_id)
138 continue;
139 found = true;
140 break;
141 }
142
143 if (!found)
144 tmp = NULL;
145
146 if (rdev && tmp != rdev)
147 return ERR_PTR(-EINVAL);
148 rdev = tmp;
149 }
150 }
151
152 if (attrs[NL802154_ATTR_IFINDEX]) {
153 int ifindex = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
154
155 netdev = __dev_get_by_index(netns, ifindex);
156 if (netdev) {
157 if (netdev->ieee802154_ptr)
158 tmp = wpan_phy_to_rdev(
159 netdev->ieee802154_ptr->wpan_phy);
160 else
161 tmp = NULL;
162
163 /* not wireless device -- return error */
164 if (!tmp)
165 return ERR_PTR(-EINVAL);
166
167 /* mismatch -- return error */
168 if (rdev && tmp != rdev)
169 return ERR_PTR(-EINVAL);
170
171 rdev = tmp;
172 }
173 }
174
175 if (!rdev)
176 return ERR_PTR(-ENODEV);
177
178 /* TODO netns compare */
179
180 return rdev;
181}
182
183/* This function returns a pointer to the driver
184 * that the genl_info item that is passed refers to.
185 *
186 * The result of this can be a PTR_ERR and hence must
187 * be checked with IS_ERR() for errors.
188 */
189static struct cfg802154_registered_device *
190cfg802154_get_dev_from_info(struct net *netns, struct genl_info *info)
191{
192 return __cfg802154_rdev_from_attrs(netns, info->attrs);
193}
194
195/* policy for the attributes */
196static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = {
ca20ce20
AA
197 [NL802154_ATTR_WPAN_PHY] = { .type = NLA_U32 },
198 [NL802154_ATTR_WPAN_PHY_NAME] = { .type = NLA_NUL_STRING,
199 .len = 20-1 },
200
201 [NL802154_ATTR_IFINDEX] = { .type = NLA_U32 },
4b96aea0
AA
202 [NL802154_ATTR_IFTYPE] = { .type = NLA_U32 },
203 [NL802154_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
ca20ce20
AA
204
205 [NL802154_ATTR_WPAN_DEV] = { .type = NLA_U64 },
206
207 [NL802154_ATTR_PAGE] = { .type = NLA_U8, },
208 [NL802154_ATTR_CHANNEL] = { .type = NLA_U8, },
209
1a19cb68 210 [NL802154_ATTR_TX_POWER] = { .type = NLA_S32, },
ca20ce20 211
ba2a9506
AA
212 [NL802154_ATTR_CCA_MODE] = { .type = NLA_U32, },
213 [NL802154_ATTR_CCA_OPT] = { .type = NLA_U32, },
ca20ce20
AA
214
215 [NL802154_ATTR_SUPPORTED_CHANNEL] = { .type = NLA_U32, },
4b96aea0
AA
216
217 [NL802154_ATTR_PAN_ID] = { .type = NLA_U16, },
218 [NL802154_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
219 [NL802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, },
220
221 [NL802154_ATTR_MIN_BE] = { .type = NLA_U8, },
222 [NL802154_ATTR_MAX_BE] = { .type = NLA_U8, },
223 [NL802154_ATTR_MAX_CSMA_BACKOFFS] = { .type = NLA_U8, },
224
225 [NL802154_ATTR_MAX_FRAME_RETRIES] = { .type = NLA_S8, },
226
227 [NL802154_ATTR_LBT_MODE] = { .type = NLA_U8, },
0e665457
AA
228
229 [NL802154_ATTR_WPAN_PHY_CAPS] = { .type = NLA_NESTED },
79fe1a2a
AA
230};
231
232/* message building helper */
233static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
234 int flags, u8 cmd)
235{
236 /* since there is no private header just add the generic one */
237 return genlmsg_put(skb, portid, seq, &nl802154_fam, flags, cmd);
238}
239
0e665457
AA
240static int
241nl802154_put_flags(struct sk_buff *msg, int attr, u32 mask)
242{
243 struct nlattr *nl_flags = nla_nest_start(msg, attr);
244 int i;
245
246 if (!nl_flags)
247 return -ENOBUFS;
248
249 i = 0;
250 while (mask) {
251 if ((mask & 1) && nla_put_flag(msg, i))
252 return -ENOBUFS;
253
254 mask >>= 1;
255 i++;
256 }
257
258 nla_nest_end(msg, nl_flags);
259 return 0;
260}
261
ca20ce20
AA
262static int
263nl802154_send_wpan_phy_channels(struct cfg802154_registered_device *rdev,
264 struct sk_buff *msg)
265{
266 struct nlattr *nl_page;
267 unsigned long page;
268
269 nl_page = nla_nest_start(msg, NL802154_ATTR_CHANNELS_SUPPORTED);
270 if (!nl_page)
271 return -ENOBUFS;
272
cb41c8dd 273 for (page = 0; page <= IEEE802154_MAX_PAGE; page++) {
ca20ce20 274 if (nla_put_u32(msg, NL802154_ATTR_SUPPORTED_CHANNEL,
72f655e4 275 rdev->wpan_phy.supported.channels[page]))
ca20ce20
AA
276 return -ENOBUFS;
277 }
278 nla_nest_end(msg, nl_page);
279
280 return 0;
281}
282
0e665457
AA
283static int
284nl802154_put_capabilities(struct sk_buff *msg,
285 struct cfg802154_registered_device *rdev)
286{
287 const struct wpan_phy_supported *caps = &rdev->wpan_phy.supported;
288 struct nlattr *nl_caps, *nl_channels;
289 int i;
290
291 nl_caps = nla_nest_start(msg, NL802154_ATTR_WPAN_PHY_CAPS);
292 if (!nl_caps)
293 return -ENOBUFS;
294
295 nl_channels = nla_nest_start(msg, NL802154_CAP_ATTR_CHANNELS);
296 if (!nl_channels)
297 return -ENOBUFS;
298
299 for (i = 0; i <= IEEE802154_MAX_PAGE; i++) {
300 if (caps->channels[i]) {
301 if (nl802154_put_flags(msg, i, caps->channels[i]))
302 return -ENOBUFS;
303 }
304 }
305
306 nla_nest_end(msg, nl_channels);
307
308 if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_ED_LEVEL) {
309 struct nlattr *nl_ed_lvls;
310
311 nl_ed_lvls = nla_nest_start(msg,
312 NL802154_CAP_ATTR_CCA_ED_LEVELS);
313 if (!nl_ed_lvls)
314 return -ENOBUFS;
315
316 for (i = 0; i < caps->cca_ed_levels_size; i++) {
317 if (nla_put_s32(msg, i, caps->cca_ed_levels[i]))
318 return -ENOBUFS;
319 }
320
321 nla_nest_end(msg, nl_ed_lvls);
322 }
323
324 if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_TXPOWER) {
325 struct nlattr *nl_tx_pwrs;
326
327 nl_tx_pwrs = nla_nest_start(msg, NL802154_CAP_ATTR_TX_POWERS);
328 if (!nl_tx_pwrs)
329 return -ENOBUFS;
330
331 for (i = 0; i < caps->tx_powers_size; i++) {
332 if (nla_put_s32(msg, i, caps->tx_powers[i]))
333 return -ENOBUFS;
334 }
335
336 nla_nest_end(msg, nl_tx_pwrs);
337 }
338
339 if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE) {
340 if (nl802154_put_flags(msg, NL802154_CAP_ATTR_CCA_MODES,
341 caps->cca_modes) ||
342 nl802154_put_flags(msg, NL802154_CAP_ATTR_CCA_OPTS,
343 caps->cca_opts))
344 return -ENOBUFS;
345 }
346
347 if (nla_put_u8(msg, NL802154_CAP_ATTR_MIN_MINBE, caps->min_minbe) ||
348 nla_put_u8(msg, NL802154_CAP_ATTR_MAX_MINBE, caps->max_minbe) ||
349 nla_put_u8(msg, NL802154_CAP_ATTR_MIN_MAXBE, caps->min_maxbe) ||
350 nla_put_u8(msg, NL802154_CAP_ATTR_MAX_MAXBE, caps->max_maxbe) ||
351 nla_put_u8(msg, NL802154_CAP_ATTR_MIN_CSMA_BACKOFFS,
352 caps->min_csma_backoffs) ||
353 nla_put_u8(msg, NL802154_CAP_ATTR_MAX_CSMA_BACKOFFS,
354 caps->max_csma_backoffs) ||
355 nla_put_s8(msg, NL802154_CAP_ATTR_MIN_FRAME_RETRIES,
356 caps->min_frame_retries) ||
357 nla_put_s8(msg, NL802154_CAP_ATTR_MAX_FRAME_RETRIES,
358 caps->max_frame_retries) ||
359 nl802154_put_flags(msg, NL802154_CAP_ATTR_IFTYPES,
360 caps->iftypes) ||
361 nla_put_u32(msg, NL802154_CAP_ATTR_LBT, caps->lbt))
362 return -ENOBUFS;
363
364 nla_nest_end(msg, nl_caps);
365
366 return 0;
367}
368
ca20ce20
AA
369static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev,
370 enum nl802154_commands cmd,
371 struct sk_buff *msg, u32 portid, u32 seq,
372 int flags)
373{
374 void *hdr;
375
376 hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
377 if (!hdr)
378 return -ENOBUFS;
379
380 if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
381 nla_put_string(msg, NL802154_ATTR_WPAN_PHY_NAME,
382 wpan_phy_name(&rdev->wpan_phy)) ||
383 nla_put_u32(msg, NL802154_ATTR_GENERATION,
384 cfg802154_rdev_list_generation))
385 goto nla_put_failure;
386
387 if (cmd != NL802154_CMD_NEW_WPAN_PHY)
388 goto finish;
389
390 /* DUMP PHY PIB */
391
392 /* current channel settings */
393 if (nla_put_u8(msg, NL802154_ATTR_PAGE,
394 rdev->wpan_phy.current_page) ||
395 nla_put_u8(msg, NL802154_ATTR_CHANNEL,
396 rdev->wpan_phy.current_channel))
397 goto nla_put_failure;
398
0e665457
AA
399 /* TODO remove this behaviour, we still keep support it for a while
400 * so users can change the behaviour to the new one.
401 */
ca20ce20
AA
402 if (nl802154_send_wpan_phy_channels(rdev, msg))
403 goto nla_put_failure;
404
405 /* cca mode */
edea8f7c
AA
406 if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE) {
407 if (nla_put_u32(msg, NL802154_ATTR_CCA_MODE,
408 rdev->wpan_phy.cca.mode))
ba2a9506 409 goto nla_put_failure;
edea8f7c
AA
410
411 if (rdev->wpan_phy.cca.mode == NL802154_CCA_ENERGY_CARRIER) {
412 if (nla_put_u32(msg, NL802154_ATTR_CCA_OPT,
413 rdev->wpan_phy.cca.opt))
414 goto nla_put_failure;
415 }
ba2a9506
AA
416 }
417
edea8f7c
AA
418 if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_TXPOWER) {
419 if (nla_put_s32(msg, NL802154_ATTR_TX_POWER,
420 rdev->wpan_phy.transmit_power))
421 goto nla_put_failure;
422 }
ca20ce20 423
0e665457
AA
424 if (nl802154_put_capabilities(msg, rdev))
425 goto nla_put_failure;
426
ca20ce20 427finish:
053c095a
JB
428 genlmsg_end(msg, hdr);
429 return 0;
ca20ce20
AA
430
431nla_put_failure:
432 genlmsg_cancel(msg, hdr);
433 return -EMSGSIZE;
434}
435
436struct nl802154_dump_wpan_phy_state {
437 s64 filter_wpan_phy;
438 long start;
439
440};
441
442static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb,
443 struct netlink_callback *cb,
444 struct nl802154_dump_wpan_phy_state *state)
445{
446 struct nlattr **tb = nl802154_fam.attrbuf;
447 int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize,
448 tb, nl802154_fam.maxattr, nl802154_policy);
449
450 /* TODO check if we can handle error here,
451 * we have no backward compatibility
452 */
453 if (ret)
454 return 0;
455
456 if (tb[NL802154_ATTR_WPAN_PHY])
457 state->filter_wpan_phy = nla_get_u32(tb[NL802154_ATTR_WPAN_PHY]);
458 if (tb[NL802154_ATTR_WPAN_DEV])
459 state->filter_wpan_phy = nla_get_u64(tb[NL802154_ATTR_WPAN_DEV]) >> 32;
460 if (tb[NL802154_ATTR_IFINDEX]) {
461 struct net_device *netdev;
462 struct cfg802154_registered_device *rdev;
463 int ifidx = nla_get_u32(tb[NL802154_ATTR_IFINDEX]);
464
465 /* TODO netns */
466 netdev = __dev_get_by_index(&init_net, ifidx);
467 if (!netdev)
468 return -ENODEV;
469 if (netdev->ieee802154_ptr) {
470 rdev = wpan_phy_to_rdev(
471 netdev->ieee802154_ptr->wpan_phy);
472 state->filter_wpan_phy = rdev->wpan_phy_idx;
473 }
474 }
475
476 return 0;
477}
478
479static int
480nl802154_dump_wpan_phy(struct sk_buff *skb, struct netlink_callback *cb)
481{
482 int idx = 0, ret;
483 struct nl802154_dump_wpan_phy_state *state = (void *)cb->args[0];
484 struct cfg802154_registered_device *rdev;
485
486 rtnl_lock();
487 if (!state) {
488 state = kzalloc(sizeof(*state), GFP_KERNEL);
489 if (!state) {
490 rtnl_unlock();
491 return -ENOMEM;
492 }
493 state->filter_wpan_phy = -1;
494 ret = nl802154_dump_wpan_phy_parse(skb, cb, state);
495 if (ret) {
496 kfree(state);
497 rtnl_unlock();
498 return ret;
499 }
500 cb->args[0] = (long)state;
501 }
502
503 list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
504 /* TODO net ns compare */
505 if (++idx <= state->start)
506 continue;
507 if (state->filter_wpan_phy != -1 &&
508 state->filter_wpan_phy != rdev->wpan_phy_idx)
509 continue;
510 /* attempt to fit multiple wpan_phy data chunks into the skb */
511 ret = nl802154_send_wpan_phy(rdev,
512 NL802154_CMD_NEW_WPAN_PHY,
513 skb,
514 NETLINK_CB(cb->skb).portid,
515 cb->nlh->nlmsg_seq, NLM_F_MULTI);
516 if (ret < 0) {
517 if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
518 !skb->len && cb->min_dump_alloc < 4096) {
519 cb->min_dump_alloc = 4096;
520 rtnl_unlock();
521 return 1;
522 }
523 idx--;
524 break;
525 }
526 break;
527 }
528 rtnl_unlock();
529
530 state->start = idx;
531
532 return skb->len;
533}
534
535static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb)
536{
537 kfree((void *)cb->args[0]);
538 return 0;
539}
540
541static int nl802154_get_wpan_phy(struct sk_buff *skb, struct genl_info *info)
542{
543 struct sk_buff *msg;
544 struct cfg802154_registered_device *rdev = info->user_ptr[0];
545
546 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
547 if (!msg)
548 return -ENOMEM;
549
550 if (nl802154_send_wpan_phy(rdev, NL802154_CMD_NEW_WPAN_PHY, msg,
551 info->snd_portid, info->snd_seq, 0) < 0) {
552 nlmsg_free(msg);
553 return -ENOBUFS;
554 }
555
556 return genlmsg_reply(msg, info);
557}
558
4b96aea0
AA
559static inline u64 wpan_dev_id(struct wpan_dev *wpan_dev)
560{
561 return (u64)wpan_dev->identifier |
562 ((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32);
563}
564
565static int
566nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
567 struct cfg802154_registered_device *rdev,
568 struct wpan_dev *wpan_dev)
569{
570 struct net_device *dev = wpan_dev->netdev;
571 void *hdr;
572
573 hdr = nl802154hdr_put(msg, portid, seq, flags,
574 NL802154_CMD_NEW_INTERFACE);
575 if (!hdr)
576 return -1;
577
578 if (dev &&
579 (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex) ||
580 nla_put_string(msg, NL802154_ATTR_IFNAME, dev->name)))
581 goto nla_put_failure;
582
583 if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
584 nla_put_u32(msg, NL802154_ATTR_IFTYPE, wpan_dev->iftype) ||
585 nla_put_u64(msg, NL802154_ATTR_WPAN_DEV, wpan_dev_id(wpan_dev)) ||
586 nla_put_u32(msg, NL802154_ATTR_GENERATION,
587 rdev->devlist_generation ^
588 (cfg802154_rdev_list_generation << 2)))
589 goto nla_put_failure;
590
591 /* address settings */
592 if (nla_put_le64(msg, NL802154_ATTR_EXTENDED_ADDR,
593 wpan_dev->extended_addr) ||
594 nla_put_le16(msg, NL802154_ATTR_SHORT_ADDR,
595 wpan_dev->short_addr) ||
596 nla_put_le16(msg, NL802154_ATTR_PAN_ID, wpan_dev->pan_id))
597 goto nla_put_failure;
598
599 /* ARET handling */
600 if (nla_put_s8(msg, NL802154_ATTR_MAX_FRAME_RETRIES,
601 wpan_dev->frame_retries) ||
602 nla_put_u8(msg, NL802154_ATTR_MAX_BE, wpan_dev->max_be) ||
603 nla_put_u8(msg, NL802154_ATTR_MAX_CSMA_BACKOFFS,
604 wpan_dev->csma_retries) ||
605 nla_put_u8(msg, NL802154_ATTR_MIN_BE, wpan_dev->min_be))
606 goto nla_put_failure;
607
608 /* listen before transmit */
609 if (nla_put_u8(msg, NL802154_ATTR_LBT_MODE, wpan_dev->lbt))
610 goto nla_put_failure;
611
053c095a
JB
612 genlmsg_end(msg, hdr);
613 return 0;
4b96aea0
AA
614
615nla_put_failure:
616 genlmsg_cancel(msg, hdr);
617 return -EMSGSIZE;
618}
619
620static int
621nl802154_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
622{
623 int wp_idx = 0;
624 int if_idx = 0;
625 int wp_start = cb->args[0];
626 int if_start = cb->args[1];
627 struct cfg802154_registered_device *rdev;
628 struct wpan_dev *wpan_dev;
629
630 rtnl_lock();
631 list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
632 /* TODO netns compare */
633 if (wp_idx < wp_start) {
634 wp_idx++;
635 continue;
636 }
637 if_idx = 0;
638
639 list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
640 if (if_idx < if_start) {
641 if_idx++;
642 continue;
643 }
644 if (nl802154_send_iface(skb, NETLINK_CB(cb->skb).portid,
645 cb->nlh->nlmsg_seq, NLM_F_MULTI,
646 rdev, wpan_dev) < 0) {
647 goto out;
648 }
649 if_idx++;
650 }
651
652 wp_idx++;
653 }
654out:
655 rtnl_unlock();
656
657 cb->args[0] = wp_idx;
658 cb->args[1] = if_idx;
659
660 return skb->len;
661}
662
663static int nl802154_get_interface(struct sk_buff *skb, struct genl_info *info)
664{
665 struct sk_buff *msg;
666 struct cfg802154_registered_device *rdev = info->user_ptr[0];
667 struct wpan_dev *wdev = info->user_ptr[1];
668
669 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
670 if (!msg)
671 return -ENOMEM;
672
673 if (nl802154_send_iface(msg, info->snd_portid, info->snd_seq, 0,
674 rdev, wdev) < 0) {
675 nlmsg_free(msg);
676 return -ENOBUFS;
677 }
678
679 return genlmsg_reply(msg, info);
680}
681
f3ea5e44
AA
682static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info)
683{
684 struct cfg802154_registered_device *rdev = info->user_ptr[0];
685 enum nl802154_iftype type = NL802154_IFTYPE_UNSPEC;
0e57547e 686 __le64 extended_addr = cpu_to_le64(0x0000000000000000ULL);
f3ea5e44
AA
687
688 /* TODO avoid failing a new interface
689 * creation due to pending removal?
690 */
691
692 if (!info->attrs[NL802154_ATTR_IFNAME])
693 return -EINVAL;
694
695 if (info->attrs[NL802154_ATTR_IFTYPE]) {
696 type = nla_get_u32(info->attrs[NL802154_ATTR_IFTYPE]);
65318680
AA
697 if (type > NL802154_IFTYPE_MAX ||
698 !(rdev->wpan_phy.supported.iftypes & BIT(type)))
f3ea5e44
AA
699 return -EINVAL;
700 }
701
0e57547e
AA
702 /* TODO add nla_get_le64 to netlink */
703 if (info->attrs[NL802154_ATTR_EXTENDED_ADDR])
704 extended_addr = (__force __le64)nla_get_u64(
705 info->attrs[NL802154_ATTR_EXTENDED_ADDR]);
706
f3ea5e44
AA
707 if (!rdev->ops->add_virtual_intf)
708 return -EOPNOTSUPP;
709
710 return rdev_add_virtual_intf(rdev,
711 nla_data(info->attrs[NL802154_ATTR_IFNAME]),
5b4a1039 712 NET_NAME_USER, type, extended_addr);
f3ea5e44
AA
713}
714
b821ecd4
AA
715static int nl802154_del_interface(struct sk_buff *skb, struct genl_info *info)
716{
717 struct cfg802154_registered_device *rdev = info->user_ptr[0];
718 struct wpan_dev *wpan_dev = info->user_ptr[1];
719
720 if (!rdev->ops->del_virtual_intf)
721 return -EOPNOTSUPP;
722
723 /* If we remove a wpan device without a netdev then clear
724 * user_ptr[1] so that nl802154_post_doit won't dereference it
725 * to check if it needs to do dev_put(). Otherwise it crashes
726 * since the wpan_dev has been freed, unlike with a netdev where
727 * we need the dev_put() for the netdev to really be freed.
728 */
729 if (!wpan_dev->netdev)
730 info->user_ptr[1] = NULL;
731
732 return rdev_del_virtual_intf(rdev, wpan_dev);
733}
734
ab0bd561
AA
735static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info)
736{
737 struct cfg802154_registered_device *rdev = info->user_ptr[0];
738 u8 channel, page;
739
740 if (!info->attrs[NL802154_ATTR_PAGE] ||
741 !info->attrs[NL802154_ATTR_CHANNEL])
742 return -EINVAL;
743
744 page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]);
745 channel = nla_get_u8(info->attrs[NL802154_ATTR_CHANNEL]);
746
747 /* check 802.15.4 constraints */
673692fa 748 if (page > IEEE802154_MAX_PAGE || channel > IEEE802154_MAX_CHANNEL ||
72f655e4 749 !(rdev->wpan_phy.supported.channels[page] & BIT(channel)))
ab0bd561
AA
750 return -EINVAL;
751
752 return rdev_set_channel(rdev, page, channel);
753}
754
ba2a9506
AA
755static int nl802154_set_cca_mode(struct sk_buff *skb, struct genl_info *info)
756{
757 struct cfg802154_registered_device *rdev = info->user_ptr[0];
758 struct wpan_phy_cca cca;
759
fc4f8052 760 if (!(rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE))
edea8f7c
AA
761 return -EOPNOTSUPP;
762
ba2a9506
AA
763 if (!info->attrs[NL802154_ATTR_CCA_MODE])
764 return -EINVAL;
765
766 cca.mode = nla_get_u32(info->attrs[NL802154_ATTR_CCA_MODE]);
767 /* checking 802.15.4 constraints */
fea3318d
AA
768 if (cca.mode < NL802154_CCA_ENERGY ||
769 cca.mode > NL802154_CCA_ATTR_MAX ||
770 !(rdev->wpan_phy.supported.cca_modes & BIT(cca.mode)))
ba2a9506
AA
771 return -EINVAL;
772
773 if (cca.mode == NL802154_CCA_ENERGY_CARRIER) {
774 if (!info->attrs[NL802154_ATTR_CCA_OPT])
775 return -EINVAL;
776
777 cca.opt = nla_get_u32(info->attrs[NL802154_ATTR_CCA_OPT]);
fea3318d
AA
778 if (cca.opt > NL802154_CCA_OPT_ATTR_MAX ||
779 !(rdev->wpan_phy.supported.cca_opts & BIT(cca.opt)))
ba2a9506
AA
780 return -EINVAL;
781 }
782
783 return rdev_set_cca_mode(rdev, &cca);
784}
785
702bf371
AA
786static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info)
787{
788 struct cfg802154_registered_device *rdev = info->user_ptr[0];
789 struct net_device *dev = info->user_ptr[1];
790 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
ee7b9053 791 __le16 pan_id;
702bf371
AA
792
793 /* conflict here while tx/rx calls */
794 if (netif_running(dev))
795 return -EBUSY;
796
797 /* don't change address fields on monitor */
0cf0879a
AA
798 if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR ||
799 !info->attrs[NL802154_ATTR_PAN_ID])
702bf371
AA
800 return -EINVAL;
801
ee7b9053 802 pan_id = nla_get_le16(info->attrs[NL802154_ATTR_PAN_ID]);
702bf371 803
673692fa
AA
804 /* TODO
805 * I am not sure about to check here on broadcast pan_id.
806 * Broadcast is a valid setting, comment from 802.15.4:
807 * If this value is 0xffff, the device is not associated.
808 *
809 * This could useful to simple deassociate an device.
810 */
811 if (pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST))
812 return -EINVAL;
813
702bf371
AA
814 return rdev_set_pan_id(rdev, wpan_dev, pan_id);
815}
816
9830c62a
AA
817static int nl802154_set_short_addr(struct sk_buff *skb, struct genl_info *info)
818{
819 struct cfg802154_registered_device *rdev = info->user_ptr[0];
820 struct net_device *dev = info->user_ptr[1];
821 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
ee7b9053 822 __le16 short_addr;
9830c62a
AA
823
824 /* conflict here while tx/rx calls */
825 if (netif_running(dev))
826 return -EBUSY;
827
828 /* don't change address fields on monitor */
0cf0879a
AA
829 if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR ||
830 !info->attrs[NL802154_ATTR_SHORT_ADDR])
9830c62a
AA
831 return -EINVAL;
832
ee7b9053 833 short_addr = nla_get_le16(info->attrs[NL802154_ATTR_SHORT_ADDR]);
9830c62a 834
673692fa
AA
835 /* TODO
836 * I am not sure about to check here on broadcast short_addr.
837 * Broadcast is a valid setting, comment from 802.15.4:
838 * A value of 0xfffe indicates that the device has
839 * associated but has not been allocated an address. A
840 * value of 0xffff indicates that the device does not
841 * have a short address.
842 *
843 * I think we should allow to set these settings but
844 * don't allow to allow socket communication with it.
845 */
846 if (short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC) ||
847 short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST))
848 return -EINVAL;
849
9830c62a
AA
850 return rdev_set_short_addr(rdev, wpan_dev, short_addr);
851}
852
656a999e
AA
853static int
854nl802154_set_backoff_exponent(struct sk_buff *skb, struct genl_info *info)
855{
856 struct cfg802154_registered_device *rdev = info->user_ptr[0];
857 struct net_device *dev = info->user_ptr[1];
858 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
859 u8 min_be, max_be;
860
861 /* should be set on netif open inside phy settings */
862 if (netif_running(dev))
863 return -EBUSY;
864
865 if (!info->attrs[NL802154_ATTR_MIN_BE] ||
866 !info->attrs[NL802154_ATTR_MAX_BE])
867 return -EINVAL;
868
869 min_be = nla_get_u8(info->attrs[NL802154_ATTR_MIN_BE]);
870 max_be = nla_get_u8(info->attrs[NL802154_ATTR_MAX_BE]);
871
872 /* check 802.15.4 constraints */
fea3318d
AA
873 if (min_be < rdev->wpan_phy.supported.min_minbe ||
874 min_be > rdev->wpan_phy.supported.max_minbe ||
875 max_be < rdev->wpan_phy.supported.min_maxbe ||
876 max_be > rdev->wpan_phy.supported.max_maxbe ||
877 min_be > max_be)
656a999e
AA
878 return -EINVAL;
879
880 return rdev_set_backoff_exponent(rdev, wpan_dev, min_be, max_be);
881}
882
a01ba765
AA
883static int
884nl802154_set_max_csma_backoffs(struct sk_buff *skb, struct genl_info *info)
885{
886 struct cfg802154_registered_device *rdev = info->user_ptr[0];
887 struct net_device *dev = info->user_ptr[1];
888 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
889 u8 max_csma_backoffs;
890
891 /* conflict here while other running iface settings */
892 if (netif_running(dev))
893 return -EBUSY;
894
895 if (!info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS])
896 return -EINVAL;
897
898 max_csma_backoffs = nla_get_u8(
899 info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]);
900
901 /* check 802.15.4 constraints */
fea3318d
AA
902 if (max_csma_backoffs < rdev->wpan_phy.supported.min_csma_backoffs ||
903 max_csma_backoffs > rdev->wpan_phy.supported.max_csma_backoffs)
a01ba765
AA
904 return -EINVAL;
905
906 return rdev_set_max_csma_backoffs(rdev, wpan_dev, max_csma_backoffs);
907}
908
17a3a46b
AA
909static int
910nl802154_set_max_frame_retries(struct sk_buff *skb, struct genl_info *info)
911{
912 struct cfg802154_registered_device *rdev = info->user_ptr[0];
913 struct net_device *dev = info->user_ptr[1];
914 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
915 s8 max_frame_retries;
916
917 if (netif_running(dev))
918 return -EBUSY;
919
920 if (!info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES])
921 return -EINVAL;
922
923 max_frame_retries = nla_get_s8(
924 info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]);
925
926 /* check 802.15.4 constraints */
fea3318d
AA
927 if (max_frame_retries < rdev->wpan_phy.supported.min_frame_retries ||
928 max_frame_retries > rdev->wpan_phy.supported.max_frame_retries)
17a3a46b
AA
929 return -EINVAL;
930
931 return rdev_set_max_frame_retries(rdev, wpan_dev, max_frame_retries);
932}
933
c8937a1d
AA
934static int nl802154_set_lbt_mode(struct sk_buff *skb, struct genl_info *info)
935{
936 struct cfg802154_registered_device *rdev = info->user_ptr[0];
937 struct net_device *dev = info->user_ptr[1];
938 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
939 bool mode;
940
941 if (netif_running(dev))
942 return -EBUSY;
943
944 if (!info->attrs[NL802154_ATTR_LBT_MODE])
945 return -EINVAL;
946
947 mode = !!nla_get_u8(info->attrs[NL802154_ATTR_LBT_MODE]);
fea3318d
AA
948 if (!wpan_phy_supported_bool(mode, rdev->wpan_phy.supported.lbt))
949 return -EINVAL;
950
c8937a1d
AA
951 return rdev_set_lbt_mode(rdev, wpan_dev, mode);
952}
953
79fe1a2a
AA
954#define NL802154_FLAG_NEED_WPAN_PHY 0x01
955#define NL802154_FLAG_NEED_NETDEV 0x02
956#define NL802154_FLAG_NEED_RTNL 0x04
957#define NL802154_FLAG_CHECK_NETDEV_UP 0x08
958#define NL802154_FLAG_NEED_NETDEV_UP (NL802154_FLAG_NEED_NETDEV |\
959 NL802154_FLAG_CHECK_NETDEV_UP)
960#define NL802154_FLAG_NEED_WPAN_DEV 0x10
961#define NL802154_FLAG_NEED_WPAN_DEV_UP (NL802154_FLAG_NEED_WPAN_DEV |\
962 NL802154_FLAG_CHECK_NETDEV_UP)
963
964static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
965 struct genl_info *info)
966{
967 struct cfg802154_registered_device *rdev;
968 struct wpan_dev *wpan_dev;
969 struct net_device *dev;
970 bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
971
972 if (rtnl)
973 rtnl_lock();
974
975 if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
976 rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
977 if (IS_ERR(rdev)) {
978 if (rtnl)
979 rtnl_unlock();
980 return PTR_ERR(rdev);
981 }
982 info->user_ptr[0] = rdev;
983 } else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
984 ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
985 ASSERT_RTNL();
986 wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
987 info->attrs);
988 if (IS_ERR(wpan_dev)) {
989 if (rtnl)
990 rtnl_unlock();
991 return PTR_ERR(wpan_dev);
992 }
993
994 dev = wpan_dev->netdev;
995 rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
996
997 if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
998 if (!dev) {
999 if (rtnl)
1000 rtnl_unlock();
1001 return -EINVAL;
1002 }
1003
1004 info->user_ptr[1] = dev;
1005 } else {
1006 info->user_ptr[1] = wpan_dev;
1007 }
1008
1009 if (dev) {
1010 if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
1011 !netif_running(dev)) {
1012 if (rtnl)
1013 rtnl_unlock();
1014 return -ENETDOWN;
1015 }
1016
1017 dev_hold(dev);
1018 }
1019
1020 info->user_ptr[0] = rdev;
1021 }
1022
1023 return 0;
1024}
1025
1026static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
1027 struct genl_info *info)
1028{
1029 if (info->user_ptr[1]) {
1030 if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
1031 struct wpan_dev *wpan_dev = info->user_ptr[1];
1032
1033 if (wpan_dev->netdev)
1034 dev_put(wpan_dev->netdev);
1035 } else {
1036 dev_put(info->user_ptr[1]);
1037 }
1038 }
1039
1040 if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
1041 rtnl_unlock();
1042}
1043
1044static const struct genl_ops nl802154_ops[] = {
ca20ce20
AA
1045 {
1046 .cmd = NL802154_CMD_GET_WPAN_PHY,
1047 .doit = nl802154_get_wpan_phy,
1048 .dumpit = nl802154_dump_wpan_phy,
1049 .done = nl802154_dump_wpan_phy_done,
1050 .policy = nl802154_policy,
1051 /* can be retrieved by unprivileged users */
1052 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
1053 NL802154_FLAG_NEED_RTNL,
1054 },
4b96aea0
AA
1055 {
1056 .cmd = NL802154_CMD_GET_INTERFACE,
1057 .doit = nl802154_get_interface,
1058 .dumpit = nl802154_dump_interface,
1059 .policy = nl802154_policy,
1060 /* can be retrieved by unprivileged users */
1061 .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
1062 NL802154_FLAG_NEED_RTNL,
1063 },
f3ea5e44
AA
1064 {
1065 .cmd = NL802154_CMD_NEW_INTERFACE,
1066 .doit = nl802154_new_interface,
1067 .policy = nl802154_policy,
1068 .flags = GENL_ADMIN_PERM,
1069 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
1070 NL802154_FLAG_NEED_RTNL,
1071 },
b821ecd4
AA
1072 {
1073 .cmd = NL802154_CMD_DEL_INTERFACE,
1074 .doit = nl802154_del_interface,
1075 .policy = nl802154_policy,
1076 .flags = GENL_ADMIN_PERM,
1077 .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
1078 NL802154_FLAG_NEED_RTNL,
1079 },
ab0bd561
AA
1080 {
1081 .cmd = NL802154_CMD_SET_CHANNEL,
1082 .doit = nl802154_set_channel,
1083 .policy = nl802154_policy,
1084 .flags = GENL_ADMIN_PERM,
1085 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
1086 NL802154_FLAG_NEED_RTNL,
1087 },
ba2a9506
AA
1088 {
1089 .cmd = NL802154_CMD_SET_CCA_MODE,
1090 .doit = nl802154_set_cca_mode,
1091 .policy = nl802154_policy,
1092 .flags = GENL_ADMIN_PERM,
1093 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
1094 NL802154_FLAG_NEED_RTNL,
1095 },
702bf371
AA
1096 {
1097 .cmd = NL802154_CMD_SET_PAN_ID,
1098 .doit = nl802154_set_pan_id,
1099 .policy = nl802154_policy,
1100 .flags = GENL_ADMIN_PERM,
1101 .internal_flags = NL802154_FLAG_NEED_NETDEV |
1102 NL802154_FLAG_NEED_RTNL,
1103 },
9830c62a
AA
1104 {
1105 .cmd = NL802154_CMD_SET_SHORT_ADDR,
1106 .doit = nl802154_set_short_addr,
1107 .policy = nl802154_policy,
1108 .flags = GENL_ADMIN_PERM,
1109 .internal_flags = NL802154_FLAG_NEED_NETDEV |
1110 NL802154_FLAG_NEED_RTNL,
1111 },
656a999e
AA
1112 {
1113 .cmd = NL802154_CMD_SET_BACKOFF_EXPONENT,
1114 .doit = nl802154_set_backoff_exponent,
1115 .policy = nl802154_policy,
1116 .flags = GENL_ADMIN_PERM,
1117 .internal_flags = NL802154_FLAG_NEED_NETDEV |
1118 NL802154_FLAG_NEED_RTNL,
1119 },
a01ba765
AA
1120 {
1121 .cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
1122 .doit = nl802154_set_max_csma_backoffs,
1123 .policy = nl802154_policy,
1124 .flags = GENL_ADMIN_PERM,
1125 .internal_flags = NL802154_FLAG_NEED_NETDEV |
1126 NL802154_FLAG_NEED_RTNL,
1127 },
17a3a46b
AA
1128 {
1129 .cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES,
1130 .doit = nl802154_set_max_frame_retries,
1131 .policy = nl802154_policy,
1132 .flags = GENL_ADMIN_PERM,
1133 .internal_flags = NL802154_FLAG_NEED_NETDEV |
1134 NL802154_FLAG_NEED_RTNL,
1135 },
c8937a1d
AA
1136 {
1137 .cmd = NL802154_CMD_SET_LBT_MODE,
1138 .doit = nl802154_set_lbt_mode,
1139 .policy = nl802154_policy,
1140 .flags = GENL_ADMIN_PERM,
1141 .internal_flags = NL802154_FLAG_NEED_NETDEV |
1142 NL802154_FLAG_NEED_RTNL,
1143 },
79fe1a2a
AA
1144};
1145
1146/* initialisation/exit functions */
1147int nl802154_init(void)
1148{
1149 return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops,
1150 nl802154_mcgrps);
1151}
1152
1153void nl802154_exit(void)
1154{
1155 genl_unregister_family(&nl802154_fam);
1156}