blob: 8896477446977a7b4fdab732aec1308c60a46d8b [file] [log] [blame]
Alexander Aring79fe1a22014-11-09 08:36:53 +01001/* 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"
Alexander Aringab0bd562014-11-12 03:36:55 +010026#include "rdev-ops.h"
Alexander Aring79fe1a22014-11-09 08:36:53 +010027#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] = {
Alexander Aringca20ce202014-11-09 08:36:54 +0100197 [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 },
Alexander Aring4b96aea2014-11-09 08:36:55 +0100202 [NL802154_ATTR_IFTYPE] = { .type = NLA_U32 },
203 [NL802154_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
Alexander Aringca20ce202014-11-09 08:36:54 +0100204
205 [NL802154_ATTR_WPAN_DEV] = { .type = NLA_U64 },
206
207 [NL802154_ATTR_PAGE] = { .type = NLA_U8, },
208 [NL802154_ATTR_CHANNEL] = { .type = NLA_U8, },
209
210 [NL802154_ATTR_TX_POWER] = { .type = NLA_S8, },
211
212 [NL802154_ATTR_CCA_MODE] = { .type = NLA_U8, },
213
214 [NL802154_ATTR_SUPPORTED_CHANNEL] = { .type = NLA_U32, },
Alexander Aring4b96aea2014-11-09 08:36:55 +0100215
216 [NL802154_ATTR_PAN_ID] = { .type = NLA_U16, },
217 [NL802154_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
218 [NL802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, },
219
220 [NL802154_ATTR_MIN_BE] = { .type = NLA_U8, },
221 [NL802154_ATTR_MAX_BE] = { .type = NLA_U8, },
222 [NL802154_ATTR_MAX_CSMA_BACKOFFS] = { .type = NLA_U8, },
223
224 [NL802154_ATTR_MAX_FRAME_RETRIES] = { .type = NLA_S8, },
225
226 [NL802154_ATTR_LBT_MODE] = { .type = NLA_U8, },
Alexander Aring79fe1a22014-11-09 08:36:53 +0100227};
228
229/* message building helper */
230static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
231 int flags, u8 cmd)
232{
233 /* since there is no private header just add the generic one */
234 return genlmsg_put(skb, portid, seq, &nl802154_fam, flags, cmd);
235}
236
Alexander Aringca20ce202014-11-09 08:36:54 +0100237static int
238nl802154_send_wpan_phy_channels(struct cfg802154_registered_device *rdev,
239 struct sk_buff *msg)
240{
241 struct nlattr *nl_page;
242 unsigned long page;
243
244 nl_page = nla_nest_start(msg, NL802154_ATTR_CHANNELS_SUPPORTED);
245 if (!nl_page)
246 return -ENOBUFS;
247
Alexander Aringcb41c8d2014-11-17 08:20:54 +0100248 for (page = 0; page <= IEEE802154_MAX_PAGE; page++) {
Alexander Aringca20ce202014-11-09 08:36:54 +0100249 if (nla_put_u32(msg, NL802154_ATTR_SUPPORTED_CHANNEL,
250 rdev->wpan_phy.channels_supported[page]))
251 return -ENOBUFS;
252 }
253 nla_nest_end(msg, nl_page);
254
255 return 0;
256}
257
258static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev,
259 enum nl802154_commands cmd,
260 struct sk_buff *msg, u32 portid, u32 seq,
261 int flags)
262{
263 void *hdr;
264
265 hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
266 if (!hdr)
267 return -ENOBUFS;
268
269 if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
270 nla_put_string(msg, NL802154_ATTR_WPAN_PHY_NAME,
271 wpan_phy_name(&rdev->wpan_phy)) ||
272 nla_put_u32(msg, NL802154_ATTR_GENERATION,
273 cfg802154_rdev_list_generation))
274 goto nla_put_failure;
275
276 if (cmd != NL802154_CMD_NEW_WPAN_PHY)
277 goto finish;
278
279 /* DUMP PHY PIB */
280
281 /* current channel settings */
282 if (nla_put_u8(msg, NL802154_ATTR_PAGE,
283 rdev->wpan_phy.current_page) ||
284 nla_put_u8(msg, NL802154_ATTR_CHANNEL,
285 rdev->wpan_phy.current_channel))
286 goto nla_put_failure;
287
288 /* supported channels array */
289 if (nl802154_send_wpan_phy_channels(rdev, msg))
290 goto nla_put_failure;
291
292 /* cca mode */
293 if (nla_put_u8(msg, NL802154_ATTR_CCA_MODE,
294 rdev->wpan_phy.cca_mode))
295 goto nla_put_failure;
296
297 if (nla_put_s8(msg, NL802154_ATTR_TX_POWER,
298 rdev->wpan_phy.transmit_power))
299 goto nla_put_failure;
300
301finish:
302 return genlmsg_end(msg, hdr);
303
304nla_put_failure:
305 genlmsg_cancel(msg, hdr);
306 return -EMSGSIZE;
307}
308
309struct nl802154_dump_wpan_phy_state {
310 s64 filter_wpan_phy;
311 long start;
312
313};
314
315static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb,
316 struct netlink_callback *cb,
317 struct nl802154_dump_wpan_phy_state *state)
318{
319 struct nlattr **tb = nl802154_fam.attrbuf;
320 int ret = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize,
321 tb, nl802154_fam.maxattr, nl802154_policy);
322
323 /* TODO check if we can handle error here,
324 * we have no backward compatibility
325 */
326 if (ret)
327 return 0;
328
329 if (tb[NL802154_ATTR_WPAN_PHY])
330 state->filter_wpan_phy = nla_get_u32(tb[NL802154_ATTR_WPAN_PHY]);
331 if (tb[NL802154_ATTR_WPAN_DEV])
332 state->filter_wpan_phy = nla_get_u64(tb[NL802154_ATTR_WPAN_DEV]) >> 32;
333 if (tb[NL802154_ATTR_IFINDEX]) {
334 struct net_device *netdev;
335 struct cfg802154_registered_device *rdev;
336 int ifidx = nla_get_u32(tb[NL802154_ATTR_IFINDEX]);
337
338 /* TODO netns */
339 netdev = __dev_get_by_index(&init_net, ifidx);
340 if (!netdev)
341 return -ENODEV;
342 if (netdev->ieee802154_ptr) {
343 rdev = wpan_phy_to_rdev(
344 netdev->ieee802154_ptr->wpan_phy);
345 state->filter_wpan_phy = rdev->wpan_phy_idx;
346 }
347 }
348
349 return 0;
350}
351
352static int
353nl802154_dump_wpan_phy(struct sk_buff *skb, struct netlink_callback *cb)
354{
355 int idx = 0, ret;
356 struct nl802154_dump_wpan_phy_state *state = (void *)cb->args[0];
357 struct cfg802154_registered_device *rdev;
358
359 rtnl_lock();
360 if (!state) {
361 state = kzalloc(sizeof(*state), GFP_KERNEL);
362 if (!state) {
363 rtnl_unlock();
364 return -ENOMEM;
365 }
366 state->filter_wpan_phy = -1;
367 ret = nl802154_dump_wpan_phy_parse(skb, cb, state);
368 if (ret) {
369 kfree(state);
370 rtnl_unlock();
371 return ret;
372 }
373 cb->args[0] = (long)state;
374 }
375
376 list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
377 /* TODO net ns compare */
378 if (++idx <= state->start)
379 continue;
380 if (state->filter_wpan_phy != -1 &&
381 state->filter_wpan_phy != rdev->wpan_phy_idx)
382 continue;
383 /* attempt to fit multiple wpan_phy data chunks into the skb */
384 ret = nl802154_send_wpan_phy(rdev,
385 NL802154_CMD_NEW_WPAN_PHY,
386 skb,
387 NETLINK_CB(cb->skb).portid,
388 cb->nlh->nlmsg_seq, NLM_F_MULTI);
389 if (ret < 0) {
390 if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
391 !skb->len && cb->min_dump_alloc < 4096) {
392 cb->min_dump_alloc = 4096;
393 rtnl_unlock();
394 return 1;
395 }
396 idx--;
397 break;
398 }
399 break;
400 }
401 rtnl_unlock();
402
403 state->start = idx;
404
405 return skb->len;
406}
407
408static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb)
409{
410 kfree((void *)cb->args[0]);
411 return 0;
412}
413
414static int nl802154_get_wpan_phy(struct sk_buff *skb, struct genl_info *info)
415{
416 struct sk_buff *msg;
417 struct cfg802154_registered_device *rdev = info->user_ptr[0];
418
419 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
420 if (!msg)
421 return -ENOMEM;
422
423 if (nl802154_send_wpan_phy(rdev, NL802154_CMD_NEW_WPAN_PHY, msg,
424 info->snd_portid, info->snd_seq, 0) < 0) {
425 nlmsg_free(msg);
426 return -ENOBUFS;
427 }
428
429 return genlmsg_reply(msg, info);
430}
431
Alexander Aring4b96aea2014-11-09 08:36:55 +0100432static inline u64 wpan_dev_id(struct wpan_dev *wpan_dev)
433{
434 return (u64)wpan_dev->identifier |
435 ((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32);
436}
437
438static int
439nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
440 struct cfg802154_registered_device *rdev,
441 struct wpan_dev *wpan_dev)
442{
443 struct net_device *dev = wpan_dev->netdev;
444 void *hdr;
445
446 hdr = nl802154hdr_put(msg, portid, seq, flags,
447 NL802154_CMD_NEW_INTERFACE);
448 if (!hdr)
449 return -1;
450
451 if (dev &&
452 (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex) ||
453 nla_put_string(msg, NL802154_ATTR_IFNAME, dev->name)))
454 goto nla_put_failure;
455
456 if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
457 nla_put_u32(msg, NL802154_ATTR_IFTYPE, wpan_dev->iftype) ||
458 nla_put_u64(msg, NL802154_ATTR_WPAN_DEV, wpan_dev_id(wpan_dev)) ||
459 nla_put_u32(msg, NL802154_ATTR_GENERATION,
460 rdev->devlist_generation ^
461 (cfg802154_rdev_list_generation << 2)))
462 goto nla_put_failure;
463
464 /* address settings */
465 if (nla_put_le64(msg, NL802154_ATTR_EXTENDED_ADDR,
466 wpan_dev->extended_addr) ||
467 nla_put_le16(msg, NL802154_ATTR_SHORT_ADDR,
468 wpan_dev->short_addr) ||
469 nla_put_le16(msg, NL802154_ATTR_PAN_ID, wpan_dev->pan_id))
470 goto nla_put_failure;
471
472 /* ARET handling */
473 if (nla_put_s8(msg, NL802154_ATTR_MAX_FRAME_RETRIES,
474 wpan_dev->frame_retries) ||
475 nla_put_u8(msg, NL802154_ATTR_MAX_BE, wpan_dev->max_be) ||
476 nla_put_u8(msg, NL802154_ATTR_MAX_CSMA_BACKOFFS,
477 wpan_dev->csma_retries) ||
478 nla_put_u8(msg, NL802154_ATTR_MIN_BE, wpan_dev->min_be))
479 goto nla_put_failure;
480
481 /* listen before transmit */
482 if (nla_put_u8(msg, NL802154_ATTR_LBT_MODE, wpan_dev->lbt))
483 goto nla_put_failure;
484
485 return genlmsg_end(msg, hdr);
486
487nla_put_failure:
488 genlmsg_cancel(msg, hdr);
489 return -EMSGSIZE;
490}
491
492static int
493nl802154_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
494{
495 int wp_idx = 0;
496 int if_idx = 0;
497 int wp_start = cb->args[0];
498 int if_start = cb->args[1];
499 struct cfg802154_registered_device *rdev;
500 struct wpan_dev *wpan_dev;
501
502 rtnl_lock();
503 list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
504 /* TODO netns compare */
505 if (wp_idx < wp_start) {
506 wp_idx++;
507 continue;
508 }
509 if_idx = 0;
510
511 list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
512 if (if_idx < if_start) {
513 if_idx++;
514 continue;
515 }
516 if (nl802154_send_iface(skb, NETLINK_CB(cb->skb).portid,
517 cb->nlh->nlmsg_seq, NLM_F_MULTI,
518 rdev, wpan_dev) < 0) {
519 goto out;
520 }
521 if_idx++;
522 }
523
524 wp_idx++;
525 }
526out:
527 rtnl_unlock();
528
529 cb->args[0] = wp_idx;
530 cb->args[1] = if_idx;
531
532 return skb->len;
533}
534
535static int nl802154_get_interface(struct sk_buff *skb, struct genl_info *info)
536{
537 struct sk_buff *msg;
538 struct cfg802154_registered_device *rdev = info->user_ptr[0];
539 struct wpan_dev *wdev = info->user_ptr[1];
540
541 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
542 if (!msg)
543 return -ENOMEM;
544
545 if (nl802154_send_iface(msg, info->snd_portid, info->snd_seq, 0,
546 rdev, wdev) < 0) {
547 nlmsg_free(msg);
548 return -ENOBUFS;
549 }
550
551 return genlmsg_reply(msg, info);
552}
553
Alexander Aringf3ea5e42014-11-17 08:20:51 +0100554static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info)
555{
556 struct cfg802154_registered_device *rdev = info->user_ptr[0];
557 enum nl802154_iftype type = NL802154_IFTYPE_UNSPEC;
Alexander Aring0e575472014-11-17 08:20:52 +0100558 __le64 extended_addr = cpu_to_le64(0x0000000000000000ULL);
Alexander Aringf3ea5e42014-11-17 08:20:51 +0100559
560 /* TODO avoid failing a new interface
561 * creation due to pending removal?
562 */
563
564 if (!info->attrs[NL802154_ATTR_IFNAME])
565 return -EINVAL;
566
567 if (info->attrs[NL802154_ATTR_IFTYPE]) {
568 type = nla_get_u32(info->attrs[NL802154_ATTR_IFTYPE]);
569 if (type > NL802154_IFTYPE_MAX)
570 return -EINVAL;
571 }
572
Alexander Aring0e575472014-11-17 08:20:52 +0100573 /* TODO add nla_get_le64 to netlink */
574 if (info->attrs[NL802154_ATTR_EXTENDED_ADDR])
575 extended_addr = (__force __le64)nla_get_u64(
576 info->attrs[NL802154_ATTR_EXTENDED_ADDR]);
577
Alexander Aringf3ea5e42014-11-17 08:20:51 +0100578 if (!rdev->ops->add_virtual_intf)
579 return -EOPNOTSUPP;
580
581 return rdev_add_virtual_intf(rdev,
582 nla_data(info->attrs[NL802154_ATTR_IFNAME]),
Alexander Aring0e575472014-11-17 08:20:52 +0100583 type, extended_addr);
Alexander Aringf3ea5e42014-11-17 08:20:51 +0100584}
585
Alexander Aringb821ecd2014-11-17 08:20:53 +0100586static int nl802154_del_interface(struct sk_buff *skb, struct genl_info *info)
587{
588 struct cfg802154_registered_device *rdev = info->user_ptr[0];
589 struct wpan_dev *wpan_dev = info->user_ptr[1];
590
591 if (!rdev->ops->del_virtual_intf)
592 return -EOPNOTSUPP;
593
594 /* If we remove a wpan device without a netdev then clear
595 * user_ptr[1] so that nl802154_post_doit won't dereference it
596 * to check if it needs to do dev_put(). Otherwise it crashes
597 * since the wpan_dev has been freed, unlike with a netdev where
598 * we need the dev_put() for the netdev to really be freed.
599 */
600 if (!wpan_dev->netdev)
601 info->user_ptr[1] = NULL;
602
603 return rdev_del_virtual_intf(rdev, wpan_dev);
604}
605
Alexander Aringab0bd562014-11-12 03:36:55 +0100606static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info)
607{
608 struct cfg802154_registered_device *rdev = info->user_ptr[0];
609 u8 channel, page;
610
611 if (!info->attrs[NL802154_ATTR_PAGE] ||
612 !info->attrs[NL802154_ATTR_CHANNEL])
613 return -EINVAL;
614
615 page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]);
616 channel = nla_get_u8(info->attrs[NL802154_ATTR_CHANNEL]);
617
618 /* check 802.15.4 constraints */
Alexander Aringcb41c8d2014-11-17 08:20:54 +0100619 if (page > IEEE802154_MAX_PAGE || channel > IEEE802154_MAX_CHANNEL)
Alexander Aringab0bd562014-11-12 03:36:55 +0100620 return -EINVAL;
621
622 return rdev_set_channel(rdev, page, channel);
623}
624
Alexander Aring702bf372014-11-12 03:36:57 +0100625static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info)
626{
627 struct cfg802154_registered_device *rdev = info->user_ptr[0];
628 struct net_device *dev = info->user_ptr[1];
629 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
Alexander Aringee7b9052014-11-17 08:20:55 +0100630 __le16 pan_id;
Alexander Aring702bf372014-11-12 03:36:57 +0100631
632 /* conflict here while tx/rx calls */
633 if (netif_running(dev))
634 return -EBUSY;
635
636 /* don't change address fields on monitor */
637 if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
638 return -EINVAL;
639
640 if (!info->attrs[NL802154_ATTR_PAN_ID])
641 return -EINVAL;
642
Alexander Aringee7b9052014-11-17 08:20:55 +0100643 pan_id = nla_get_le16(info->attrs[NL802154_ATTR_PAN_ID]);
Alexander Aring702bf372014-11-12 03:36:57 +0100644
645 return rdev_set_pan_id(rdev, wpan_dev, pan_id);
646}
647
Alexander Aring9830c622014-11-12 03:36:58 +0100648static int nl802154_set_short_addr(struct sk_buff *skb, struct genl_info *info)
649{
650 struct cfg802154_registered_device *rdev = info->user_ptr[0];
651 struct net_device *dev = info->user_ptr[1];
652 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
Alexander Aringee7b9052014-11-17 08:20:55 +0100653 __le16 short_addr;
Alexander Aring9830c622014-11-12 03:36:58 +0100654
655 /* conflict here while tx/rx calls */
656 if (netif_running(dev))
657 return -EBUSY;
658
659 /* don't change address fields on monitor */
660 if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
661 return -EINVAL;
662
663 if (!info->attrs[NL802154_ATTR_SHORT_ADDR])
664 return -EINVAL;
665
Alexander Aringee7b9052014-11-17 08:20:55 +0100666 short_addr = nla_get_le16(info->attrs[NL802154_ATTR_SHORT_ADDR]);
Alexander Aring9830c622014-11-12 03:36:58 +0100667
668 return rdev_set_short_addr(rdev, wpan_dev, short_addr);
669}
670
Alexander Aring656a9992014-11-12 03:36:59 +0100671static int
672nl802154_set_backoff_exponent(struct sk_buff *skb, struct genl_info *info)
673{
674 struct cfg802154_registered_device *rdev = info->user_ptr[0];
675 struct net_device *dev = info->user_ptr[1];
676 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
677 u8 min_be, max_be;
678
679 /* should be set on netif open inside phy settings */
680 if (netif_running(dev))
681 return -EBUSY;
682
683 if (!info->attrs[NL802154_ATTR_MIN_BE] ||
684 !info->attrs[NL802154_ATTR_MAX_BE])
685 return -EINVAL;
686
687 min_be = nla_get_u8(info->attrs[NL802154_ATTR_MIN_BE]);
688 max_be = nla_get_u8(info->attrs[NL802154_ATTR_MAX_BE]);
689
690 /* check 802.15.4 constraints */
691 if (max_be < 3 || max_be > 8 || min_be > max_be)
692 return -EINVAL;
693
694 return rdev_set_backoff_exponent(rdev, wpan_dev, min_be, max_be);
695}
696
Alexander Aringa01ba762014-11-12 03:37:01 +0100697static int
698nl802154_set_max_csma_backoffs(struct sk_buff *skb, struct genl_info *info)
699{
700 struct cfg802154_registered_device *rdev = info->user_ptr[0];
701 struct net_device *dev = info->user_ptr[1];
702 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
703 u8 max_csma_backoffs;
704
705 /* conflict here while other running iface settings */
706 if (netif_running(dev))
707 return -EBUSY;
708
709 if (!info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS])
710 return -EINVAL;
711
712 max_csma_backoffs = nla_get_u8(
713 info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]);
714
715 /* check 802.15.4 constraints */
716 if (max_csma_backoffs > 5)
717 return -EINVAL;
718
719 return rdev_set_max_csma_backoffs(rdev, wpan_dev, max_csma_backoffs);
720}
721
Alexander Aring17a3a462014-11-12 03:37:03 +0100722static int
723nl802154_set_max_frame_retries(struct sk_buff *skb, struct genl_info *info)
724{
725 struct cfg802154_registered_device *rdev = info->user_ptr[0];
726 struct net_device *dev = info->user_ptr[1];
727 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
728 s8 max_frame_retries;
729
730 if (netif_running(dev))
731 return -EBUSY;
732
733 if (!info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES])
734 return -EINVAL;
735
736 max_frame_retries = nla_get_s8(
737 info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]);
738
739 /* check 802.15.4 constraints */
740 if (max_frame_retries < -1 || max_frame_retries > 7)
741 return -EINVAL;
742
743 return rdev_set_max_frame_retries(rdev, wpan_dev, max_frame_retries);
744}
745
Alexander Aringc8937a1d2014-11-12 03:37:05 +0100746static int nl802154_set_lbt_mode(struct sk_buff *skb, struct genl_info *info)
747{
748 struct cfg802154_registered_device *rdev = info->user_ptr[0];
749 struct net_device *dev = info->user_ptr[1];
750 struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
751 bool mode;
752
753 if (netif_running(dev))
754 return -EBUSY;
755
756 if (!info->attrs[NL802154_ATTR_LBT_MODE])
757 return -EINVAL;
758
759 mode = !!nla_get_u8(info->attrs[NL802154_ATTR_LBT_MODE]);
760 return rdev_set_lbt_mode(rdev, wpan_dev, mode);
761}
762
Alexander Aring79fe1a22014-11-09 08:36:53 +0100763#define NL802154_FLAG_NEED_WPAN_PHY 0x01
764#define NL802154_FLAG_NEED_NETDEV 0x02
765#define NL802154_FLAG_NEED_RTNL 0x04
766#define NL802154_FLAG_CHECK_NETDEV_UP 0x08
767#define NL802154_FLAG_NEED_NETDEV_UP (NL802154_FLAG_NEED_NETDEV |\
768 NL802154_FLAG_CHECK_NETDEV_UP)
769#define NL802154_FLAG_NEED_WPAN_DEV 0x10
770#define NL802154_FLAG_NEED_WPAN_DEV_UP (NL802154_FLAG_NEED_WPAN_DEV |\
771 NL802154_FLAG_CHECK_NETDEV_UP)
772
773static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
774 struct genl_info *info)
775{
776 struct cfg802154_registered_device *rdev;
777 struct wpan_dev *wpan_dev;
778 struct net_device *dev;
779 bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
780
781 if (rtnl)
782 rtnl_lock();
783
784 if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
785 rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
786 if (IS_ERR(rdev)) {
787 if (rtnl)
788 rtnl_unlock();
789 return PTR_ERR(rdev);
790 }
791 info->user_ptr[0] = rdev;
792 } else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
793 ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
794 ASSERT_RTNL();
795 wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
796 info->attrs);
797 if (IS_ERR(wpan_dev)) {
798 if (rtnl)
799 rtnl_unlock();
800 return PTR_ERR(wpan_dev);
801 }
802
803 dev = wpan_dev->netdev;
804 rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
805
806 if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
807 if (!dev) {
808 if (rtnl)
809 rtnl_unlock();
810 return -EINVAL;
811 }
812
813 info->user_ptr[1] = dev;
814 } else {
815 info->user_ptr[1] = wpan_dev;
816 }
817
818 if (dev) {
819 if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
820 !netif_running(dev)) {
821 if (rtnl)
822 rtnl_unlock();
823 return -ENETDOWN;
824 }
825
826 dev_hold(dev);
827 }
828
829 info->user_ptr[0] = rdev;
830 }
831
832 return 0;
833}
834
835static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
836 struct genl_info *info)
837{
838 if (info->user_ptr[1]) {
839 if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
840 struct wpan_dev *wpan_dev = info->user_ptr[1];
841
842 if (wpan_dev->netdev)
843 dev_put(wpan_dev->netdev);
844 } else {
845 dev_put(info->user_ptr[1]);
846 }
847 }
848
849 if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
850 rtnl_unlock();
851}
852
853static const struct genl_ops nl802154_ops[] = {
Alexander Aringca20ce202014-11-09 08:36:54 +0100854 {
855 .cmd = NL802154_CMD_GET_WPAN_PHY,
856 .doit = nl802154_get_wpan_phy,
857 .dumpit = nl802154_dump_wpan_phy,
858 .done = nl802154_dump_wpan_phy_done,
859 .policy = nl802154_policy,
860 /* can be retrieved by unprivileged users */
861 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
862 NL802154_FLAG_NEED_RTNL,
863 },
Alexander Aring4b96aea2014-11-09 08:36:55 +0100864 {
865 .cmd = NL802154_CMD_GET_INTERFACE,
866 .doit = nl802154_get_interface,
867 .dumpit = nl802154_dump_interface,
868 .policy = nl802154_policy,
869 /* can be retrieved by unprivileged users */
870 .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
871 NL802154_FLAG_NEED_RTNL,
872 },
Alexander Aringab0bd562014-11-12 03:36:55 +0100873 {
Alexander Aringf3ea5e42014-11-17 08:20:51 +0100874 .cmd = NL802154_CMD_NEW_INTERFACE,
875 .doit = nl802154_new_interface,
876 .policy = nl802154_policy,
877 .flags = GENL_ADMIN_PERM,
878 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
879 NL802154_FLAG_NEED_RTNL,
880 },
881 {
Alexander Aringb821ecd2014-11-17 08:20:53 +0100882 .cmd = NL802154_CMD_DEL_INTERFACE,
883 .doit = nl802154_del_interface,
884 .policy = nl802154_policy,
885 .flags = GENL_ADMIN_PERM,
886 .internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
887 NL802154_FLAG_NEED_RTNL,
888 },
889 {
Alexander Aringab0bd562014-11-12 03:36:55 +0100890 .cmd = NL802154_CMD_SET_CHANNEL,
891 .doit = nl802154_set_channel,
892 .policy = nl802154_policy,
893 .flags = GENL_ADMIN_PERM,
894 .internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
895 NL802154_FLAG_NEED_RTNL,
896 },
Alexander Aring702bf372014-11-12 03:36:57 +0100897 {
898 .cmd = NL802154_CMD_SET_PAN_ID,
899 .doit = nl802154_set_pan_id,
900 .policy = nl802154_policy,
901 .flags = GENL_ADMIN_PERM,
902 .internal_flags = NL802154_FLAG_NEED_NETDEV |
903 NL802154_FLAG_NEED_RTNL,
904 },
Alexander Aring9830c622014-11-12 03:36:58 +0100905 {
906 .cmd = NL802154_CMD_SET_SHORT_ADDR,
907 .doit = nl802154_set_short_addr,
908 .policy = nl802154_policy,
909 .flags = GENL_ADMIN_PERM,
910 .internal_flags = NL802154_FLAG_NEED_NETDEV |
911 NL802154_FLAG_NEED_RTNL,
912 },
Alexander Aring656a9992014-11-12 03:36:59 +0100913 {
914 .cmd = NL802154_CMD_SET_BACKOFF_EXPONENT,
915 .doit = nl802154_set_backoff_exponent,
916 .policy = nl802154_policy,
917 .flags = GENL_ADMIN_PERM,
918 .internal_flags = NL802154_FLAG_NEED_NETDEV |
919 NL802154_FLAG_NEED_RTNL,
920 },
Alexander Aringa01ba762014-11-12 03:37:01 +0100921 {
922 .cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
923 .doit = nl802154_set_max_csma_backoffs,
924 .policy = nl802154_policy,
925 .flags = GENL_ADMIN_PERM,
926 .internal_flags = NL802154_FLAG_NEED_NETDEV |
927 NL802154_FLAG_NEED_RTNL,
928 },
Alexander Aring17a3a462014-11-12 03:37:03 +0100929 {
930 .cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES,
931 .doit = nl802154_set_max_frame_retries,
932 .policy = nl802154_policy,
933 .flags = GENL_ADMIN_PERM,
934 .internal_flags = NL802154_FLAG_NEED_NETDEV |
935 NL802154_FLAG_NEED_RTNL,
936 },
Alexander Aringc8937a1d2014-11-12 03:37:05 +0100937 {
938 .cmd = NL802154_CMD_SET_LBT_MODE,
939 .doit = nl802154_set_lbt_mode,
940 .policy = nl802154_policy,
941 .flags = GENL_ADMIN_PERM,
942 .internal_flags = NL802154_FLAG_NEED_NETDEV |
943 NL802154_FLAG_NEED_RTNL,
944 },
Alexander Aring79fe1a22014-11-09 08:36:53 +0100945};
946
947/* initialisation/exit functions */
948int nl802154_init(void)
949{
950 return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops,
951 nl802154_mcgrps);
952}
953
954void nl802154_exit(void)
955{
956 genl_unregister_family(&nl802154_fam);
957}