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
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] = {
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 */
231static 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
238static int
239nl802154_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
259static 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
308finish:
309	genlmsg_end(msg, hdr);
310	return 0;
311
312nla_put_failure:
313	genlmsg_cancel(msg, hdr);
314	return -EMSGSIZE;
315}
316
317struct nl802154_dump_wpan_phy_state {
318	s64 filter_wpan_phy;
319	long start;
320
321};
322
323static 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
360static int
361nl802154_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
416static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb)
417{
418	kfree((void *)cb->args[0]);
419	return 0;
420}
421
422static 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
440static 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
446static int
447nl802154_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
496nla_put_failure:
497	genlmsg_cancel(msg, hdr);
498	return -EMSGSIZE;
499}
500
501static int
502nl802154_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	}
535out:
536	rtnl_unlock();
537
538	cb->args[0] = wp_idx;
539	cb->args[1] = if_idx;
540
541	return skb->len;
542}
543
544static 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
563static 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				     NET_NAME_USER, type, extended_addr);
593}
594
595static 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
615static 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
634static 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
659static 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
682static 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
705static int
706nl802154_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
731static int
732nl802154_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
756static int
757nl802154_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
780static 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
807static 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
869static 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
887static 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 */
990int nl802154_init(void)
991{
992	return genl_register_family_with_ops_groups(&nl802154_fam, nl802154_ops,
993						    nl802154_mcgrps);
994}
995
996void nl802154_exit(void)
997{
998	genl_unregister_family(&nl802154_fam);
999}
1000