xref: /openbmc/linux/net/ieee802154/nl802154.c (revision 1f9f6a78)
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"
26 #include "rdev-ops.h"
27 #include "core.h"
28 
29 static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
30 			     struct genl_info *info);
31 
32 static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
33 			       struct genl_info *info);
34 
35 /* the netlink family */
36 static 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 */
48 enum nl802154_multicast_groups {
49 	NL802154_MCGRP_CONFIG,
50 };
51 
52 static const struct genl_multicast_group nl802154_mcgrps[] = {
53 	[NL802154_MCGRP_CONFIG] = { .name = "config", },
54 };
55 
56 /* returns ERR_PTR values */
57 static 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 
111 static 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  */
189 static struct cfg802154_registered_device *
190 cfg802154_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 */
196 static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = {
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 },
202 	[NL802154_ATTR_IFTYPE] = { .type = NLA_U32 },
203 	[NL802154_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
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 
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, },
215 
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, },
227 };
228 
229 /* message building helper */
230 static 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 
237 static int
238 nl802154_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 
248 	for (page = 0; page <= IEEE802154_MAX_PAGE; page++) {
249 		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 
258 static 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 
301 finish:
302 	return genlmsg_end(msg, hdr);
303 
304 nla_put_failure:
305 	genlmsg_cancel(msg, hdr);
306 	return -EMSGSIZE;
307 }
308 
309 struct nl802154_dump_wpan_phy_state {
310 	s64 filter_wpan_phy;
311 	long start;
312 
313 };
314 
315 static 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 
352 static int
353 nl802154_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 
408 static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb)
409 {
410 	kfree((void *)cb->args[0]);
411 	return 0;
412 }
413 
414 static 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 
432 static 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 
438 static int
439 nl802154_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 
487 nla_put_failure:
488 	genlmsg_cancel(msg, hdr);
489 	return -EMSGSIZE;
490 }
491 
492 static int
493 nl802154_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 	}
526 out:
527 	rtnl_unlock();
528 
529 	cb->args[0] = wp_idx;
530 	cb->args[1] = if_idx;
531 
532 	return skb->len;
533 }
534 
535 static 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 
554 static 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;
558 	__le64 extended_addr = cpu_to_le64(0x0000000000000000ULL);
559 
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 
573 	/* 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 
578 	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]),
583 				     type, extended_addr);
584 }
585 
586 static 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 
606 static 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 */
619 	if (page > IEEE802154_MAX_PAGE || channel > IEEE802154_MAX_CHANNEL)
620 		return -EINVAL;
621 
622 	return rdev_set_channel(rdev, page, channel);
623 }
624 
625 static 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;
630 	__le16 pan_id;
631 
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 
643 	pan_id = nla_get_le16(info->attrs[NL802154_ATTR_PAN_ID]);
644 
645 	return rdev_set_pan_id(rdev, wpan_dev, pan_id);
646 }
647 
648 static 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;
653 	__le16 short_addr;
654 
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 
666 	short_addr = nla_get_le16(info->attrs[NL802154_ATTR_SHORT_ADDR]);
667 
668 	return rdev_set_short_addr(rdev, wpan_dev, short_addr);
669 }
670 
671 static int
672 nl802154_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 
697 static int
698 nl802154_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 
722 static int
723 nl802154_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 
746 static 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 
763 #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 
773 static 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 
835 static 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 
853 static const struct genl_ops nl802154_ops[] = {
854 	{
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 	},
864 	{
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 	},
873 	{
874 		.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 	{
882 		.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 	{
890 		.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 	},
897 	{
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 	},
905 	{
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 	},
913 	{
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 	},
921 	{
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 	},
929 	{
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 	},
937 	{
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 	},
945 };
946 
947 /* initialisation/exit functions */
948 int nl802154_init(void)
949 {
950 	return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops,
951 						    nl802154_mcgrps);
952 }
953 
954 void nl802154_exit(void)
955 {
956 	genl_unregister_family(&nl802154_fam);
957 }
958