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