xref: /OK3568_Linux_fs/kernel/net/ieee802154/nl802154.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Authors:
5*4882a593Smuzhiyun  * Alexander Aring <aar@pengutronix.de>
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Based on: net/wireless/nl80211.c
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <linux/rtnetlink.h>
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <net/cfg802154.h>
13*4882a593Smuzhiyun #include <net/genetlink.h>
14*4882a593Smuzhiyun #include <net/mac802154.h>
15*4882a593Smuzhiyun #include <net/netlink.h>
16*4882a593Smuzhiyun #include <net/nl802154.h>
17*4882a593Smuzhiyun #include <net/sock.h>
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun #include "nl802154.h"
20*4882a593Smuzhiyun #include "rdev-ops.h"
21*4882a593Smuzhiyun #include "core.h"
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun /* the netlink family */
24*4882a593Smuzhiyun static struct genl_family nl802154_fam;
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun /* multicast groups */
27*4882a593Smuzhiyun enum nl802154_multicast_groups {
28*4882a593Smuzhiyun 	NL802154_MCGRP_CONFIG,
29*4882a593Smuzhiyun };
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun static const struct genl_multicast_group nl802154_mcgrps[] = {
32*4882a593Smuzhiyun 	[NL802154_MCGRP_CONFIG] = { .name = "config", },
33*4882a593Smuzhiyun };
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun /* returns ERR_PTR values */
36*4882a593Smuzhiyun static struct wpan_dev *
__cfg802154_wpan_dev_from_attrs(struct net * netns,struct nlattr ** attrs)37*4882a593Smuzhiyun __cfg802154_wpan_dev_from_attrs(struct net *netns, struct nlattr **attrs)
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev;
40*4882a593Smuzhiyun 	struct wpan_dev *result = NULL;
41*4882a593Smuzhiyun 	bool have_ifidx = attrs[NL802154_ATTR_IFINDEX];
42*4882a593Smuzhiyun 	bool have_wpan_dev_id = attrs[NL802154_ATTR_WPAN_DEV];
43*4882a593Smuzhiyun 	u64 wpan_dev_id;
44*4882a593Smuzhiyun 	int wpan_phy_idx = -1;
45*4882a593Smuzhiyun 	int ifidx = -1;
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 	ASSERT_RTNL();
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	if (!have_ifidx && !have_wpan_dev_id)
50*4882a593Smuzhiyun 		return ERR_PTR(-EINVAL);
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	if (have_ifidx)
53*4882a593Smuzhiyun 		ifidx = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
54*4882a593Smuzhiyun 	if (have_wpan_dev_id) {
55*4882a593Smuzhiyun 		wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
56*4882a593Smuzhiyun 		wpan_phy_idx = wpan_dev_id >> 32;
57*4882a593Smuzhiyun 	}
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
60*4882a593Smuzhiyun 		struct wpan_dev *wpan_dev;
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 		if (wpan_phy_net(&rdev->wpan_phy) != netns)
63*4882a593Smuzhiyun 			continue;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 		if (have_wpan_dev_id && rdev->wpan_phy_idx != wpan_phy_idx)
66*4882a593Smuzhiyun 			continue;
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 		list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
69*4882a593Smuzhiyun 			if (have_ifidx && wpan_dev->netdev &&
70*4882a593Smuzhiyun 			    wpan_dev->netdev->ifindex == ifidx) {
71*4882a593Smuzhiyun 				result = wpan_dev;
72*4882a593Smuzhiyun 				break;
73*4882a593Smuzhiyun 			}
74*4882a593Smuzhiyun 			if (have_wpan_dev_id &&
75*4882a593Smuzhiyun 			    wpan_dev->identifier == (u32)wpan_dev_id) {
76*4882a593Smuzhiyun 				result = wpan_dev;
77*4882a593Smuzhiyun 				break;
78*4882a593Smuzhiyun 			}
79*4882a593Smuzhiyun 		}
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 		if (result)
82*4882a593Smuzhiyun 			break;
83*4882a593Smuzhiyun 	}
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	if (result)
86*4882a593Smuzhiyun 		return result;
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	return ERR_PTR(-ENODEV);
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun static struct cfg802154_registered_device *
__cfg802154_rdev_from_attrs(struct net * netns,struct nlattr ** attrs)92*4882a593Smuzhiyun __cfg802154_rdev_from_attrs(struct net *netns, struct nlattr **attrs)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = NULL, *tmp;
95*4882a593Smuzhiyun 	struct net_device *netdev;
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	ASSERT_RTNL();
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	if (!attrs[NL802154_ATTR_WPAN_PHY] &&
100*4882a593Smuzhiyun 	    !attrs[NL802154_ATTR_IFINDEX] &&
101*4882a593Smuzhiyun 	    !attrs[NL802154_ATTR_WPAN_DEV])
102*4882a593Smuzhiyun 		return ERR_PTR(-EINVAL);
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	if (attrs[NL802154_ATTR_WPAN_PHY])
105*4882a593Smuzhiyun 		rdev = cfg802154_rdev_by_wpan_phy_idx(
106*4882a593Smuzhiyun 				nla_get_u32(attrs[NL802154_ATTR_WPAN_PHY]));
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	if (attrs[NL802154_ATTR_WPAN_DEV]) {
109*4882a593Smuzhiyun 		u64 wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]);
110*4882a593Smuzhiyun 		struct wpan_dev *wpan_dev;
111*4882a593Smuzhiyun 		bool found = false;
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 		tmp = cfg802154_rdev_by_wpan_phy_idx(wpan_dev_id >> 32);
114*4882a593Smuzhiyun 		if (tmp) {
115*4882a593Smuzhiyun 			/* make sure wpan_dev exists */
116*4882a593Smuzhiyun 			list_for_each_entry(wpan_dev, &tmp->wpan_dev_list, list) {
117*4882a593Smuzhiyun 				if (wpan_dev->identifier != (u32)wpan_dev_id)
118*4882a593Smuzhiyun 					continue;
119*4882a593Smuzhiyun 				found = true;
120*4882a593Smuzhiyun 				break;
121*4882a593Smuzhiyun 			}
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 			if (!found)
124*4882a593Smuzhiyun 				tmp = NULL;
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 			if (rdev && tmp != rdev)
127*4882a593Smuzhiyun 				return ERR_PTR(-EINVAL);
128*4882a593Smuzhiyun 			rdev = tmp;
129*4882a593Smuzhiyun 		}
130*4882a593Smuzhiyun 	}
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 	if (attrs[NL802154_ATTR_IFINDEX]) {
133*4882a593Smuzhiyun 		int ifindex = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]);
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 		netdev = __dev_get_by_index(netns, ifindex);
136*4882a593Smuzhiyun 		if (netdev) {
137*4882a593Smuzhiyun 			if (netdev->ieee802154_ptr)
138*4882a593Smuzhiyun 				tmp = wpan_phy_to_rdev(
139*4882a593Smuzhiyun 						netdev->ieee802154_ptr->wpan_phy);
140*4882a593Smuzhiyun 			else
141*4882a593Smuzhiyun 				tmp = NULL;
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 			/* not wireless device -- return error */
144*4882a593Smuzhiyun 			if (!tmp)
145*4882a593Smuzhiyun 				return ERR_PTR(-EINVAL);
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 			/* mismatch -- return error */
148*4882a593Smuzhiyun 			if (rdev && tmp != rdev)
149*4882a593Smuzhiyun 				return ERR_PTR(-EINVAL);
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 			rdev = tmp;
152*4882a593Smuzhiyun 		}
153*4882a593Smuzhiyun 	}
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	if (!rdev)
156*4882a593Smuzhiyun 		return ERR_PTR(-ENODEV);
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun 	if (netns != wpan_phy_net(&rdev->wpan_phy))
159*4882a593Smuzhiyun 		return ERR_PTR(-ENODEV);
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 	return rdev;
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun /* This function returns a pointer to the driver
165*4882a593Smuzhiyun  * that the genl_info item that is passed refers to.
166*4882a593Smuzhiyun  *
167*4882a593Smuzhiyun  * The result of this can be a PTR_ERR and hence must
168*4882a593Smuzhiyun  * be checked with IS_ERR() for errors.
169*4882a593Smuzhiyun  */
170*4882a593Smuzhiyun static struct cfg802154_registered_device *
cfg802154_get_dev_from_info(struct net * netns,struct genl_info * info)171*4882a593Smuzhiyun cfg802154_get_dev_from_info(struct net *netns, struct genl_info *info)
172*4882a593Smuzhiyun {
173*4882a593Smuzhiyun 	return __cfg802154_rdev_from_attrs(netns, info->attrs);
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun /* policy for the attributes */
177*4882a593Smuzhiyun static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = {
178*4882a593Smuzhiyun 	[NL802154_ATTR_WPAN_PHY] = { .type = NLA_U32 },
179*4882a593Smuzhiyun 	[NL802154_ATTR_WPAN_PHY_NAME] = { .type = NLA_NUL_STRING,
180*4882a593Smuzhiyun 					  .len = 20-1 },
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	[NL802154_ATTR_IFINDEX] = { .type = NLA_U32 },
183*4882a593Smuzhiyun 	[NL802154_ATTR_IFTYPE] = { .type = NLA_U32 },
184*4882a593Smuzhiyun 	[NL802154_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	[NL802154_ATTR_WPAN_DEV] = { .type = NLA_U64 },
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	[NL802154_ATTR_PAGE] = { .type = NLA_U8, },
189*4882a593Smuzhiyun 	[NL802154_ATTR_CHANNEL] = { .type = NLA_U8, },
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	[NL802154_ATTR_TX_POWER] = { .type = NLA_S32, },
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	[NL802154_ATTR_CCA_MODE] = { .type = NLA_U32, },
194*4882a593Smuzhiyun 	[NL802154_ATTR_CCA_OPT] = { .type = NLA_U32, },
195*4882a593Smuzhiyun 	[NL802154_ATTR_CCA_ED_LEVEL] = { .type = NLA_S32, },
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun 	[NL802154_ATTR_SUPPORTED_CHANNEL] = { .type = NLA_U32, },
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	[NL802154_ATTR_PAN_ID] = { .type = NLA_U16, },
200*4882a593Smuzhiyun 	[NL802154_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
201*4882a593Smuzhiyun 	[NL802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, },
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	[NL802154_ATTR_MIN_BE] = { .type = NLA_U8, },
204*4882a593Smuzhiyun 	[NL802154_ATTR_MAX_BE] = { .type = NLA_U8, },
205*4882a593Smuzhiyun 	[NL802154_ATTR_MAX_CSMA_BACKOFFS] = { .type = NLA_U8, },
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	[NL802154_ATTR_MAX_FRAME_RETRIES] = { .type = NLA_S8, },
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	[NL802154_ATTR_LBT_MODE] = { .type = NLA_U8, },
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	[NL802154_ATTR_WPAN_PHY_CAPS] = { .type = NLA_NESTED },
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	[NL802154_ATTR_SUPPORTED_COMMANDS] = { .type = NLA_NESTED },
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 	[NL802154_ATTR_ACKREQ_DEFAULT] = { .type = NLA_U8 },
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	[NL802154_ATTR_PID] = { .type = NLA_U32 },
218*4882a593Smuzhiyun 	[NL802154_ATTR_NETNS_FD] = { .type = NLA_U32 },
219*4882a593Smuzhiyun #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
220*4882a593Smuzhiyun 	[NL802154_ATTR_SEC_ENABLED] = { .type = NLA_U8, },
221*4882a593Smuzhiyun 	[NL802154_ATTR_SEC_OUT_LEVEL] = { .type = NLA_U32, },
222*4882a593Smuzhiyun 	[NL802154_ATTR_SEC_OUT_KEY_ID] = { .type = NLA_NESTED, },
223*4882a593Smuzhiyun 	[NL802154_ATTR_SEC_FRAME_COUNTER] = { .type = NLA_U32 },
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 	[NL802154_ATTR_SEC_LEVEL] = { .type = NLA_NESTED },
226*4882a593Smuzhiyun 	[NL802154_ATTR_SEC_DEVICE] = { .type = NLA_NESTED },
227*4882a593Smuzhiyun 	[NL802154_ATTR_SEC_DEVKEY] = { .type = NLA_NESTED },
228*4882a593Smuzhiyun 	[NL802154_ATTR_SEC_KEY] = { .type = NLA_NESTED },
229*4882a593Smuzhiyun #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
230*4882a593Smuzhiyun };
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
233*4882a593Smuzhiyun static int
nl802154_prepare_wpan_dev_dump(struct sk_buff * skb,struct netlink_callback * cb,struct cfg802154_registered_device ** rdev,struct wpan_dev ** wpan_dev)234*4882a593Smuzhiyun nl802154_prepare_wpan_dev_dump(struct sk_buff *skb,
235*4882a593Smuzhiyun 			       struct netlink_callback *cb,
236*4882a593Smuzhiyun 			       struct cfg802154_registered_device **rdev,
237*4882a593Smuzhiyun 			       struct wpan_dev **wpan_dev)
238*4882a593Smuzhiyun {
239*4882a593Smuzhiyun 	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
240*4882a593Smuzhiyun 	int err;
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 	rtnl_lock();
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	if (!cb->args[0]) {
245*4882a593Smuzhiyun 		*wpan_dev = __cfg802154_wpan_dev_from_attrs(sock_net(skb->sk),
246*4882a593Smuzhiyun 							    info->attrs);
247*4882a593Smuzhiyun 		if (IS_ERR(*wpan_dev)) {
248*4882a593Smuzhiyun 			err = PTR_ERR(*wpan_dev);
249*4882a593Smuzhiyun 			goto out_unlock;
250*4882a593Smuzhiyun 		}
251*4882a593Smuzhiyun 		*rdev = wpan_phy_to_rdev((*wpan_dev)->wpan_phy);
252*4882a593Smuzhiyun 		/* 0 is the first index - add 1 to parse only once */
253*4882a593Smuzhiyun 		cb->args[0] = (*rdev)->wpan_phy_idx + 1;
254*4882a593Smuzhiyun 		cb->args[1] = (*wpan_dev)->identifier;
255*4882a593Smuzhiyun 	} else {
256*4882a593Smuzhiyun 		/* subtract the 1 again here */
257*4882a593Smuzhiyun 		struct wpan_phy *wpan_phy = wpan_phy_idx_to_wpan_phy(cb->args[0] - 1);
258*4882a593Smuzhiyun 		struct wpan_dev *tmp;
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 		if (!wpan_phy) {
261*4882a593Smuzhiyun 			err = -ENODEV;
262*4882a593Smuzhiyun 			goto out_unlock;
263*4882a593Smuzhiyun 		}
264*4882a593Smuzhiyun 		*rdev = wpan_phy_to_rdev(wpan_phy);
265*4882a593Smuzhiyun 		*wpan_dev = NULL;
266*4882a593Smuzhiyun 
267*4882a593Smuzhiyun 		list_for_each_entry(tmp, &(*rdev)->wpan_dev_list, list) {
268*4882a593Smuzhiyun 			if (tmp->identifier == cb->args[1]) {
269*4882a593Smuzhiyun 				*wpan_dev = tmp;
270*4882a593Smuzhiyun 				break;
271*4882a593Smuzhiyun 			}
272*4882a593Smuzhiyun 		}
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 		if (!*wpan_dev) {
275*4882a593Smuzhiyun 			err = -ENODEV;
276*4882a593Smuzhiyun 			goto out_unlock;
277*4882a593Smuzhiyun 		}
278*4882a593Smuzhiyun 	}
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	return 0;
281*4882a593Smuzhiyun  out_unlock:
282*4882a593Smuzhiyun 	rtnl_unlock();
283*4882a593Smuzhiyun 	return err;
284*4882a593Smuzhiyun }
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun static void
nl802154_finish_wpan_dev_dump(struct cfg802154_registered_device * rdev)287*4882a593Smuzhiyun nl802154_finish_wpan_dev_dump(struct cfg802154_registered_device *rdev)
288*4882a593Smuzhiyun {
289*4882a593Smuzhiyun 	rtnl_unlock();
290*4882a593Smuzhiyun }
291*4882a593Smuzhiyun #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun /* message building helper */
nl802154hdr_put(struct sk_buff * skb,u32 portid,u32 seq,int flags,u8 cmd)294*4882a593Smuzhiyun static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
295*4882a593Smuzhiyun 				    int flags, u8 cmd)
296*4882a593Smuzhiyun {
297*4882a593Smuzhiyun 	/* since there is no private header just add the generic one */
298*4882a593Smuzhiyun 	return genlmsg_put(skb, portid, seq, &nl802154_fam, flags, cmd);
299*4882a593Smuzhiyun }
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun static int
nl802154_put_flags(struct sk_buff * msg,int attr,u32 mask)302*4882a593Smuzhiyun nl802154_put_flags(struct sk_buff *msg, int attr, u32 mask)
303*4882a593Smuzhiyun {
304*4882a593Smuzhiyun 	struct nlattr *nl_flags = nla_nest_start_noflag(msg, attr);
305*4882a593Smuzhiyun 	int i;
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	if (!nl_flags)
308*4882a593Smuzhiyun 		return -ENOBUFS;
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	i = 0;
311*4882a593Smuzhiyun 	while (mask) {
312*4882a593Smuzhiyun 		if ((mask & 1) && nla_put_flag(msg, i))
313*4882a593Smuzhiyun 			return -ENOBUFS;
314*4882a593Smuzhiyun 
315*4882a593Smuzhiyun 		mask >>= 1;
316*4882a593Smuzhiyun 		i++;
317*4882a593Smuzhiyun 	}
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 	nla_nest_end(msg, nl_flags);
320*4882a593Smuzhiyun 	return 0;
321*4882a593Smuzhiyun }
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun static int
nl802154_send_wpan_phy_channels(struct cfg802154_registered_device * rdev,struct sk_buff * msg)324*4882a593Smuzhiyun nl802154_send_wpan_phy_channels(struct cfg802154_registered_device *rdev,
325*4882a593Smuzhiyun 				struct sk_buff *msg)
326*4882a593Smuzhiyun {
327*4882a593Smuzhiyun 	struct nlattr *nl_page;
328*4882a593Smuzhiyun 	unsigned long page;
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 	nl_page = nla_nest_start_noflag(msg, NL802154_ATTR_CHANNELS_SUPPORTED);
331*4882a593Smuzhiyun 	if (!nl_page)
332*4882a593Smuzhiyun 		return -ENOBUFS;
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun 	for (page = 0; page <= IEEE802154_MAX_PAGE; page++) {
335*4882a593Smuzhiyun 		if (nla_put_u32(msg, NL802154_ATTR_SUPPORTED_CHANNEL,
336*4882a593Smuzhiyun 				rdev->wpan_phy.supported.channels[page]))
337*4882a593Smuzhiyun 			return -ENOBUFS;
338*4882a593Smuzhiyun 	}
339*4882a593Smuzhiyun 	nla_nest_end(msg, nl_page);
340*4882a593Smuzhiyun 
341*4882a593Smuzhiyun 	return 0;
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun static int
nl802154_put_capabilities(struct sk_buff * msg,struct cfg802154_registered_device * rdev)345*4882a593Smuzhiyun nl802154_put_capabilities(struct sk_buff *msg,
346*4882a593Smuzhiyun 			  struct cfg802154_registered_device *rdev)
347*4882a593Smuzhiyun {
348*4882a593Smuzhiyun 	const struct wpan_phy_supported *caps = &rdev->wpan_phy.supported;
349*4882a593Smuzhiyun 	struct nlattr *nl_caps, *nl_channels;
350*4882a593Smuzhiyun 	int i;
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun 	nl_caps = nla_nest_start_noflag(msg, NL802154_ATTR_WPAN_PHY_CAPS);
353*4882a593Smuzhiyun 	if (!nl_caps)
354*4882a593Smuzhiyun 		return -ENOBUFS;
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 	nl_channels = nla_nest_start_noflag(msg, NL802154_CAP_ATTR_CHANNELS);
357*4882a593Smuzhiyun 	if (!nl_channels)
358*4882a593Smuzhiyun 		return -ENOBUFS;
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	for (i = 0; i <= IEEE802154_MAX_PAGE; i++) {
361*4882a593Smuzhiyun 		if (caps->channels[i]) {
362*4882a593Smuzhiyun 			if (nl802154_put_flags(msg, i, caps->channels[i]))
363*4882a593Smuzhiyun 				return -ENOBUFS;
364*4882a593Smuzhiyun 		}
365*4882a593Smuzhiyun 	}
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun 	nla_nest_end(msg, nl_channels);
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_ED_LEVEL) {
370*4882a593Smuzhiyun 		struct nlattr *nl_ed_lvls;
371*4882a593Smuzhiyun 
372*4882a593Smuzhiyun 		nl_ed_lvls = nla_nest_start_noflag(msg,
373*4882a593Smuzhiyun 						   NL802154_CAP_ATTR_CCA_ED_LEVELS);
374*4882a593Smuzhiyun 		if (!nl_ed_lvls)
375*4882a593Smuzhiyun 			return -ENOBUFS;
376*4882a593Smuzhiyun 
377*4882a593Smuzhiyun 		for (i = 0; i < caps->cca_ed_levels_size; i++) {
378*4882a593Smuzhiyun 			if (nla_put_s32(msg, i, caps->cca_ed_levels[i]))
379*4882a593Smuzhiyun 				return -ENOBUFS;
380*4882a593Smuzhiyun 		}
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 		nla_nest_end(msg, nl_ed_lvls);
383*4882a593Smuzhiyun 	}
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 	if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_TXPOWER) {
386*4882a593Smuzhiyun 		struct nlattr *nl_tx_pwrs;
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 		nl_tx_pwrs = nla_nest_start_noflag(msg,
389*4882a593Smuzhiyun 						   NL802154_CAP_ATTR_TX_POWERS);
390*4882a593Smuzhiyun 		if (!nl_tx_pwrs)
391*4882a593Smuzhiyun 			return -ENOBUFS;
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 		for (i = 0; i < caps->tx_powers_size; i++) {
394*4882a593Smuzhiyun 			if (nla_put_s32(msg, i, caps->tx_powers[i]))
395*4882a593Smuzhiyun 				return -ENOBUFS;
396*4882a593Smuzhiyun 		}
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun 		nla_nest_end(msg, nl_tx_pwrs);
399*4882a593Smuzhiyun 	}
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE) {
402*4882a593Smuzhiyun 		if (nl802154_put_flags(msg, NL802154_CAP_ATTR_CCA_MODES,
403*4882a593Smuzhiyun 				       caps->cca_modes) ||
404*4882a593Smuzhiyun 		    nl802154_put_flags(msg, NL802154_CAP_ATTR_CCA_OPTS,
405*4882a593Smuzhiyun 				       caps->cca_opts))
406*4882a593Smuzhiyun 			return -ENOBUFS;
407*4882a593Smuzhiyun 	}
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 	if (nla_put_u8(msg, NL802154_CAP_ATTR_MIN_MINBE, caps->min_minbe) ||
410*4882a593Smuzhiyun 	    nla_put_u8(msg, NL802154_CAP_ATTR_MAX_MINBE, caps->max_minbe) ||
411*4882a593Smuzhiyun 	    nla_put_u8(msg, NL802154_CAP_ATTR_MIN_MAXBE, caps->min_maxbe) ||
412*4882a593Smuzhiyun 	    nla_put_u8(msg, NL802154_CAP_ATTR_MAX_MAXBE, caps->max_maxbe) ||
413*4882a593Smuzhiyun 	    nla_put_u8(msg, NL802154_CAP_ATTR_MIN_CSMA_BACKOFFS,
414*4882a593Smuzhiyun 		       caps->min_csma_backoffs) ||
415*4882a593Smuzhiyun 	    nla_put_u8(msg, NL802154_CAP_ATTR_MAX_CSMA_BACKOFFS,
416*4882a593Smuzhiyun 		       caps->max_csma_backoffs) ||
417*4882a593Smuzhiyun 	    nla_put_s8(msg, NL802154_CAP_ATTR_MIN_FRAME_RETRIES,
418*4882a593Smuzhiyun 		       caps->min_frame_retries) ||
419*4882a593Smuzhiyun 	    nla_put_s8(msg, NL802154_CAP_ATTR_MAX_FRAME_RETRIES,
420*4882a593Smuzhiyun 		       caps->max_frame_retries) ||
421*4882a593Smuzhiyun 	    nl802154_put_flags(msg, NL802154_CAP_ATTR_IFTYPES,
422*4882a593Smuzhiyun 			       caps->iftypes) ||
423*4882a593Smuzhiyun 	    nla_put_u32(msg, NL802154_CAP_ATTR_LBT, caps->lbt))
424*4882a593Smuzhiyun 		return -ENOBUFS;
425*4882a593Smuzhiyun 
426*4882a593Smuzhiyun 	nla_nest_end(msg, nl_caps);
427*4882a593Smuzhiyun 
428*4882a593Smuzhiyun 	return 0;
429*4882a593Smuzhiyun }
430*4882a593Smuzhiyun 
nl802154_send_wpan_phy(struct cfg802154_registered_device * rdev,enum nl802154_commands cmd,struct sk_buff * msg,u32 portid,u32 seq,int flags)431*4882a593Smuzhiyun static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev,
432*4882a593Smuzhiyun 				  enum nl802154_commands cmd,
433*4882a593Smuzhiyun 				  struct sk_buff *msg, u32 portid, u32 seq,
434*4882a593Smuzhiyun 				  int flags)
435*4882a593Smuzhiyun {
436*4882a593Smuzhiyun 	struct nlattr *nl_cmds;
437*4882a593Smuzhiyun 	void *hdr;
438*4882a593Smuzhiyun 	int i;
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
441*4882a593Smuzhiyun 	if (!hdr)
442*4882a593Smuzhiyun 		return -ENOBUFS;
443*4882a593Smuzhiyun 
444*4882a593Smuzhiyun 	if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
445*4882a593Smuzhiyun 	    nla_put_string(msg, NL802154_ATTR_WPAN_PHY_NAME,
446*4882a593Smuzhiyun 			   wpan_phy_name(&rdev->wpan_phy)) ||
447*4882a593Smuzhiyun 	    nla_put_u32(msg, NL802154_ATTR_GENERATION,
448*4882a593Smuzhiyun 			cfg802154_rdev_list_generation))
449*4882a593Smuzhiyun 		goto nla_put_failure;
450*4882a593Smuzhiyun 
451*4882a593Smuzhiyun 	if (cmd != NL802154_CMD_NEW_WPAN_PHY)
452*4882a593Smuzhiyun 		goto finish;
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun 	/* DUMP PHY PIB */
455*4882a593Smuzhiyun 
456*4882a593Smuzhiyun 	/* current channel settings */
457*4882a593Smuzhiyun 	if (nla_put_u8(msg, NL802154_ATTR_PAGE,
458*4882a593Smuzhiyun 		       rdev->wpan_phy.current_page) ||
459*4882a593Smuzhiyun 	    nla_put_u8(msg, NL802154_ATTR_CHANNEL,
460*4882a593Smuzhiyun 		       rdev->wpan_phy.current_channel))
461*4882a593Smuzhiyun 		goto nla_put_failure;
462*4882a593Smuzhiyun 
463*4882a593Smuzhiyun 	/* TODO remove this behaviour, we still keep support it for a while
464*4882a593Smuzhiyun 	 * so users can change the behaviour to the new one.
465*4882a593Smuzhiyun 	 */
466*4882a593Smuzhiyun 	if (nl802154_send_wpan_phy_channels(rdev, msg))
467*4882a593Smuzhiyun 		goto nla_put_failure;
468*4882a593Smuzhiyun 
469*4882a593Smuzhiyun 	/* cca mode */
470*4882a593Smuzhiyun 	if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE) {
471*4882a593Smuzhiyun 		if (nla_put_u32(msg, NL802154_ATTR_CCA_MODE,
472*4882a593Smuzhiyun 				rdev->wpan_phy.cca.mode))
473*4882a593Smuzhiyun 			goto nla_put_failure;
474*4882a593Smuzhiyun 
475*4882a593Smuzhiyun 		if (rdev->wpan_phy.cca.mode == NL802154_CCA_ENERGY_CARRIER) {
476*4882a593Smuzhiyun 			if (nla_put_u32(msg, NL802154_ATTR_CCA_OPT,
477*4882a593Smuzhiyun 					rdev->wpan_phy.cca.opt))
478*4882a593Smuzhiyun 				goto nla_put_failure;
479*4882a593Smuzhiyun 		}
480*4882a593Smuzhiyun 	}
481*4882a593Smuzhiyun 
482*4882a593Smuzhiyun 	if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_TXPOWER) {
483*4882a593Smuzhiyun 		if (nla_put_s32(msg, NL802154_ATTR_TX_POWER,
484*4882a593Smuzhiyun 				rdev->wpan_phy.transmit_power))
485*4882a593Smuzhiyun 			goto nla_put_failure;
486*4882a593Smuzhiyun 	}
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun 	if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_ED_LEVEL) {
489*4882a593Smuzhiyun 		if (nla_put_s32(msg, NL802154_ATTR_CCA_ED_LEVEL,
490*4882a593Smuzhiyun 				rdev->wpan_phy.cca_ed_level))
491*4882a593Smuzhiyun 			goto nla_put_failure;
492*4882a593Smuzhiyun 	}
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun 	if (nl802154_put_capabilities(msg, rdev))
495*4882a593Smuzhiyun 		goto nla_put_failure;
496*4882a593Smuzhiyun 
497*4882a593Smuzhiyun 	nl_cmds = nla_nest_start_noflag(msg, NL802154_ATTR_SUPPORTED_COMMANDS);
498*4882a593Smuzhiyun 	if (!nl_cmds)
499*4882a593Smuzhiyun 		goto nla_put_failure;
500*4882a593Smuzhiyun 
501*4882a593Smuzhiyun 	i = 0;
502*4882a593Smuzhiyun #define CMD(op, n)							\
503*4882a593Smuzhiyun 	do {								\
504*4882a593Smuzhiyun 		if (rdev->ops->op) {					\
505*4882a593Smuzhiyun 			i++;						\
506*4882a593Smuzhiyun 			if (nla_put_u32(msg, i, NL802154_CMD_ ## n))	\
507*4882a593Smuzhiyun 				goto nla_put_failure;			\
508*4882a593Smuzhiyun 		}							\
509*4882a593Smuzhiyun 	} while (0)
510*4882a593Smuzhiyun 
511*4882a593Smuzhiyun 	CMD(add_virtual_intf, NEW_INTERFACE);
512*4882a593Smuzhiyun 	CMD(del_virtual_intf, DEL_INTERFACE);
513*4882a593Smuzhiyun 	CMD(set_channel, SET_CHANNEL);
514*4882a593Smuzhiyun 	CMD(set_pan_id, SET_PAN_ID);
515*4882a593Smuzhiyun 	CMD(set_short_addr, SET_SHORT_ADDR);
516*4882a593Smuzhiyun 	CMD(set_backoff_exponent, SET_BACKOFF_EXPONENT);
517*4882a593Smuzhiyun 	CMD(set_max_csma_backoffs, SET_MAX_CSMA_BACKOFFS);
518*4882a593Smuzhiyun 	CMD(set_max_frame_retries, SET_MAX_FRAME_RETRIES);
519*4882a593Smuzhiyun 	CMD(set_lbt_mode, SET_LBT_MODE);
520*4882a593Smuzhiyun 	CMD(set_ackreq_default, SET_ACKREQ_DEFAULT);
521*4882a593Smuzhiyun 
522*4882a593Smuzhiyun 	if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_TXPOWER)
523*4882a593Smuzhiyun 		CMD(set_tx_power, SET_TX_POWER);
524*4882a593Smuzhiyun 
525*4882a593Smuzhiyun 	if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_ED_LEVEL)
526*4882a593Smuzhiyun 		CMD(set_cca_ed_level, SET_CCA_ED_LEVEL);
527*4882a593Smuzhiyun 
528*4882a593Smuzhiyun 	if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE)
529*4882a593Smuzhiyun 		CMD(set_cca_mode, SET_CCA_MODE);
530*4882a593Smuzhiyun 
531*4882a593Smuzhiyun #undef CMD
532*4882a593Smuzhiyun 	nla_nest_end(msg, nl_cmds);
533*4882a593Smuzhiyun 
534*4882a593Smuzhiyun finish:
535*4882a593Smuzhiyun 	genlmsg_end(msg, hdr);
536*4882a593Smuzhiyun 	return 0;
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun nla_put_failure:
539*4882a593Smuzhiyun 	genlmsg_cancel(msg, hdr);
540*4882a593Smuzhiyun 	return -EMSGSIZE;
541*4882a593Smuzhiyun }
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun struct nl802154_dump_wpan_phy_state {
544*4882a593Smuzhiyun 	s64 filter_wpan_phy;
545*4882a593Smuzhiyun 	long start;
546*4882a593Smuzhiyun 
547*4882a593Smuzhiyun };
548*4882a593Smuzhiyun 
nl802154_dump_wpan_phy_parse(struct sk_buff * skb,struct netlink_callback * cb,struct nl802154_dump_wpan_phy_state * state)549*4882a593Smuzhiyun static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb,
550*4882a593Smuzhiyun 					struct netlink_callback *cb,
551*4882a593Smuzhiyun 					struct nl802154_dump_wpan_phy_state *state)
552*4882a593Smuzhiyun {
553*4882a593Smuzhiyun 	const struct genl_dumpit_info *info = genl_dumpit_info(cb);
554*4882a593Smuzhiyun 	struct nlattr **tb = info->attrs;
555*4882a593Smuzhiyun 
556*4882a593Smuzhiyun 	if (tb[NL802154_ATTR_WPAN_PHY])
557*4882a593Smuzhiyun 		state->filter_wpan_phy = nla_get_u32(tb[NL802154_ATTR_WPAN_PHY]);
558*4882a593Smuzhiyun 	if (tb[NL802154_ATTR_WPAN_DEV])
559*4882a593Smuzhiyun 		state->filter_wpan_phy = nla_get_u64(tb[NL802154_ATTR_WPAN_DEV]) >> 32;
560*4882a593Smuzhiyun 	if (tb[NL802154_ATTR_IFINDEX]) {
561*4882a593Smuzhiyun 		struct net_device *netdev;
562*4882a593Smuzhiyun 		struct cfg802154_registered_device *rdev;
563*4882a593Smuzhiyun 		int ifidx = nla_get_u32(tb[NL802154_ATTR_IFINDEX]);
564*4882a593Smuzhiyun 
565*4882a593Smuzhiyun 		netdev = __dev_get_by_index(&init_net, ifidx);
566*4882a593Smuzhiyun 		if (!netdev)
567*4882a593Smuzhiyun 			return -ENODEV;
568*4882a593Smuzhiyun 		if (netdev->ieee802154_ptr) {
569*4882a593Smuzhiyun 			rdev = wpan_phy_to_rdev(
570*4882a593Smuzhiyun 					netdev->ieee802154_ptr->wpan_phy);
571*4882a593Smuzhiyun 			state->filter_wpan_phy = rdev->wpan_phy_idx;
572*4882a593Smuzhiyun 		}
573*4882a593Smuzhiyun 	}
574*4882a593Smuzhiyun 
575*4882a593Smuzhiyun 	return 0;
576*4882a593Smuzhiyun }
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun static int
nl802154_dump_wpan_phy(struct sk_buff * skb,struct netlink_callback * cb)579*4882a593Smuzhiyun nl802154_dump_wpan_phy(struct sk_buff *skb, struct netlink_callback *cb)
580*4882a593Smuzhiyun {
581*4882a593Smuzhiyun 	int idx = 0, ret;
582*4882a593Smuzhiyun 	struct nl802154_dump_wpan_phy_state *state = (void *)cb->args[0];
583*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev;
584*4882a593Smuzhiyun 
585*4882a593Smuzhiyun 	rtnl_lock();
586*4882a593Smuzhiyun 	if (!state) {
587*4882a593Smuzhiyun 		state = kzalloc(sizeof(*state), GFP_KERNEL);
588*4882a593Smuzhiyun 		if (!state) {
589*4882a593Smuzhiyun 			rtnl_unlock();
590*4882a593Smuzhiyun 			return -ENOMEM;
591*4882a593Smuzhiyun 		}
592*4882a593Smuzhiyun 		state->filter_wpan_phy = -1;
593*4882a593Smuzhiyun 		ret = nl802154_dump_wpan_phy_parse(skb, cb, state);
594*4882a593Smuzhiyun 		if (ret) {
595*4882a593Smuzhiyun 			kfree(state);
596*4882a593Smuzhiyun 			rtnl_unlock();
597*4882a593Smuzhiyun 			return ret;
598*4882a593Smuzhiyun 		}
599*4882a593Smuzhiyun 		cb->args[0] = (long)state;
600*4882a593Smuzhiyun 	}
601*4882a593Smuzhiyun 
602*4882a593Smuzhiyun 	list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
603*4882a593Smuzhiyun 		if (!net_eq(wpan_phy_net(&rdev->wpan_phy), sock_net(skb->sk)))
604*4882a593Smuzhiyun 			continue;
605*4882a593Smuzhiyun 		if (++idx <= state->start)
606*4882a593Smuzhiyun 			continue;
607*4882a593Smuzhiyun 		if (state->filter_wpan_phy != -1 &&
608*4882a593Smuzhiyun 		    state->filter_wpan_phy != rdev->wpan_phy_idx)
609*4882a593Smuzhiyun 			continue;
610*4882a593Smuzhiyun 		/* attempt to fit multiple wpan_phy data chunks into the skb */
611*4882a593Smuzhiyun 		ret = nl802154_send_wpan_phy(rdev,
612*4882a593Smuzhiyun 					     NL802154_CMD_NEW_WPAN_PHY,
613*4882a593Smuzhiyun 					     skb,
614*4882a593Smuzhiyun 					     NETLINK_CB(cb->skb).portid,
615*4882a593Smuzhiyun 					     cb->nlh->nlmsg_seq, NLM_F_MULTI);
616*4882a593Smuzhiyun 		if (ret < 0) {
617*4882a593Smuzhiyun 			if ((ret == -ENOBUFS || ret == -EMSGSIZE) &&
618*4882a593Smuzhiyun 			    !skb->len && cb->min_dump_alloc < 4096) {
619*4882a593Smuzhiyun 				cb->min_dump_alloc = 4096;
620*4882a593Smuzhiyun 				rtnl_unlock();
621*4882a593Smuzhiyun 				return 1;
622*4882a593Smuzhiyun 			}
623*4882a593Smuzhiyun 			idx--;
624*4882a593Smuzhiyun 			break;
625*4882a593Smuzhiyun 		}
626*4882a593Smuzhiyun 		break;
627*4882a593Smuzhiyun 	}
628*4882a593Smuzhiyun 	rtnl_unlock();
629*4882a593Smuzhiyun 
630*4882a593Smuzhiyun 	state->start = idx;
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun 	return skb->len;
633*4882a593Smuzhiyun }
634*4882a593Smuzhiyun 
nl802154_dump_wpan_phy_done(struct netlink_callback * cb)635*4882a593Smuzhiyun static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb)
636*4882a593Smuzhiyun {
637*4882a593Smuzhiyun 	kfree((void *)cb->args[0]);
638*4882a593Smuzhiyun 	return 0;
639*4882a593Smuzhiyun }
640*4882a593Smuzhiyun 
nl802154_get_wpan_phy(struct sk_buff * skb,struct genl_info * info)641*4882a593Smuzhiyun static int nl802154_get_wpan_phy(struct sk_buff *skb, struct genl_info *info)
642*4882a593Smuzhiyun {
643*4882a593Smuzhiyun 	struct sk_buff *msg;
644*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
645*4882a593Smuzhiyun 
646*4882a593Smuzhiyun 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
647*4882a593Smuzhiyun 	if (!msg)
648*4882a593Smuzhiyun 		return -ENOMEM;
649*4882a593Smuzhiyun 
650*4882a593Smuzhiyun 	if (nl802154_send_wpan_phy(rdev, NL802154_CMD_NEW_WPAN_PHY, msg,
651*4882a593Smuzhiyun 				   info->snd_portid, info->snd_seq, 0) < 0) {
652*4882a593Smuzhiyun 		nlmsg_free(msg);
653*4882a593Smuzhiyun 		return -ENOBUFS;
654*4882a593Smuzhiyun 	}
655*4882a593Smuzhiyun 
656*4882a593Smuzhiyun 	return genlmsg_reply(msg, info);
657*4882a593Smuzhiyun }
658*4882a593Smuzhiyun 
wpan_dev_id(struct wpan_dev * wpan_dev)659*4882a593Smuzhiyun static inline u64 wpan_dev_id(struct wpan_dev *wpan_dev)
660*4882a593Smuzhiyun {
661*4882a593Smuzhiyun 	return (u64)wpan_dev->identifier |
662*4882a593Smuzhiyun 	       ((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32);
663*4882a593Smuzhiyun }
664*4882a593Smuzhiyun 
665*4882a593Smuzhiyun #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
666*4882a593Smuzhiyun #include <net/ieee802154_netdev.h>
667*4882a593Smuzhiyun 
668*4882a593Smuzhiyun static int
ieee802154_llsec_send_key_id(struct sk_buff * msg,const struct ieee802154_llsec_key_id * desc)669*4882a593Smuzhiyun ieee802154_llsec_send_key_id(struct sk_buff *msg,
670*4882a593Smuzhiyun 			     const struct ieee802154_llsec_key_id *desc)
671*4882a593Smuzhiyun {
672*4882a593Smuzhiyun 	struct nlattr *nl_dev_addr;
673*4882a593Smuzhiyun 
674*4882a593Smuzhiyun 	if (nla_put_u32(msg, NL802154_KEY_ID_ATTR_MODE, desc->mode))
675*4882a593Smuzhiyun 		return -ENOBUFS;
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun 	switch (desc->mode) {
678*4882a593Smuzhiyun 	case NL802154_KEY_ID_MODE_IMPLICIT:
679*4882a593Smuzhiyun 		nl_dev_addr = nla_nest_start_noflag(msg,
680*4882a593Smuzhiyun 						    NL802154_KEY_ID_ATTR_IMPLICIT);
681*4882a593Smuzhiyun 		if (!nl_dev_addr)
682*4882a593Smuzhiyun 			return -ENOBUFS;
683*4882a593Smuzhiyun 
684*4882a593Smuzhiyun 		if (nla_put_le16(msg, NL802154_DEV_ADDR_ATTR_PAN_ID,
685*4882a593Smuzhiyun 				 desc->device_addr.pan_id) ||
686*4882a593Smuzhiyun 		    nla_put_u32(msg,  NL802154_DEV_ADDR_ATTR_MODE,
687*4882a593Smuzhiyun 				desc->device_addr.mode))
688*4882a593Smuzhiyun 			return -ENOBUFS;
689*4882a593Smuzhiyun 
690*4882a593Smuzhiyun 		switch (desc->device_addr.mode) {
691*4882a593Smuzhiyun 		case NL802154_DEV_ADDR_SHORT:
692*4882a593Smuzhiyun 			if (nla_put_le16(msg, NL802154_DEV_ADDR_ATTR_SHORT,
693*4882a593Smuzhiyun 					 desc->device_addr.short_addr))
694*4882a593Smuzhiyun 				return -ENOBUFS;
695*4882a593Smuzhiyun 			break;
696*4882a593Smuzhiyun 		case NL802154_DEV_ADDR_EXTENDED:
697*4882a593Smuzhiyun 			if (nla_put_le64(msg, NL802154_DEV_ADDR_ATTR_EXTENDED,
698*4882a593Smuzhiyun 					 desc->device_addr.extended_addr,
699*4882a593Smuzhiyun 					 NL802154_DEV_ADDR_ATTR_PAD))
700*4882a593Smuzhiyun 				return -ENOBUFS;
701*4882a593Smuzhiyun 			break;
702*4882a593Smuzhiyun 		default:
703*4882a593Smuzhiyun 			/* userspace should handle unknown */
704*4882a593Smuzhiyun 			break;
705*4882a593Smuzhiyun 		}
706*4882a593Smuzhiyun 
707*4882a593Smuzhiyun 		nla_nest_end(msg, nl_dev_addr);
708*4882a593Smuzhiyun 		break;
709*4882a593Smuzhiyun 	case NL802154_KEY_ID_MODE_INDEX:
710*4882a593Smuzhiyun 		break;
711*4882a593Smuzhiyun 	case NL802154_KEY_ID_MODE_INDEX_SHORT:
712*4882a593Smuzhiyun 		/* TODO renmae short_source? */
713*4882a593Smuzhiyun 		if (nla_put_le32(msg, NL802154_KEY_ID_ATTR_SOURCE_SHORT,
714*4882a593Smuzhiyun 				 desc->short_source))
715*4882a593Smuzhiyun 			return -ENOBUFS;
716*4882a593Smuzhiyun 		break;
717*4882a593Smuzhiyun 	case NL802154_KEY_ID_MODE_INDEX_EXTENDED:
718*4882a593Smuzhiyun 		if (nla_put_le64(msg, NL802154_KEY_ID_ATTR_SOURCE_EXTENDED,
719*4882a593Smuzhiyun 				 desc->extended_source,
720*4882a593Smuzhiyun 				 NL802154_KEY_ID_ATTR_PAD))
721*4882a593Smuzhiyun 			return -ENOBUFS;
722*4882a593Smuzhiyun 		break;
723*4882a593Smuzhiyun 	default:
724*4882a593Smuzhiyun 		/* userspace should handle unknown */
725*4882a593Smuzhiyun 		break;
726*4882a593Smuzhiyun 	}
727*4882a593Smuzhiyun 
728*4882a593Smuzhiyun 	/* TODO key_id to key_idx ? Check naming */
729*4882a593Smuzhiyun 	if (desc->mode != NL802154_KEY_ID_MODE_IMPLICIT) {
730*4882a593Smuzhiyun 		if (nla_put_u8(msg, NL802154_KEY_ID_ATTR_INDEX, desc->id))
731*4882a593Smuzhiyun 			return -ENOBUFS;
732*4882a593Smuzhiyun 	}
733*4882a593Smuzhiyun 
734*4882a593Smuzhiyun 	return 0;
735*4882a593Smuzhiyun }
736*4882a593Smuzhiyun 
nl802154_get_llsec_params(struct sk_buff * msg,struct cfg802154_registered_device * rdev,struct wpan_dev * wpan_dev)737*4882a593Smuzhiyun static int nl802154_get_llsec_params(struct sk_buff *msg,
738*4882a593Smuzhiyun 				     struct cfg802154_registered_device *rdev,
739*4882a593Smuzhiyun 				     struct wpan_dev *wpan_dev)
740*4882a593Smuzhiyun {
741*4882a593Smuzhiyun 	struct nlattr *nl_key_id;
742*4882a593Smuzhiyun 	struct ieee802154_llsec_params params;
743*4882a593Smuzhiyun 	int ret;
744*4882a593Smuzhiyun 
745*4882a593Smuzhiyun 	ret = rdev_get_llsec_params(rdev, wpan_dev, &params);
746*4882a593Smuzhiyun 	if (ret < 0)
747*4882a593Smuzhiyun 		return ret;
748*4882a593Smuzhiyun 
749*4882a593Smuzhiyun 	if (nla_put_u8(msg, NL802154_ATTR_SEC_ENABLED, params.enabled) ||
750*4882a593Smuzhiyun 	    nla_put_u32(msg, NL802154_ATTR_SEC_OUT_LEVEL, params.out_level) ||
751*4882a593Smuzhiyun 	    nla_put_be32(msg, NL802154_ATTR_SEC_FRAME_COUNTER,
752*4882a593Smuzhiyun 			 params.frame_counter))
753*4882a593Smuzhiyun 		return -ENOBUFS;
754*4882a593Smuzhiyun 
755*4882a593Smuzhiyun 	nl_key_id = nla_nest_start_noflag(msg, NL802154_ATTR_SEC_OUT_KEY_ID);
756*4882a593Smuzhiyun 	if (!nl_key_id)
757*4882a593Smuzhiyun 		return -ENOBUFS;
758*4882a593Smuzhiyun 
759*4882a593Smuzhiyun 	ret = ieee802154_llsec_send_key_id(msg, &params.out_key);
760*4882a593Smuzhiyun 	if (ret < 0)
761*4882a593Smuzhiyun 		return ret;
762*4882a593Smuzhiyun 
763*4882a593Smuzhiyun 	nla_nest_end(msg, nl_key_id);
764*4882a593Smuzhiyun 
765*4882a593Smuzhiyun 	return 0;
766*4882a593Smuzhiyun }
767*4882a593Smuzhiyun #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
768*4882a593Smuzhiyun 
769*4882a593Smuzhiyun static int
nl802154_send_iface(struct sk_buff * msg,u32 portid,u32 seq,int flags,struct cfg802154_registered_device * rdev,struct wpan_dev * wpan_dev)770*4882a593Smuzhiyun nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
771*4882a593Smuzhiyun 		    struct cfg802154_registered_device *rdev,
772*4882a593Smuzhiyun 		    struct wpan_dev *wpan_dev)
773*4882a593Smuzhiyun {
774*4882a593Smuzhiyun 	struct net_device *dev = wpan_dev->netdev;
775*4882a593Smuzhiyun 	void *hdr;
776*4882a593Smuzhiyun 
777*4882a593Smuzhiyun 	hdr = nl802154hdr_put(msg, portid, seq, flags,
778*4882a593Smuzhiyun 			      NL802154_CMD_NEW_INTERFACE);
779*4882a593Smuzhiyun 	if (!hdr)
780*4882a593Smuzhiyun 		return -1;
781*4882a593Smuzhiyun 
782*4882a593Smuzhiyun 	if (dev &&
783*4882a593Smuzhiyun 	    (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex) ||
784*4882a593Smuzhiyun 	     nla_put_string(msg, NL802154_ATTR_IFNAME, dev->name)))
785*4882a593Smuzhiyun 		goto nla_put_failure;
786*4882a593Smuzhiyun 
787*4882a593Smuzhiyun 	if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) ||
788*4882a593Smuzhiyun 	    nla_put_u32(msg, NL802154_ATTR_IFTYPE, wpan_dev->iftype) ||
789*4882a593Smuzhiyun 	    nla_put_u64_64bit(msg, NL802154_ATTR_WPAN_DEV,
790*4882a593Smuzhiyun 			      wpan_dev_id(wpan_dev), NL802154_ATTR_PAD) ||
791*4882a593Smuzhiyun 	    nla_put_u32(msg, NL802154_ATTR_GENERATION,
792*4882a593Smuzhiyun 			rdev->devlist_generation ^
793*4882a593Smuzhiyun 			(cfg802154_rdev_list_generation << 2)))
794*4882a593Smuzhiyun 		goto nla_put_failure;
795*4882a593Smuzhiyun 
796*4882a593Smuzhiyun 	/* address settings */
797*4882a593Smuzhiyun 	if (nla_put_le64(msg, NL802154_ATTR_EXTENDED_ADDR,
798*4882a593Smuzhiyun 			 wpan_dev->extended_addr,
799*4882a593Smuzhiyun 			 NL802154_ATTR_PAD) ||
800*4882a593Smuzhiyun 	    nla_put_le16(msg, NL802154_ATTR_SHORT_ADDR,
801*4882a593Smuzhiyun 			 wpan_dev->short_addr) ||
802*4882a593Smuzhiyun 	    nla_put_le16(msg, NL802154_ATTR_PAN_ID, wpan_dev->pan_id))
803*4882a593Smuzhiyun 		goto nla_put_failure;
804*4882a593Smuzhiyun 
805*4882a593Smuzhiyun 	/* ARET handling */
806*4882a593Smuzhiyun 	if (nla_put_s8(msg, NL802154_ATTR_MAX_FRAME_RETRIES,
807*4882a593Smuzhiyun 		       wpan_dev->frame_retries) ||
808*4882a593Smuzhiyun 	    nla_put_u8(msg, NL802154_ATTR_MAX_BE, wpan_dev->max_be) ||
809*4882a593Smuzhiyun 	    nla_put_u8(msg, NL802154_ATTR_MAX_CSMA_BACKOFFS,
810*4882a593Smuzhiyun 		       wpan_dev->csma_retries) ||
811*4882a593Smuzhiyun 	    nla_put_u8(msg, NL802154_ATTR_MIN_BE, wpan_dev->min_be))
812*4882a593Smuzhiyun 		goto nla_put_failure;
813*4882a593Smuzhiyun 
814*4882a593Smuzhiyun 	/* listen before transmit */
815*4882a593Smuzhiyun 	if (nla_put_u8(msg, NL802154_ATTR_LBT_MODE, wpan_dev->lbt))
816*4882a593Smuzhiyun 		goto nla_put_failure;
817*4882a593Smuzhiyun 
818*4882a593Smuzhiyun 	/* ackreq default behaviour */
819*4882a593Smuzhiyun 	if (nla_put_u8(msg, NL802154_ATTR_ACKREQ_DEFAULT, wpan_dev->ackreq))
820*4882a593Smuzhiyun 		goto nla_put_failure;
821*4882a593Smuzhiyun 
822*4882a593Smuzhiyun #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
823*4882a593Smuzhiyun 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
824*4882a593Smuzhiyun 		goto out;
825*4882a593Smuzhiyun 
826*4882a593Smuzhiyun 	if (nl802154_get_llsec_params(msg, rdev, wpan_dev) < 0)
827*4882a593Smuzhiyun 		goto nla_put_failure;
828*4882a593Smuzhiyun 
829*4882a593Smuzhiyun out:
830*4882a593Smuzhiyun #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
831*4882a593Smuzhiyun 
832*4882a593Smuzhiyun 	genlmsg_end(msg, hdr);
833*4882a593Smuzhiyun 	return 0;
834*4882a593Smuzhiyun 
835*4882a593Smuzhiyun nla_put_failure:
836*4882a593Smuzhiyun 	genlmsg_cancel(msg, hdr);
837*4882a593Smuzhiyun 	return -EMSGSIZE;
838*4882a593Smuzhiyun }
839*4882a593Smuzhiyun 
840*4882a593Smuzhiyun static int
nl802154_dump_interface(struct sk_buff * skb,struct netlink_callback * cb)841*4882a593Smuzhiyun nl802154_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
842*4882a593Smuzhiyun {
843*4882a593Smuzhiyun 	int wp_idx = 0;
844*4882a593Smuzhiyun 	int if_idx = 0;
845*4882a593Smuzhiyun 	int wp_start = cb->args[0];
846*4882a593Smuzhiyun 	int if_start = cb->args[1];
847*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev;
848*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev;
849*4882a593Smuzhiyun 
850*4882a593Smuzhiyun 	rtnl_lock();
851*4882a593Smuzhiyun 	list_for_each_entry(rdev, &cfg802154_rdev_list, list) {
852*4882a593Smuzhiyun 		if (!net_eq(wpan_phy_net(&rdev->wpan_phy), sock_net(skb->sk)))
853*4882a593Smuzhiyun 			continue;
854*4882a593Smuzhiyun 		if (wp_idx < wp_start) {
855*4882a593Smuzhiyun 			wp_idx++;
856*4882a593Smuzhiyun 			continue;
857*4882a593Smuzhiyun 		}
858*4882a593Smuzhiyun 		if_idx = 0;
859*4882a593Smuzhiyun 
860*4882a593Smuzhiyun 		list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) {
861*4882a593Smuzhiyun 			if (if_idx < if_start) {
862*4882a593Smuzhiyun 				if_idx++;
863*4882a593Smuzhiyun 				continue;
864*4882a593Smuzhiyun 			}
865*4882a593Smuzhiyun 			if (nl802154_send_iface(skb, NETLINK_CB(cb->skb).portid,
866*4882a593Smuzhiyun 						cb->nlh->nlmsg_seq, NLM_F_MULTI,
867*4882a593Smuzhiyun 						rdev, wpan_dev) < 0) {
868*4882a593Smuzhiyun 				goto out;
869*4882a593Smuzhiyun 			}
870*4882a593Smuzhiyun 			if_idx++;
871*4882a593Smuzhiyun 		}
872*4882a593Smuzhiyun 
873*4882a593Smuzhiyun 		wp_idx++;
874*4882a593Smuzhiyun 	}
875*4882a593Smuzhiyun out:
876*4882a593Smuzhiyun 	rtnl_unlock();
877*4882a593Smuzhiyun 
878*4882a593Smuzhiyun 	cb->args[0] = wp_idx;
879*4882a593Smuzhiyun 	cb->args[1] = if_idx;
880*4882a593Smuzhiyun 
881*4882a593Smuzhiyun 	return skb->len;
882*4882a593Smuzhiyun }
883*4882a593Smuzhiyun 
nl802154_get_interface(struct sk_buff * skb,struct genl_info * info)884*4882a593Smuzhiyun static int nl802154_get_interface(struct sk_buff *skb, struct genl_info *info)
885*4882a593Smuzhiyun {
886*4882a593Smuzhiyun 	struct sk_buff *msg;
887*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
888*4882a593Smuzhiyun 	struct wpan_dev *wdev = info->user_ptr[1];
889*4882a593Smuzhiyun 
890*4882a593Smuzhiyun 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
891*4882a593Smuzhiyun 	if (!msg)
892*4882a593Smuzhiyun 		return -ENOMEM;
893*4882a593Smuzhiyun 
894*4882a593Smuzhiyun 	if (nl802154_send_iface(msg, info->snd_portid, info->snd_seq, 0,
895*4882a593Smuzhiyun 				rdev, wdev) < 0) {
896*4882a593Smuzhiyun 		nlmsg_free(msg);
897*4882a593Smuzhiyun 		return -ENOBUFS;
898*4882a593Smuzhiyun 	}
899*4882a593Smuzhiyun 
900*4882a593Smuzhiyun 	return genlmsg_reply(msg, info);
901*4882a593Smuzhiyun }
902*4882a593Smuzhiyun 
nl802154_new_interface(struct sk_buff * skb,struct genl_info * info)903*4882a593Smuzhiyun static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info)
904*4882a593Smuzhiyun {
905*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
906*4882a593Smuzhiyun 	enum nl802154_iftype type = NL802154_IFTYPE_UNSPEC;
907*4882a593Smuzhiyun 	__le64 extended_addr = cpu_to_le64(0x0000000000000000ULL);
908*4882a593Smuzhiyun 
909*4882a593Smuzhiyun 	/* TODO avoid failing a new interface
910*4882a593Smuzhiyun 	 * creation due to pending removal?
911*4882a593Smuzhiyun 	 */
912*4882a593Smuzhiyun 
913*4882a593Smuzhiyun 	if (!info->attrs[NL802154_ATTR_IFNAME])
914*4882a593Smuzhiyun 		return -EINVAL;
915*4882a593Smuzhiyun 
916*4882a593Smuzhiyun 	if (info->attrs[NL802154_ATTR_IFTYPE]) {
917*4882a593Smuzhiyun 		type = nla_get_u32(info->attrs[NL802154_ATTR_IFTYPE]);
918*4882a593Smuzhiyun 		if (type > NL802154_IFTYPE_MAX ||
919*4882a593Smuzhiyun 		    !(rdev->wpan_phy.supported.iftypes & BIT(type)))
920*4882a593Smuzhiyun 			return -EINVAL;
921*4882a593Smuzhiyun 	}
922*4882a593Smuzhiyun 
923*4882a593Smuzhiyun 	if (info->attrs[NL802154_ATTR_EXTENDED_ADDR])
924*4882a593Smuzhiyun 		extended_addr = nla_get_le64(info->attrs[NL802154_ATTR_EXTENDED_ADDR]);
925*4882a593Smuzhiyun 
926*4882a593Smuzhiyun 	if (!rdev->ops->add_virtual_intf)
927*4882a593Smuzhiyun 		return -EOPNOTSUPP;
928*4882a593Smuzhiyun 
929*4882a593Smuzhiyun 	return rdev_add_virtual_intf(rdev,
930*4882a593Smuzhiyun 				     nla_data(info->attrs[NL802154_ATTR_IFNAME]),
931*4882a593Smuzhiyun 				     NET_NAME_USER, type, extended_addr);
932*4882a593Smuzhiyun }
933*4882a593Smuzhiyun 
nl802154_del_interface(struct sk_buff * skb,struct genl_info * info)934*4882a593Smuzhiyun static int nl802154_del_interface(struct sk_buff *skb, struct genl_info *info)
935*4882a593Smuzhiyun {
936*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
937*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev = info->user_ptr[1];
938*4882a593Smuzhiyun 
939*4882a593Smuzhiyun 	if (!rdev->ops->del_virtual_intf)
940*4882a593Smuzhiyun 		return -EOPNOTSUPP;
941*4882a593Smuzhiyun 
942*4882a593Smuzhiyun 	/* If we remove a wpan device without a netdev then clear
943*4882a593Smuzhiyun 	 * user_ptr[1] so that nl802154_post_doit won't dereference it
944*4882a593Smuzhiyun 	 * to check if it needs to do dev_put(). Otherwise it crashes
945*4882a593Smuzhiyun 	 * since the wpan_dev has been freed, unlike with a netdev where
946*4882a593Smuzhiyun 	 * we need the dev_put() for the netdev to really be freed.
947*4882a593Smuzhiyun 	 */
948*4882a593Smuzhiyun 	if (!wpan_dev->netdev)
949*4882a593Smuzhiyun 		info->user_ptr[1] = NULL;
950*4882a593Smuzhiyun 
951*4882a593Smuzhiyun 	return rdev_del_virtual_intf(rdev, wpan_dev);
952*4882a593Smuzhiyun }
953*4882a593Smuzhiyun 
nl802154_set_channel(struct sk_buff * skb,struct genl_info * info)954*4882a593Smuzhiyun static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info)
955*4882a593Smuzhiyun {
956*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
957*4882a593Smuzhiyun 	u8 channel, page;
958*4882a593Smuzhiyun 
959*4882a593Smuzhiyun 	if (!info->attrs[NL802154_ATTR_PAGE] ||
960*4882a593Smuzhiyun 	    !info->attrs[NL802154_ATTR_CHANNEL])
961*4882a593Smuzhiyun 		return -EINVAL;
962*4882a593Smuzhiyun 
963*4882a593Smuzhiyun 	page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]);
964*4882a593Smuzhiyun 	channel = nla_get_u8(info->attrs[NL802154_ATTR_CHANNEL]);
965*4882a593Smuzhiyun 
966*4882a593Smuzhiyun 	/* check 802.15.4 constraints */
967*4882a593Smuzhiyun 	if (page > IEEE802154_MAX_PAGE || channel > IEEE802154_MAX_CHANNEL ||
968*4882a593Smuzhiyun 	    !(rdev->wpan_phy.supported.channels[page] & BIT(channel)))
969*4882a593Smuzhiyun 		return -EINVAL;
970*4882a593Smuzhiyun 
971*4882a593Smuzhiyun 	return rdev_set_channel(rdev, page, channel);
972*4882a593Smuzhiyun }
973*4882a593Smuzhiyun 
nl802154_set_cca_mode(struct sk_buff * skb,struct genl_info * info)974*4882a593Smuzhiyun static int nl802154_set_cca_mode(struct sk_buff *skb, struct genl_info *info)
975*4882a593Smuzhiyun {
976*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
977*4882a593Smuzhiyun 	struct wpan_phy_cca cca;
978*4882a593Smuzhiyun 
979*4882a593Smuzhiyun 	if (!(rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE))
980*4882a593Smuzhiyun 		return -EOPNOTSUPP;
981*4882a593Smuzhiyun 
982*4882a593Smuzhiyun 	if (!info->attrs[NL802154_ATTR_CCA_MODE])
983*4882a593Smuzhiyun 		return -EINVAL;
984*4882a593Smuzhiyun 
985*4882a593Smuzhiyun 	cca.mode = nla_get_u32(info->attrs[NL802154_ATTR_CCA_MODE]);
986*4882a593Smuzhiyun 	/* checking 802.15.4 constraints */
987*4882a593Smuzhiyun 	if (cca.mode < NL802154_CCA_ENERGY ||
988*4882a593Smuzhiyun 	    cca.mode > NL802154_CCA_ATTR_MAX ||
989*4882a593Smuzhiyun 	    !(rdev->wpan_phy.supported.cca_modes & BIT(cca.mode)))
990*4882a593Smuzhiyun 		return -EINVAL;
991*4882a593Smuzhiyun 
992*4882a593Smuzhiyun 	if (cca.mode == NL802154_CCA_ENERGY_CARRIER) {
993*4882a593Smuzhiyun 		if (!info->attrs[NL802154_ATTR_CCA_OPT])
994*4882a593Smuzhiyun 			return -EINVAL;
995*4882a593Smuzhiyun 
996*4882a593Smuzhiyun 		cca.opt = nla_get_u32(info->attrs[NL802154_ATTR_CCA_OPT]);
997*4882a593Smuzhiyun 		if (cca.opt > NL802154_CCA_OPT_ATTR_MAX ||
998*4882a593Smuzhiyun 		    !(rdev->wpan_phy.supported.cca_opts & BIT(cca.opt)))
999*4882a593Smuzhiyun 			return -EINVAL;
1000*4882a593Smuzhiyun 	}
1001*4882a593Smuzhiyun 
1002*4882a593Smuzhiyun 	return rdev_set_cca_mode(rdev, &cca);
1003*4882a593Smuzhiyun }
1004*4882a593Smuzhiyun 
nl802154_set_cca_ed_level(struct sk_buff * skb,struct genl_info * info)1005*4882a593Smuzhiyun static int nl802154_set_cca_ed_level(struct sk_buff *skb, struct genl_info *info)
1006*4882a593Smuzhiyun {
1007*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1008*4882a593Smuzhiyun 	s32 ed_level;
1009*4882a593Smuzhiyun 	int i;
1010*4882a593Smuzhiyun 
1011*4882a593Smuzhiyun 	if (!(rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_ED_LEVEL))
1012*4882a593Smuzhiyun 		return -EOPNOTSUPP;
1013*4882a593Smuzhiyun 
1014*4882a593Smuzhiyun 	if (!info->attrs[NL802154_ATTR_CCA_ED_LEVEL])
1015*4882a593Smuzhiyun 		return -EINVAL;
1016*4882a593Smuzhiyun 
1017*4882a593Smuzhiyun 	ed_level = nla_get_s32(info->attrs[NL802154_ATTR_CCA_ED_LEVEL]);
1018*4882a593Smuzhiyun 
1019*4882a593Smuzhiyun 	for (i = 0; i < rdev->wpan_phy.supported.cca_ed_levels_size; i++) {
1020*4882a593Smuzhiyun 		if (ed_level == rdev->wpan_phy.supported.cca_ed_levels[i])
1021*4882a593Smuzhiyun 			return rdev_set_cca_ed_level(rdev, ed_level);
1022*4882a593Smuzhiyun 	}
1023*4882a593Smuzhiyun 
1024*4882a593Smuzhiyun 	return -EINVAL;
1025*4882a593Smuzhiyun }
1026*4882a593Smuzhiyun 
nl802154_set_tx_power(struct sk_buff * skb,struct genl_info * info)1027*4882a593Smuzhiyun static int nl802154_set_tx_power(struct sk_buff *skb, struct genl_info *info)
1028*4882a593Smuzhiyun {
1029*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1030*4882a593Smuzhiyun 	s32 power;
1031*4882a593Smuzhiyun 	int i;
1032*4882a593Smuzhiyun 
1033*4882a593Smuzhiyun 	if (!(rdev->wpan_phy.flags & WPAN_PHY_FLAG_TXPOWER))
1034*4882a593Smuzhiyun 		return -EOPNOTSUPP;
1035*4882a593Smuzhiyun 
1036*4882a593Smuzhiyun 	if (!info->attrs[NL802154_ATTR_TX_POWER])
1037*4882a593Smuzhiyun 		return -EINVAL;
1038*4882a593Smuzhiyun 
1039*4882a593Smuzhiyun 	power = nla_get_s32(info->attrs[NL802154_ATTR_TX_POWER]);
1040*4882a593Smuzhiyun 
1041*4882a593Smuzhiyun 	for (i = 0; i < rdev->wpan_phy.supported.tx_powers_size; i++) {
1042*4882a593Smuzhiyun 		if (power == rdev->wpan_phy.supported.tx_powers[i])
1043*4882a593Smuzhiyun 			return rdev_set_tx_power(rdev, power);
1044*4882a593Smuzhiyun 	}
1045*4882a593Smuzhiyun 
1046*4882a593Smuzhiyun 	return -EINVAL;
1047*4882a593Smuzhiyun }
1048*4882a593Smuzhiyun 
nl802154_set_pan_id(struct sk_buff * skb,struct genl_info * info)1049*4882a593Smuzhiyun static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info)
1050*4882a593Smuzhiyun {
1051*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1052*4882a593Smuzhiyun 	struct net_device *dev = info->user_ptr[1];
1053*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1054*4882a593Smuzhiyun 	__le16 pan_id;
1055*4882a593Smuzhiyun 
1056*4882a593Smuzhiyun 	/* conflict here while tx/rx calls */
1057*4882a593Smuzhiyun 	if (netif_running(dev))
1058*4882a593Smuzhiyun 		return -EBUSY;
1059*4882a593Smuzhiyun 
1060*4882a593Smuzhiyun 	if (wpan_dev->lowpan_dev) {
1061*4882a593Smuzhiyun 		if (netif_running(wpan_dev->lowpan_dev))
1062*4882a593Smuzhiyun 			return -EBUSY;
1063*4882a593Smuzhiyun 	}
1064*4882a593Smuzhiyun 
1065*4882a593Smuzhiyun 	/* don't change address fields on monitor */
1066*4882a593Smuzhiyun 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR ||
1067*4882a593Smuzhiyun 	    !info->attrs[NL802154_ATTR_PAN_ID])
1068*4882a593Smuzhiyun 		return -EINVAL;
1069*4882a593Smuzhiyun 
1070*4882a593Smuzhiyun 	pan_id = nla_get_le16(info->attrs[NL802154_ATTR_PAN_ID]);
1071*4882a593Smuzhiyun 
1072*4882a593Smuzhiyun 	/* TODO
1073*4882a593Smuzhiyun 	 * I am not sure about to check here on broadcast pan_id.
1074*4882a593Smuzhiyun 	 * Broadcast is a valid setting, comment from 802.15.4:
1075*4882a593Smuzhiyun 	 * If this value is 0xffff, the device is not associated.
1076*4882a593Smuzhiyun 	 *
1077*4882a593Smuzhiyun 	 * This could useful to simple deassociate an device.
1078*4882a593Smuzhiyun 	 */
1079*4882a593Smuzhiyun 	if (pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST))
1080*4882a593Smuzhiyun 		return -EINVAL;
1081*4882a593Smuzhiyun 
1082*4882a593Smuzhiyun 	return rdev_set_pan_id(rdev, wpan_dev, pan_id);
1083*4882a593Smuzhiyun }
1084*4882a593Smuzhiyun 
nl802154_set_short_addr(struct sk_buff * skb,struct genl_info * info)1085*4882a593Smuzhiyun static int nl802154_set_short_addr(struct sk_buff *skb, struct genl_info *info)
1086*4882a593Smuzhiyun {
1087*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1088*4882a593Smuzhiyun 	struct net_device *dev = info->user_ptr[1];
1089*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1090*4882a593Smuzhiyun 	__le16 short_addr;
1091*4882a593Smuzhiyun 
1092*4882a593Smuzhiyun 	/* conflict here while tx/rx calls */
1093*4882a593Smuzhiyun 	if (netif_running(dev))
1094*4882a593Smuzhiyun 		return -EBUSY;
1095*4882a593Smuzhiyun 
1096*4882a593Smuzhiyun 	if (wpan_dev->lowpan_dev) {
1097*4882a593Smuzhiyun 		if (netif_running(wpan_dev->lowpan_dev))
1098*4882a593Smuzhiyun 			return -EBUSY;
1099*4882a593Smuzhiyun 	}
1100*4882a593Smuzhiyun 
1101*4882a593Smuzhiyun 	/* don't change address fields on monitor */
1102*4882a593Smuzhiyun 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR ||
1103*4882a593Smuzhiyun 	    !info->attrs[NL802154_ATTR_SHORT_ADDR])
1104*4882a593Smuzhiyun 		return -EINVAL;
1105*4882a593Smuzhiyun 
1106*4882a593Smuzhiyun 	short_addr = nla_get_le16(info->attrs[NL802154_ATTR_SHORT_ADDR]);
1107*4882a593Smuzhiyun 
1108*4882a593Smuzhiyun 	/* TODO
1109*4882a593Smuzhiyun 	 * I am not sure about to check here on broadcast short_addr.
1110*4882a593Smuzhiyun 	 * Broadcast is a valid setting, comment from 802.15.4:
1111*4882a593Smuzhiyun 	 * A value of 0xfffe indicates that the device has
1112*4882a593Smuzhiyun 	 * associated but has not been allocated an address. A
1113*4882a593Smuzhiyun 	 * value of 0xffff indicates that the device does not
1114*4882a593Smuzhiyun 	 * have a short address.
1115*4882a593Smuzhiyun 	 *
1116*4882a593Smuzhiyun 	 * I think we should allow to set these settings but
1117*4882a593Smuzhiyun 	 * don't allow to allow socket communication with it.
1118*4882a593Smuzhiyun 	 */
1119*4882a593Smuzhiyun 	if (short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC) ||
1120*4882a593Smuzhiyun 	    short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST))
1121*4882a593Smuzhiyun 		return -EINVAL;
1122*4882a593Smuzhiyun 
1123*4882a593Smuzhiyun 	return rdev_set_short_addr(rdev, wpan_dev, short_addr);
1124*4882a593Smuzhiyun }
1125*4882a593Smuzhiyun 
1126*4882a593Smuzhiyun static int
nl802154_set_backoff_exponent(struct sk_buff * skb,struct genl_info * info)1127*4882a593Smuzhiyun nl802154_set_backoff_exponent(struct sk_buff *skb, struct genl_info *info)
1128*4882a593Smuzhiyun {
1129*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1130*4882a593Smuzhiyun 	struct net_device *dev = info->user_ptr[1];
1131*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1132*4882a593Smuzhiyun 	u8 min_be, max_be;
1133*4882a593Smuzhiyun 
1134*4882a593Smuzhiyun 	/* should be set on netif open inside phy settings */
1135*4882a593Smuzhiyun 	if (netif_running(dev))
1136*4882a593Smuzhiyun 		return -EBUSY;
1137*4882a593Smuzhiyun 
1138*4882a593Smuzhiyun 	if (!info->attrs[NL802154_ATTR_MIN_BE] ||
1139*4882a593Smuzhiyun 	    !info->attrs[NL802154_ATTR_MAX_BE])
1140*4882a593Smuzhiyun 		return -EINVAL;
1141*4882a593Smuzhiyun 
1142*4882a593Smuzhiyun 	min_be = nla_get_u8(info->attrs[NL802154_ATTR_MIN_BE]);
1143*4882a593Smuzhiyun 	max_be = nla_get_u8(info->attrs[NL802154_ATTR_MAX_BE]);
1144*4882a593Smuzhiyun 
1145*4882a593Smuzhiyun 	/* check 802.15.4 constraints */
1146*4882a593Smuzhiyun 	if (min_be < rdev->wpan_phy.supported.min_minbe ||
1147*4882a593Smuzhiyun 	    min_be > rdev->wpan_phy.supported.max_minbe ||
1148*4882a593Smuzhiyun 	    max_be < rdev->wpan_phy.supported.min_maxbe ||
1149*4882a593Smuzhiyun 	    max_be > rdev->wpan_phy.supported.max_maxbe ||
1150*4882a593Smuzhiyun 	    min_be > max_be)
1151*4882a593Smuzhiyun 		return -EINVAL;
1152*4882a593Smuzhiyun 
1153*4882a593Smuzhiyun 	return rdev_set_backoff_exponent(rdev, wpan_dev, min_be, max_be);
1154*4882a593Smuzhiyun }
1155*4882a593Smuzhiyun 
1156*4882a593Smuzhiyun static int
nl802154_set_max_csma_backoffs(struct sk_buff * skb,struct genl_info * info)1157*4882a593Smuzhiyun nl802154_set_max_csma_backoffs(struct sk_buff *skb, struct genl_info *info)
1158*4882a593Smuzhiyun {
1159*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1160*4882a593Smuzhiyun 	struct net_device *dev = info->user_ptr[1];
1161*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1162*4882a593Smuzhiyun 	u8 max_csma_backoffs;
1163*4882a593Smuzhiyun 
1164*4882a593Smuzhiyun 	/* conflict here while other running iface settings */
1165*4882a593Smuzhiyun 	if (netif_running(dev))
1166*4882a593Smuzhiyun 		return -EBUSY;
1167*4882a593Smuzhiyun 
1168*4882a593Smuzhiyun 	if (!info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS])
1169*4882a593Smuzhiyun 		return -EINVAL;
1170*4882a593Smuzhiyun 
1171*4882a593Smuzhiyun 	max_csma_backoffs = nla_get_u8(
1172*4882a593Smuzhiyun 			info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]);
1173*4882a593Smuzhiyun 
1174*4882a593Smuzhiyun 	/* check 802.15.4 constraints */
1175*4882a593Smuzhiyun 	if (max_csma_backoffs < rdev->wpan_phy.supported.min_csma_backoffs ||
1176*4882a593Smuzhiyun 	    max_csma_backoffs > rdev->wpan_phy.supported.max_csma_backoffs)
1177*4882a593Smuzhiyun 		return -EINVAL;
1178*4882a593Smuzhiyun 
1179*4882a593Smuzhiyun 	return rdev_set_max_csma_backoffs(rdev, wpan_dev, max_csma_backoffs);
1180*4882a593Smuzhiyun }
1181*4882a593Smuzhiyun 
1182*4882a593Smuzhiyun static int
nl802154_set_max_frame_retries(struct sk_buff * skb,struct genl_info * info)1183*4882a593Smuzhiyun nl802154_set_max_frame_retries(struct sk_buff *skb, struct genl_info *info)
1184*4882a593Smuzhiyun {
1185*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1186*4882a593Smuzhiyun 	struct net_device *dev = info->user_ptr[1];
1187*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1188*4882a593Smuzhiyun 	s8 max_frame_retries;
1189*4882a593Smuzhiyun 
1190*4882a593Smuzhiyun 	if (netif_running(dev))
1191*4882a593Smuzhiyun 		return -EBUSY;
1192*4882a593Smuzhiyun 
1193*4882a593Smuzhiyun 	if (!info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES])
1194*4882a593Smuzhiyun 		return -EINVAL;
1195*4882a593Smuzhiyun 
1196*4882a593Smuzhiyun 	max_frame_retries = nla_get_s8(
1197*4882a593Smuzhiyun 			info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]);
1198*4882a593Smuzhiyun 
1199*4882a593Smuzhiyun 	/* check 802.15.4 constraints */
1200*4882a593Smuzhiyun 	if (max_frame_retries < rdev->wpan_phy.supported.min_frame_retries ||
1201*4882a593Smuzhiyun 	    max_frame_retries > rdev->wpan_phy.supported.max_frame_retries)
1202*4882a593Smuzhiyun 		return -EINVAL;
1203*4882a593Smuzhiyun 
1204*4882a593Smuzhiyun 	return rdev_set_max_frame_retries(rdev, wpan_dev, max_frame_retries);
1205*4882a593Smuzhiyun }
1206*4882a593Smuzhiyun 
nl802154_set_lbt_mode(struct sk_buff * skb,struct genl_info * info)1207*4882a593Smuzhiyun static int nl802154_set_lbt_mode(struct sk_buff *skb, struct genl_info *info)
1208*4882a593Smuzhiyun {
1209*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1210*4882a593Smuzhiyun 	struct net_device *dev = info->user_ptr[1];
1211*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1212*4882a593Smuzhiyun 	int mode;
1213*4882a593Smuzhiyun 
1214*4882a593Smuzhiyun 	if (netif_running(dev))
1215*4882a593Smuzhiyun 		return -EBUSY;
1216*4882a593Smuzhiyun 
1217*4882a593Smuzhiyun 	if (!info->attrs[NL802154_ATTR_LBT_MODE])
1218*4882a593Smuzhiyun 		return -EINVAL;
1219*4882a593Smuzhiyun 
1220*4882a593Smuzhiyun 	mode = nla_get_u8(info->attrs[NL802154_ATTR_LBT_MODE]);
1221*4882a593Smuzhiyun 
1222*4882a593Smuzhiyun 	if (mode != 0 && mode != 1)
1223*4882a593Smuzhiyun 		return -EINVAL;
1224*4882a593Smuzhiyun 
1225*4882a593Smuzhiyun 	if (!wpan_phy_supported_bool(mode, rdev->wpan_phy.supported.lbt))
1226*4882a593Smuzhiyun 		return -EINVAL;
1227*4882a593Smuzhiyun 
1228*4882a593Smuzhiyun 	return rdev_set_lbt_mode(rdev, wpan_dev, mode);
1229*4882a593Smuzhiyun }
1230*4882a593Smuzhiyun 
1231*4882a593Smuzhiyun static int
nl802154_set_ackreq_default(struct sk_buff * skb,struct genl_info * info)1232*4882a593Smuzhiyun nl802154_set_ackreq_default(struct sk_buff *skb, struct genl_info *info)
1233*4882a593Smuzhiyun {
1234*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1235*4882a593Smuzhiyun 	struct net_device *dev = info->user_ptr[1];
1236*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1237*4882a593Smuzhiyun 	int ackreq;
1238*4882a593Smuzhiyun 
1239*4882a593Smuzhiyun 	if (netif_running(dev))
1240*4882a593Smuzhiyun 		return -EBUSY;
1241*4882a593Smuzhiyun 
1242*4882a593Smuzhiyun 	if (!info->attrs[NL802154_ATTR_ACKREQ_DEFAULT])
1243*4882a593Smuzhiyun 		return -EINVAL;
1244*4882a593Smuzhiyun 
1245*4882a593Smuzhiyun 	ackreq = nla_get_u8(info->attrs[NL802154_ATTR_ACKREQ_DEFAULT]);
1246*4882a593Smuzhiyun 
1247*4882a593Smuzhiyun 	if (ackreq != 0 && ackreq != 1)
1248*4882a593Smuzhiyun 		return -EINVAL;
1249*4882a593Smuzhiyun 
1250*4882a593Smuzhiyun 	return rdev_set_ackreq_default(rdev, wpan_dev, ackreq);
1251*4882a593Smuzhiyun }
1252*4882a593Smuzhiyun 
nl802154_wpan_phy_netns(struct sk_buff * skb,struct genl_info * info)1253*4882a593Smuzhiyun static int nl802154_wpan_phy_netns(struct sk_buff *skb, struct genl_info *info)
1254*4882a593Smuzhiyun {
1255*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1256*4882a593Smuzhiyun 	struct net *net;
1257*4882a593Smuzhiyun 	int err;
1258*4882a593Smuzhiyun 
1259*4882a593Smuzhiyun 	if (info->attrs[NL802154_ATTR_PID]) {
1260*4882a593Smuzhiyun 		u32 pid = nla_get_u32(info->attrs[NL802154_ATTR_PID]);
1261*4882a593Smuzhiyun 
1262*4882a593Smuzhiyun 		net = get_net_ns_by_pid(pid);
1263*4882a593Smuzhiyun 	} else if (info->attrs[NL802154_ATTR_NETNS_FD]) {
1264*4882a593Smuzhiyun 		u32 fd = nla_get_u32(info->attrs[NL802154_ATTR_NETNS_FD]);
1265*4882a593Smuzhiyun 
1266*4882a593Smuzhiyun 		net = get_net_ns_by_fd(fd);
1267*4882a593Smuzhiyun 	} else {
1268*4882a593Smuzhiyun 		return -EINVAL;
1269*4882a593Smuzhiyun 	}
1270*4882a593Smuzhiyun 
1271*4882a593Smuzhiyun 	if (IS_ERR(net))
1272*4882a593Smuzhiyun 		return PTR_ERR(net);
1273*4882a593Smuzhiyun 
1274*4882a593Smuzhiyun 	err = 0;
1275*4882a593Smuzhiyun 
1276*4882a593Smuzhiyun 	/* check if anything to do */
1277*4882a593Smuzhiyun 	if (!net_eq(wpan_phy_net(&rdev->wpan_phy), net))
1278*4882a593Smuzhiyun 		err = cfg802154_switch_netns(rdev, net);
1279*4882a593Smuzhiyun 
1280*4882a593Smuzhiyun 	put_net(net);
1281*4882a593Smuzhiyun 	return err;
1282*4882a593Smuzhiyun }
1283*4882a593Smuzhiyun 
1284*4882a593Smuzhiyun #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
1285*4882a593Smuzhiyun static const struct nla_policy nl802154_dev_addr_policy[NL802154_DEV_ADDR_ATTR_MAX + 1] = {
1286*4882a593Smuzhiyun 	[NL802154_DEV_ADDR_ATTR_PAN_ID] = { .type = NLA_U16 },
1287*4882a593Smuzhiyun 	[NL802154_DEV_ADDR_ATTR_MODE] = { .type = NLA_U32 },
1288*4882a593Smuzhiyun 	[NL802154_DEV_ADDR_ATTR_SHORT] = { .type = NLA_U16 },
1289*4882a593Smuzhiyun 	[NL802154_DEV_ADDR_ATTR_EXTENDED] = { .type = NLA_U64 },
1290*4882a593Smuzhiyun };
1291*4882a593Smuzhiyun 
1292*4882a593Smuzhiyun static int
ieee802154_llsec_parse_dev_addr(struct nlattr * nla,struct ieee802154_addr * addr)1293*4882a593Smuzhiyun ieee802154_llsec_parse_dev_addr(struct nlattr *nla,
1294*4882a593Smuzhiyun 				struct ieee802154_addr *addr)
1295*4882a593Smuzhiyun {
1296*4882a593Smuzhiyun 	struct nlattr *attrs[NL802154_DEV_ADDR_ATTR_MAX + 1];
1297*4882a593Smuzhiyun 
1298*4882a593Smuzhiyun 	if (!nla || nla_parse_nested_deprecated(attrs, NL802154_DEV_ADDR_ATTR_MAX, nla, nl802154_dev_addr_policy, NULL))
1299*4882a593Smuzhiyun 		return -EINVAL;
1300*4882a593Smuzhiyun 
1301*4882a593Smuzhiyun 	if (!attrs[NL802154_DEV_ADDR_ATTR_PAN_ID] || !attrs[NL802154_DEV_ADDR_ATTR_MODE])
1302*4882a593Smuzhiyun 		return -EINVAL;
1303*4882a593Smuzhiyun 
1304*4882a593Smuzhiyun 	addr->pan_id = nla_get_le16(attrs[NL802154_DEV_ADDR_ATTR_PAN_ID]);
1305*4882a593Smuzhiyun 	addr->mode = nla_get_u32(attrs[NL802154_DEV_ADDR_ATTR_MODE]);
1306*4882a593Smuzhiyun 	switch (addr->mode) {
1307*4882a593Smuzhiyun 	case NL802154_DEV_ADDR_SHORT:
1308*4882a593Smuzhiyun 		if (!attrs[NL802154_DEV_ADDR_ATTR_SHORT])
1309*4882a593Smuzhiyun 			return -EINVAL;
1310*4882a593Smuzhiyun 		addr->short_addr = nla_get_le16(attrs[NL802154_DEV_ADDR_ATTR_SHORT]);
1311*4882a593Smuzhiyun 		break;
1312*4882a593Smuzhiyun 	case NL802154_DEV_ADDR_EXTENDED:
1313*4882a593Smuzhiyun 		if (!attrs[NL802154_DEV_ADDR_ATTR_EXTENDED])
1314*4882a593Smuzhiyun 			return -EINVAL;
1315*4882a593Smuzhiyun 		addr->extended_addr = nla_get_le64(attrs[NL802154_DEV_ADDR_ATTR_EXTENDED]);
1316*4882a593Smuzhiyun 		break;
1317*4882a593Smuzhiyun 	default:
1318*4882a593Smuzhiyun 		return -EINVAL;
1319*4882a593Smuzhiyun 	}
1320*4882a593Smuzhiyun 
1321*4882a593Smuzhiyun 	return 0;
1322*4882a593Smuzhiyun }
1323*4882a593Smuzhiyun 
1324*4882a593Smuzhiyun static const struct nla_policy nl802154_key_id_policy[NL802154_KEY_ID_ATTR_MAX + 1] = {
1325*4882a593Smuzhiyun 	[NL802154_KEY_ID_ATTR_MODE] = { .type = NLA_U32 },
1326*4882a593Smuzhiyun 	[NL802154_KEY_ID_ATTR_INDEX] = { .type = NLA_U8 },
1327*4882a593Smuzhiyun 	[NL802154_KEY_ID_ATTR_IMPLICIT] = { .type = NLA_NESTED },
1328*4882a593Smuzhiyun 	[NL802154_KEY_ID_ATTR_SOURCE_SHORT] = { .type = NLA_U32 },
1329*4882a593Smuzhiyun 	[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED] = { .type = NLA_U64 },
1330*4882a593Smuzhiyun };
1331*4882a593Smuzhiyun 
1332*4882a593Smuzhiyun static int
ieee802154_llsec_parse_key_id(struct nlattr * nla,struct ieee802154_llsec_key_id * desc)1333*4882a593Smuzhiyun ieee802154_llsec_parse_key_id(struct nlattr *nla,
1334*4882a593Smuzhiyun 			      struct ieee802154_llsec_key_id *desc)
1335*4882a593Smuzhiyun {
1336*4882a593Smuzhiyun 	struct nlattr *attrs[NL802154_KEY_ID_ATTR_MAX + 1];
1337*4882a593Smuzhiyun 
1338*4882a593Smuzhiyun 	if (!nla || nla_parse_nested_deprecated(attrs, NL802154_KEY_ID_ATTR_MAX, nla, nl802154_key_id_policy, NULL))
1339*4882a593Smuzhiyun 		return -EINVAL;
1340*4882a593Smuzhiyun 
1341*4882a593Smuzhiyun 	if (!attrs[NL802154_KEY_ID_ATTR_MODE])
1342*4882a593Smuzhiyun 		return -EINVAL;
1343*4882a593Smuzhiyun 
1344*4882a593Smuzhiyun 	desc->mode = nla_get_u32(attrs[NL802154_KEY_ID_ATTR_MODE]);
1345*4882a593Smuzhiyun 	switch (desc->mode) {
1346*4882a593Smuzhiyun 	case NL802154_KEY_ID_MODE_IMPLICIT:
1347*4882a593Smuzhiyun 		if (!attrs[NL802154_KEY_ID_ATTR_IMPLICIT])
1348*4882a593Smuzhiyun 			return -EINVAL;
1349*4882a593Smuzhiyun 
1350*4882a593Smuzhiyun 		if (ieee802154_llsec_parse_dev_addr(attrs[NL802154_KEY_ID_ATTR_IMPLICIT],
1351*4882a593Smuzhiyun 						    &desc->device_addr) < 0)
1352*4882a593Smuzhiyun 			return -EINVAL;
1353*4882a593Smuzhiyun 		break;
1354*4882a593Smuzhiyun 	case NL802154_KEY_ID_MODE_INDEX:
1355*4882a593Smuzhiyun 		break;
1356*4882a593Smuzhiyun 	case NL802154_KEY_ID_MODE_INDEX_SHORT:
1357*4882a593Smuzhiyun 		if (!attrs[NL802154_KEY_ID_ATTR_SOURCE_SHORT])
1358*4882a593Smuzhiyun 			return -EINVAL;
1359*4882a593Smuzhiyun 
1360*4882a593Smuzhiyun 		desc->short_source = nla_get_le32(attrs[NL802154_KEY_ID_ATTR_SOURCE_SHORT]);
1361*4882a593Smuzhiyun 		break;
1362*4882a593Smuzhiyun 	case NL802154_KEY_ID_MODE_INDEX_EXTENDED:
1363*4882a593Smuzhiyun 		if (!attrs[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED])
1364*4882a593Smuzhiyun 			return -EINVAL;
1365*4882a593Smuzhiyun 
1366*4882a593Smuzhiyun 		desc->extended_source = nla_get_le64(attrs[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED]);
1367*4882a593Smuzhiyun 		break;
1368*4882a593Smuzhiyun 	default:
1369*4882a593Smuzhiyun 		return -EINVAL;
1370*4882a593Smuzhiyun 	}
1371*4882a593Smuzhiyun 
1372*4882a593Smuzhiyun 	if (desc->mode != NL802154_KEY_ID_MODE_IMPLICIT) {
1373*4882a593Smuzhiyun 		if (!attrs[NL802154_KEY_ID_ATTR_INDEX])
1374*4882a593Smuzhiyun 			return -EINVAL;
1375*4882a593Smuzhiyun 
1376*4882a593Smuzhiyun 		/* TODO change id to idx */
1377*4882a593Smuzhiyun 		desc->id = nla_get_u8(attrs[NL802154_KEY_ID_ATTR_INDEX]);
1378*4882a593Smuzhiyun 	}
1379*4882a593Smuzhiyun 
1380*4882a593Smuzhiyun 	return 0;
1381*4882a593Smuzhiyun }
1382*4882a593Smuzhiyun 
nl802154_set_llsec_params(struct sk_buff * skb,struct genl_info * info)1383*4882a593Smuzhiyun static int nl802154_set_llsec_params(struct sk_buff *skb,
1384*4882a593Smuzhiyun 				     struct genl_info *info)
1385*4882a593Smuzhiyun {
1386*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1387*4882a593Smuzhiyun 	struct net_device *dev = info->user_ptr[1];
1388*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1389*4882a593Smuzhiyun 	struct ieee802154_llsec_params params;
1390*4882a593Smuzhiyun 	u32 changed = 0;
1391*4882a593Smuzhiyun 	int ret;
1392*4882a593Smuzhiyun 
1393*4882a593Smuzhiyun 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
1394*4882a593Smuzhiyun 		return -EOPNOTSUPP;
1395*4882a593Smuzhiyun 
1396*4882a593Smuzhiyun 	if (info->attrs[NL802154_ATTR_SEC_ENABLED]) {
1397*4882a593Smuzhiyun 		u8 enabled;
1398*4882a593Smuzhiyun 
1399*4882a593Smuzhiyun 		enabled = nla_get_u8(info->attrs[NL802154_ATTR_SEC_ENABLED]);
1400*4882a593Smuzhiyun 		if (enabled != 0 && enabled != 1)
1401*4882a593Smuzhiyun 			return -EINVAL;
1402*4882a593Smuzhiyun 
1403*4882a593Smuzhiyun 		params.enabled = nla_get_u8(info->attrs[NL802154_ATTR_SEC_ENABLED]);
1404*4882a593Smuzhiyun 		changed |= IEEE802154_LLSEC_PARAM_ENABLED;
1405*4882a593Smuzhiyun 	}
1406*4882a593Smuzhiyun 
1407*4882a593Smuzhiyun 	if (info->attrs[NL802154_ATTR_SEC_OUT_KEY_ID]) {
1408*4882a593Smuzhiyun 		ret = ieee802154_llsec_parse_key_id(info->attrs[NL802154_ATTR_SEC_OUT_KEY_ID],
1409*4882a593Smuzhiyun 						    &params.out_key);
1410*4882a593Smuzhiyun 		if (ret < 0)
1411*4882a593Smuzhiyun 			return ret;
1412*4882a593Smuzhiyun 
1413*4882a593Smuzhiyun 		changed |= IEEE802154_LLSEC_PARAM_OUT_KEY;
1414*4882a593Smuzhiyun 	}
1415*4882a593Smuzhiyun 
1416*4882a593Smuzhiyun 	if (info->attrs[NL802154_ATTR_SEC_OUT_LEVEL]) {
1417*4882a593Smuzhiyun 		params.out_level = nla_get_u32(info->attrs[NL802154_ATTR_SEC_OUT_LEVEL]);
1418*4882a593Smuzhiyun 		if (params.out_level > NL802154_SECLEVEL_MAX)
1419*4882a593Smuzhiyun 			return -EINVAL;
1420*4882a593Smuzhiyun 
1421*4882a593Smuzhiyun 		changed |= IEEE802154_LLSEC_PARAM_OUT_LEVEL;
1422*4882a593Smuzhiyun 	}
1423*4882a593Smuzhiyun 
1424*4882a593Smuzhiyun 	if (info->attrs[NL802154_ATTR_SEC_FRAME_COUNTER]) {
1425*4882a593Smuzhiyun 		params.frame_counter = nla_get_be32(info->attrs[NL802154_ATTR_SEC_FRAME_COUNTER]);
1426*4882a593Smuzhiyun 		changed |= IEEE802154_LLSEC_PARAM_FRAME_COUNTER;
1427*4882a593Smuzhiyun 	}
1428*4882a593Smuzhiyun 
1429*4882a593Smuzhiyun 	return rdev_set_llsec_params(rdev, wpan_dev, &params, changed);
1430*4882a593Smuzhiyun }
1431*4882a593Smuzhiyun 
nl802154_send_key(struct sk_buff * msg,u32 cmd,u32 portid,u32 seq,int flags,struct cfg802154_registered_device * rdev,struct net_device * dev,const struct ieee802154_llsec_key_entry * key)1432*4882a593Smuzhiyun static int nl802154_send_key(struct sk_buff *msg, u32 cmd, u32 portid,
1433*4882a593Smuzhiyun 			     u32 seq, int flags,
1434*4882a593Smuzhiyun 			     struct cfg802154_registered_device *rdev,
1435*4882a593Smuzhiyun 			     struct net_device *dev,
1436*4882a593Smuzhiyun 			     const struct ieee802154_llsec_key_entry *key)
1437*4882a593Smuzhiyun {
1438*4882a593Smuzhiyun 	void *hdr;
1439*4882a593Smuzhiyun 	u32 commands[NL802154_CMD_FRAME_NR_IDS / 32];
1440*4882a593Smuzhiyun 	struct nlattr *nl_key, *nl_key_id;
1441*4882a593Smuzhiyun 
1442*4882a593Smuzhiyun 	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
1443*4882a593Smuzhiyun 	if (!hdr)
1444*4882a593Smuzhiyun 		return -ENOBUFS;
1445*4882a593Smuzhiyun 
1446*4882a593Smuzhiyun 	if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
1447*4882a593Smuzhiyun 		goto nla_put_failure;
1448*4882a593Smuzhiyun 
1449*4882a593Smuzhiyun 	nl_key = nla_nest_start_noflag(msg, NL802154_ATTR_SEC_KEY);
1450*4882a593Smuzhiyun 	if (!nl_key)
1451*4882a593Smuzhiyun 		goto nla_put_failure;
1452*4882a593Smuzhiyun 
1453*4882a593Smuzhiyun 	nl_key_id = nla_nest_start_noflag(msg, NL802154_KEY_ATTR_ID);
1454*4882a593Smuzhiyun 	if (!nl_key_id)
1455*4882a593Smuzhiyun 		goto nla_put_failure;
1456*4882a593Smuzhiyun 
1457*4882a593Smuzhiyun 	if (ieee802154_llsec_send_key_id(msg, &key->id) < 0)
1458*4882a593Smuzhiyun 		goto nla_put_failure;
1459*4882a593Smuzhiyun 
1460*4882a593Smuzhiyun 	nla_nest_end(msg, nl_key_id);
1461*4882a593Smuzhiyun 
1462*4882a593Smuzhiyun 	if (nla_put_u8(msg, NL802154_KEY_ATTR_USAGE_FRAMES,
1463*4882a593Smuzhiyun 		       key->key->frame_types))
1464*4882a593Smuzhiyun 		goto nla_put_failure;
1465*4882a593Smuzhiyun 
1466*4882a593Smuzhiyun 	if (key->key->frame_types & BIT(NL802154_FRAME_CMD)) {
1467*4882a593Smuzhiyun 		/* TODO for each nested */
1468*4882a593Smuzhiyun 		memset(commands, 0, sizeof(commands));
1469*4882a593Smuzhiyun 		commands[7] = key->key->cmd_frame_ids;
1470*4882a593Smuzhiyun 		if (nla_put(msg, NL802154_KEY_ATTR_USAGE_CMDS,
1471*4882a593Smuzhiyun 			    sizeof(commands), commands))
1472*4882a593Smuzhiyun 			goto nla_put_failure;
1473*4882a593Smuzhiyun 	}
1474*4882a593Smuzhiyun 
1475*4882a593Smuzhiyun 	if (nla_put(msg, NL802154_KEY_ATTR_BYTES, NL802154_KEY_SIZE,
1476*4882a593Smuzhiyun 		    key->key->key))
1477*4882a593Smuzhiyun 		goto nla_put_failure;
1478*4882a593Smuzhiyun 
1479*4882a593Smuzhiyun 	nla_nest_end(msg, nl_key);
1480*4882a593Smuzhiyun 	genlmsg_end(msg, hdr);
1481*4882a593Smuzhiyun 
1482*4882a593Smuzhiyun 	return 0;
1483*4882a593Smuzhiyun 
1484*4882a593Smuzhiyun nla_put_failure:
1485*4882a593Smuzhiyun 	genlmsg_cancel(msg, hdr);
1486*4882a593Smuzhiyun 	return -EMSGSIZE;
1487*4882a593Smuzhiyun }
1488*4882a593Smuzhiyun 
1489*4882a593Smuzhiyun static int
nl802154_dump_llsec_key(struct sk_buff * skb,struct netlink_callback * cb)1490*4882a593Smuzhiyun nl802154_dump_llsec_key(struct sk_buff *skb, struct netlink_callback *cb)
1491*4882a593Smuzhiyun {
1492*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = NULL;
1493*4882a593Smuzhiyun 	struct ieee802154_llsec_key_entry *key;
1494*4882a593Smuzhiyun 	struct ieee802154_llsec_table *table;
1495*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev;
1496*4882a593Smuzhiyun 	int err;
1497*4882a593Smuzhiyun 
1498*4882a593Smuzhiyun 	err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
1499*4882a593Smuzhiyun 	if (err)
1500*4882a593Smuzhiyun 		return err;
1501*4882a593Smuzhiyun 
1502*4882a593Smuzhiyun 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) {
1503*4882a593Smuzhiyun 		err = skb->len;
1504*4882a593Smuzhiyun 		goto out_err;
1505*4882a593Smuzhiyun 	}
1506*4882a593Smuzhiyun 
1507*4882a593Smuzhiyun 	if (!wpan_dev->netdev) {
1508*4882a593Smuzhiyun 		err = -EINVAL;
1509*4882a593Smuzhiyun 		goto out_err;
1510*4882a593Smuzhiyun 	}
1511*4882a593Smuzhiyun 
1512*4882a593Smuzhiyun 	rdev_lock_llsec_table(rdev, wpan_dev);
1513*4882a593Smuzhiyun 	rdev_get_llsec_table(rdev, wpan_dev, &table);
1514*4882a593Smuzhiyun 
1515*4882a593Smuzhiyun 	/* TODO make it like station dump */
1516*4882a593Smuzhiyun 	if (cb->args[2])
1517*4882a593Smuzhiyun 		goto out;
1518*4882a593Smuzhiyun 
1519*4882a593Smuzhiyun 	list_for_each_entry(key, &table->keys, list) {
1520*4882a593Smuzhiyun 		if (nl802154_send_key(skb, NL802154_CMD_NEW_SEC_KEY,
1521*4882a593Smuzhiyun 				      NETLINK_CB(cb->skb).portid,
1522*4882a593Smuzhiyun 				      cb->nlh->nlmsg_seq, NLM_F_MULTI,
1523*4882a593Smuzhiyun 				      rdev, wpan_dev->netdev, key) < 0) {
1524*4882a593Smuzhiyun 			/* TODO */
1525*4882a593Smuzhiyun 			err = -EIO;
1526*4882a593Smuzhiyun 			rdev_unlock_llsec_table(rdev, wpan_dev);
1527*4882a593Smuzhiyun 			goto out_err;
1528*4882a593Smuzhiyun 		}
1529*4882a593Smuzhiyun 	}
1530*4882a593Smuzhiyun 
1531*4882a593Smuzhiyun 	cb->args[2] = 1;
1532*4882a593Smuzhiyun 
1533*4882a593Smuzhiyun out:
1534*4882a593Smuzhiyun 	rdev_unlock_llsec_table(rdev, wpan_dev);
1535*4882a593Smuzhiyun 	err = skb->len;
1536*4882a593Smuzhiyun out_err:
1537*4882a593Smuzhiyun 	nl802154_finish_wpan_dev_dump(rdev);
1538*4882a593Smuzhiyun 
1539*4882a593Smuzhiyun 	return err;
1540*4882a593Smuzhiyun }
1541*4882a593Smuzhiyun 
1542*4882a593Smuzhiyun static const struct nla_policy nl802154_key_policy[NL802154_KEY_ATTR_MAX + 1] = {
1543*4882a593Smuzhiyun 	[NL802154_KEY_ATTR_ID] = { NLA_NESTED },
1544*4882a593Smuzhiyun 	/* TODO handle it as for_each_nested and NLA_FLAG? */
1545*4882a593Smuzhiyun 	[NL802154_KEY_ATTR_USAGE_FRAMES] = { NLA_U8 },
1546*4882a593Smuzhiyun 	/* TODO handle it as for_each_nested, not static array? */
1547*4882a593Smuzhiyun 	[NL802154_KEY_ATTR_USAGE_CMDS] = { .len = NL802154_CMD_FRAME_NR_IDS / 8 },
1548*4882a593Smuzhiyun 	[NL802154_KEY_ATTR_BYTES] = { .len = NL802154_KEY_SIZE },
1549*4882a593Smuzhiyun };
1550*4882a593Smuzhiyun 
nl802154_add_llsec_key(struct sk_buff * skb,struct genl_info * info)1551*4882a593Smuzhiyun static int nl802154_add_llsec_key(struct sk_buff *skb, struct genl_info *info)
1552*4882a593Smuzhiyun {
1553*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1554*4882a593Smuzhiyun 	struct net_device *dev = info->user_ptr[1];
1555*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1556*4882a593Smuzhiyun 	struct nlattr *attrs[NL802154_KEY_ATTR_MAX + 1];
1557*4882a593Smuzhiyun 	struct ieee802154_llsec_key key = { };
1558*4882a593Smuzhiyun 	struct ieee802154_llsec_key_id id = { };
1559*4882a593Smuzhiyun 	u32 commands[NL802154_CMD_FRAME_NR_IDS / 32] = { };
1560*4882a593Smuzhiyun 
1561*4882a593Smuzhiyun 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
1562*4882a593Smuzhiyun 		return -EOPNOTSUPP;
1563*4882a593Smuzhiyun 
1564*4882a593Smuzhiyun 	if (!info->attrs[NL802154_ATTR_SEC_KEY] ||
1565*4882a593Smuzhiyun 	    nla_parse_nested_deprecated(attrs, NL802154_KEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_KEY], nl802154_key_policy, info->extack))
1566*4882a593Smuzhiyun 		return -EINVAL;
1567*4882a593Smuzhiyun 
1568*4882a593Smuzhiyun 	if (!attrs[NL802154_KEY_ATTR_USAGE_FRAMES] ||
1569*4882a593Smuzhiyun 	    !attrs[NL802154_KEY_ATTR_BYTES])
1570*4882a593Smuzhiyun 		return -EINVAL;
1571*4882a593Smuzhiyun 
1572*4882a593Smuzhiyun 	if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0)
1573*4882a593Smuzhiyun 		return -ENOBUFS;
1574*4882a593Smuzhiyun 
1575*4882a593Smuzhiyun 	key.frame_types = nla_get_u8(attrs[NL802154_KEY_ATTR_USAGE_FRAMES]);
1576*4882a593Smuzhiyun 	if (key.frame_types > BIT(NL802154_FRAME_MAX) ||
1577*4882a593Smuzhiyun 	    ((key.frame_types & BIT(NL802154_FRAME_CMD)) &&
1578*4882a593Smuzhiyun 	     !attrs[NL802154_KEY_ATTR_USAGE_CMDS]))
1579*4882a593Smuzhiyun 		return -EINVAL;
1580*4882a593Smuzhiyun 
1581*4882a593Smuzhiyun 	if (attrs[NL802154_KEY_ATTR_USAGE_CMDS]) {
1582*4882a593Smuzhiyun 		/* TODO for each nested */
1583*4882a593Smuzhiyun 		nla_memcpy(commands, attrs[NL802154_KEY_ATTR_USAGE_CMDS],
1584*4882a593Smuzhiyun 			   NL802154_CMD_FRAME_NR_IDS / 8);
1585*4882a593Smuzhiyun 
1586*4882a593Smuzhiyun 		/* TODO understand the -EINVAL logic here? last condition */
1587*4882a593Smuzhiyun 		if (commands[0] || commands[1] || commands[2] || commands[3] ||
1588*4882a593Smuzhiyun 		    commands[4] || commands[5] || commands[6] ||
1589*4882a593Smuzhiyun 		    commands[7] > BIT(NL802154_CMD_FRAME_MAX))
1590*4882a593Smuzhiyun 			return -EINVAL;
1591*4882a593Smuzhiyun 
1592*4882a593Smuzhiyun 		key.cmd_frame_ids = commands[7];
1593*4882a593Smuzhiyun 	} else {
1594*4882a593Smuzhiyun 		key.cmd_frame_ids = 0;
1595*4882a593Smuzhiyun 	}
1596*4882a593Smuzhiyun 
1597*4882a593Smuzhiyun 	nla_memcpy(key.key, attrs[NL802154_KEY_ATTR_BYTES], NL802154_KEY_SIZE);
1598*4882a593Smuzhiyun 
1599*4882a593Smuzhiyun 	if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0)
1600*4882a593Smuzhiyun 		return -ENOBUFS;
1601*4882a593Smuzhiyun 
1602*4882a593Smuzhiyun 	return rdev_add_llsec_key(rdev, wpan_dev, &id, &key);
1603*4882a593Smuzhiyun }
1604*4882a593Smuzhiyun 
nl802154_del_llsec_key(struct sk_buff * skb,struct genl_info * info)1605*4882a593Smuzhiyun static int nl802154_del_llsec_key(struct sk_buff *skb, struct genl_info *info)
1606*4882a593Smuzhiyun {
1607*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1608*4882a593Smuzhiyun 	struct net_device *dev = info->user_ptr[1];
1609*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1610*4882a593Smuzhiyun 	struct nlattr *attrs[NL802154_KEY_ATTR_MAX + 1];
1611*4882a593Smuzhiyun 	struct ieee802154_llsec_key_id id;
1612*4882a593Smuzhiyun 
1613*4882a593Smuzhiyun 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
1614*4882a593Smuzhiyun 		return -EOPNOTSUPP;
1615*4882a593Smuzhiyun 
1616*4882a593Smuzhiyun 	if (!info->attrs[NL802154_ATTR_SEC_KEY] ||
1617*4882a593Smuzhiyun 	    nla_parse_nested_deprecated(attrs, NL802154_KEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_KEY], nl802154_key_policy, info->extack))
1618*4882a593Smuzhiyun 		return -EINVAL;
1619*4882a593Smuzhiyun 
1620*4882a593Smuzhiyun 	if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0)
1621*4882a593Smuzhiyun 		return -ENOBUFS;
1622*4882a593Smuzhiyun 
1623*4882a593Smuzhiyun 	return rdev_del_llsec_key(rdev, wpan_dev, &id);
1624*4882a593Smuzhiyun }
1625*4882a593Smuzhiyun 
nl802154_send_device(struct sk_buff * msg,u32 cmd,u32 portid,u32 seq,int flags,struct cfg802154_registered_device * rdev,struct net_device * dev,const struct ieee802154_llsec_device * dev_desc)1626*4882a593Smuzhiyun static int nl802154_send_device(struct sk_buff *msg, u32 cmd, u32 portid,
1627*4882a593Smuzhiyun 				u32 seq, int flags,
1628*4882a593Smuzhiyun 				struct cfg802154_registered_device *rdev,
1629*4882a593Smuzhiyun 				struct net_device *dev,
1630*4882a593Smuzhiyun 				const struct ieee802154_llsec_device *dev_desc)
1631*4882a593Smuzhiyun {
1632*4882a593Smuzhiyun 	void *hdr;
1633*4882a593Smuzhiyun 	struct nlattr *nl_device;
1634*4882a593Smuzhiyun 
1635*4882a593Smuzhiyun 	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
1636*4882a593Smuzhiyun 	if (!hdr)
1637*4882a593Smuzhiyun 		return -ENOBUFS;
1638*4882a593Smuzhiyun 
1639*4882a593Smuzhiyun 	if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
1640*4882a593Smuzhiyun 		goto nla_put_failure;
1641*4882a593Smuzhiyun 
1642*4882a593Smuzhiyun 	nl_device = nla_nest_start_noflag(msg, NL802154_ATTR_SEC_DEVICE);
1643*4882a593Smuzhiyun 	if (!nl_device)
1644*4882a593Smuzhiyun 		goto nla_put_failure;
1645*4882a593Smuzhiyun 
1646*4882a593Smuzhiyun 	if (nla_put_u32(msg, NL802154_DEV_ATTR_FRAME_COUNTER,
1647*4882a593Smuzhiyun 			dev_desc->frame_counter) ||
1648*4882a593Smuzhiyun 	    nla_put_le16(msg, NL802154_DEV_ATTR_PAN_ID, dev_desc->pan_id) ||
1649*4882a593Smuzhiyun 	    nla_put_le16(msg, NL802154_DEV_ATTR_SHORT_ADDR,
1650*4882a593Smuzhiyun 			 dev_desc->short_addr) ||
1651*4882a593Smuzhiyun 	    nla_put_le64(msg, NL802154_DEV_ATTR_EXTENDED_ADDR,
1652*4882a593Smuzhiyun 			 dev_desc->hwaddr, NL802154_DEV_ATTR_PAD) ||
1653*4882a593Smuzhiyun 	    nla_put_u8(msg, NL802154_DEV_ATTR_SECLEVEL_EXEMPT,
1654*4882a593Smuzhiyun 		       dev_desc->seclevel_exempt) ||
1655*4882a593Smuzhiyun 	    nla_put_u32(msg, NL802154_DEV_ATTR_KEY_MODE, dev_desc->key_mode))
1656*4882a593Smuzhiyun 		goto nla_put_failure;
1657*4882a593Smuzhiyun 
1658*4882a593Smuzhiyun 	nla_nest_end(msg, nl_device);
1659*4882a593Smuzhiyun 	genlmsg_end(msg, hdr);
1660*4882a593Smuzhiyun 
1661*4882a593Smuzhiyun 	return 0;
1662*4882a593Smuzhiyun 
1663*4882a593Smuzhiyun nla_put_failure:
1664*4882a593Smuzhiyun 	genlmsg_cancel(msg, hdr);
1665*4882a593Smuzhiyun 	return -EMSGSIZE;
1666*4882a593Smuzhiyun }
1667*4882a593Smuzhiyun 
1668*4882a593Smuzhiyun static int
nl802154_dump_llsec_dev(struct sk_buff * skb,struct netlink_callback * cb)1669*4882a593Smuzhiyun nl802154_dump_llsec_dev(struct sk_buff *skb, struct netlink_callback *cb)
1670*4882a593Smuzhiyun {
1671*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = NULL;
1672*4882a593Smuzhiyun 	struct ieee802154_llsec_device *dev;
1673*4882a593Smuzhiyun 	struct ieee802154_llsec_table *table;
1674*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev;
1675*4882a593Smuzhiyun 	int err;
1676*4882a593Smuzhiyun 
1677*4882a593Smuzhiyun 	err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
1678*4882a593Smuzhiyun 	if (err)
1679*4882a593Smuzhiyun 		return err;
1680*4882a593Smuzhiyun 
1681*4882a593Smuzhiyun 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) {
1682*4882a593Smuzhiyun 		err = skb->len;
1683*4882a593Smuzhiyun 		goto out_err;
1684*4882a593Smuzhiyun 	}
1685*4882a593Smuzhiyun 
1686*4882a593Smuzhiyun 	if (!wpan_dev->netdev) {
1687*4882a593Smuzhiyun 		err = -EINVAL;
1688*4882a593Smuzhiyun 		goto out_err;
1689*4882a593Smuzhiyun 	}
1690*4882a593Smuzhiyun 
1691*4882a593Smuzhiyun 	rdev_lock_llsec_table(rdev, wpan_dev);
1692*4882a593Smuzhiyun 	rdev_get_llsec_table(rdev, wpan_dev, &table);
1693*4882a593Smuzhiyun 
1694*4882a593Smuzhiyun 	/* TODO make it like station dump */
1695*4882a593Smuzhiyun 	if (cb->args[2])
1696*4882a593Smuzhiyun 		goto out;
1697*4882a593Smuzhiyun 
1698*4882a593Smuzhiyun 	list_for_each_entry(dev, &table->devices, list) {
1699*4882a593Smuzhiyun 		if (nl802154_send_device(skb, NL802154_CMD_NEW_SEC_LEVEL,
1700*4882a593Smuzhiyun 					 NETLINK_CB(cb->skb).portid,
1701*4882a593Smuzhiyun 					 cb->nlh->nlmsg_seq, NLM_F_MULTI,
1702*4882a593Smuzhiyun 					 rdev, wpan_dev->netdev, dev) < 0) {
1703*4882a593Smuzhiyun 			/* TODO */
1704*4882a593Smuzhiyun 			err = -EIO;
1705*4882a593Smuzhiyun 			rdev_unlock_llsec_table(rdev, wpan_dev);
1706*4882a593Smuzhiyun 			goto out_err;
1707*4882a593Smuzhiyun 		}
1708*4882a593Smuzhiyun 	}
1709*4882a593Smuzhiyun 
1710*4882a593Smuzhiyun 	cb->args[2] = 1;
1711*4882a593Smuzhiyun 
1712*4882a593Smuzhiyun out:
1713*4882a593Smuzhiyun 	rdev_unlock_llsec_table(rdev, wpan_dev);
1714*4882a593Smuzhiyun 	err = skb->len;
1715*4882a593Smuzhiyun out_err:
1716*4882a593Smuzhiyun 	nl802154_finish_wpan_dev_dump(rdev);
1717*4882a593Smuzhiyun 
1718*4882a593Smuzhiyun 	return err;
1719*4882a593Smuzhiyun }
1720*4882a593Smuzhiyun 
1721*4882a593Smuzhiyun static const struct nla_policy nl802154_dev_policy[NL802154_DEV_ATTR_MAX + 1] = {
1722*4882a593Smuzhiyun 	[NL802154_DEV_ATTR_FRAME_COUNTER] = { NLA_U32 },
1723*4882a593Smuzhiyun 	[NL802154_DEV_ATTR_PAN_ID] = { .type = NLA_U16 },
1724*4882a593Smuzhiyun 	[NL802154_DEV_ATTR_SHORT_ADDR] = { .type = NLA_U16 },
1725*4882a593Smuzhiyun 	[NL802154_DEV_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
1726*4882a593Smuzhiyun 	[NL802154_DEV_ATTR_SECLEVEL_EXEMPT] = { NLA_U8 },
1727*4882a593Smuzhiyun 	[NL802154_DEV_ATTR_KEY_MODE] = { NLA_U32 },
1728*4882a593Smuzhiyun };
1729*4882a593Smuzhiyun 
1730*4882a593Smuzhiyun static int
ieee802154_llsec_parse_device(struct nlattr * nla,struct ieee802154_llsec_device * dev)1731*4882a593Smuzhiyun ieee802154_llsec_parse_device(struct nlattr *nla,
1732*4882a593Smuzhiyun 			      struct ieee802154_llsec_device *dev)
1733*4882a593Smuzhiyun {
1734*4882a593Smuzhiyun 	struct nlattr *attrs[NL802154_DEV_ATTR_MAX + 1];
1735*4882a593Smuzhiyun 
1736*4882a593Smuzhiyun 	if (!nla || nla_parse_nested_deprecated(attrs, NL802154_DEV_ATTR_MAX, nla, nl802154_dev_policy, NULL))
1737*4882a593Smuzhiyun 		return -EINVAL;
1738*4882a593Smuzhiyun 
1739*4882a593Smuzhiyun 	memset(dev, 0, sizeof(*dev));
1740*4882a593Smuzhiyun 
1741*4882a593Smuzhiyun 	if (!attrs[NL802154_DEV_ATTR_FRAME_COUNTER] ||
1742*4882a593Smuzhiyun 	    !attrs[NL802154_DEV_ATTR_PAN_ID] ||
1743*4882a593Smuzhiyun 	    !attrs[NL802154_DEV_ATTR_SHORT_ADDR] ||
1744*4882a593Smuzhiyun 	    !attrs[NL802154_DEV_ATTR_EXTENDED_ADDR] ||
1745*4882a593Smuzhiyun 	    !attrs[NL802154_DEV_ATTR_SECLEVEL_EXEMPT] ||
1746*4882a593Smuzhiyun 	    !attrs[NL802154_DEV_ATTR_KEY_MODE])
1747*4882a593Smuzhiyun 		return -EINVAL;
1748*4882a593Smuzhiyun 
1749*4882a593Smuzhiyun 	/* TODO be32 */
1750*4882a593Smuzhiyun 	dev->frame_counter = nla_get_u32(attrs[NL802154_DEV_ATTR_FRAME_COUNTER]);
1751*4882a593Smuzhiyun 	dev->pan_id = nla_get_le16(attrs[NL802154_DEV_ATTR_PAN_ID]);
1752*4882a593Smuzhiyun 	dev->short_addr = nla_get_le16(attrs[NL802154_DEV_ATTR_SHORT_ADDR]);
1753*4882a593Smuzhiyun 	/* TODO rename hwaddr to extended_addr */
1754*4882a593Smuzhiyun 	dev->hwaddr = nla_get_le64(attrs[NL802154_DEV_ATTR_EXTENDED_ADDR]);
1755*4882a593Smuzhiyun 	dev->seclevel_exempt = nla_get_u8(attrs[NL802154_DEV_ATTR_SECLEVEL_EXEMPT]);
1756*4882a593Smuzhiyun 	dev->key_mode = nla_get_u32(attrs[NL802154_DEV_ATTR_KEY_MODE]);
1757*4882a593Smuzhiyun 
1758*4882a593Smuzhiyun 	if (dev->key_mode > NL802154_DEVKEY_MAX ||
1759*4882a593Smuzhiyun 	    (dev->seclevel_exempt != 0 && dev->seclevel_exempt != 1))
1760*4882a593Smuzhiyun 		return -EINVAL;
1761*4882a593Smuzhiyun 
1762*4882a593Smuzhiyun 	return 0;
1763*4882a593Smuzhiyun }
1764*4882a593Smuzhiyun 
nl802154_add_llsec_dev(struct sk_buff * skb,struct genl_info * info)1765*4882a593Smuzhiyun static int nl802154_add_llsec_dev(struct sk_buff *skb, struct genl_info *info)
1766*4882a593Smuzhiyun {
1767*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1768*4882a593Smuzhiyun 	struct net_device *dev = info->user_ptr[1];
1769*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1770*4882a593Smuzhiyun 	struct ieee802154_llsec_device dev_desc;
1771*4882a593Smuzhiyun 
1772*4882a593Smuzhiyun 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
1773*4882a593Smuzhiyun 		return -EOPNOTSUPP;
1774*4882a593Smuzhiyun 
1775*4882a593Smuzhiyun 	if (ieee802154_llsec_parse_device(info->attrs[NL802154_ATTR_SEC_DEVICE],
1776*4882a593Smuzhiyun 					  &dev_desc) < 0)
1777*4882a593Smuzhiyun 		return -EINVAL;
1778*4882a593Smuzhiyun 
1779*4882a593Smuzhiyun 	return rdev_add_device(rdev, wpan_dev, &dev_desc);
1780*4882a593Smuzhiyun }
1781*4882a593Smuzhiyun 
nl802154_del_llsec_dev(struct sk_buff * skb,struct genl_info * info)1782*4882a593Smuzhiyun static int nl802154_del_llsec_dev(struct sk_buff *skb, struct genl_info *info)
1783*4882a593Smuzhiyun {
1784*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1785*4882a593Smuzhiyun 	struct net_device *dev = info->user_ptr[1];
1786*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1787*4882a593Smuzhiyun 	struct nlattr *attrs[NL802154_DEV_ATTR_MAX + 1];
1788*4882a593Smuzhiyun 	__le64 extended_addr;
1789*4882a593Smuzhiyun 
1790*4882a593Smuzhiyun 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
1791*4882a593Smuzhiyun 		return -EOPNOTSUPP;
1792*4882a593Smuzhiyun 
1793*4882a593Smuzhiyun 	if (!info->attrs[NL802154_ATTR_SEC_DEVICE] ||
1794*4882a593Smuzhiyun 	    nla_parse_nested_deprecated(attrs, NL802154_DEV_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_DEVICE], nl802154_dev_policy, info->extack))
1795*4882a593Smuzhiyun 		return -EINVAL;
1796*4882a593Smuzhiyun 
1797*4882a593Smuzhiyun 	if (!attrs[NL802154_DEV_ATTR_EXTENDED_ADDR])
1798*4882a593Smuzhiyun 		return -EINVAL;
1799*4882a593Smuzhiyun 
1800*4882a593Smuzhiyun 	extended_addr = nla_get_le64(attrs[NL802154_DEV_ATTR_EXTENDED_ADDR]);
1801*4882a593Smuzhiyun 	return rdev_del_device(rdev, wpan_dev, extended_addr);
1802*4882a593Smuzhiyun }
1803*4882a593Smuzhiyun 
nl802154_send_devkey(struct sk_buff * msg,u32 cmd,u32 portid,u32 seq,int flags,struct cfg802154_registered_device * rdev,struct net_device * dev,__le64 extended_addr,const struct ieee802154_llsec_device_key * devkey)1804*4882a593Smuzhiyun static int nl802154_send_devkey(struct sk_buff *msg, u32 cmd, u32 portid,
1805*4882a593Smuzhiyun 				u32 seq, int flags,
1806*4882a593Smuzhiyun 				struct cfg802154_registered_device *rdev,
1807*4882a593Smuzhiyun 				struct net_device *dev, __le64 extended_addr,
1808*4882a593Smuzhiyun 				const struct ieee802154_llsec_device_key *devkey)
1809*4882a593Smuzhiyun {
1810*4882a593Smuzhiyun 	void *hdr;
1811*4882a593Smuzhiyun 	struct nlattr *nl_devkey, *nl_key_id;
1812*4882a593Smuzhiyun 
1813*4882a593Smuzhiyun 	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
1814*4882a593Smuzhiyun 	if (!hdr)
1815*4882a593Smuzhiyun 		return -ENOBUFS;
1816*4882a593Smuzhiyun 
1817*4882a593Smuzhiyun 	if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
1818*4882a593Smuzhiyun 		goto nla_put_failure;
1819*4882a593Smuzhiyun 
1820*4882a593Smuzhiyun 	nl_devkey = nla_nest_start_noflag(msg, NL802154_ATTR_SEC_DEVKEY);
1821*4882a593Smuzhiyun 	if (!nl_devkey)
1822*4882a593Smuzhiyun 		goto nla_put_failure;
1823*4882a593Smuzhiyun 
1824*4882a593Smuzhiyun 	if (nla_put_le64(msg, NL802154_DEVKEY_ATTR_EXTENDED_ADDR,
1825*4882a593Smuzhiyun 			 extended_addr, NL802154_DEVKEY_ATTR_PAD) ||
1826*4882a593Smuzhiyun 	    nla_put_u32(msg, NL802154_DEVKEY_ATTR_FRAME_COUNTER,
1827*4882a593Smuzhiyun 			devkey->frame_counter))
1828*4882a593Smuzhiyun 		goto nla_put_failure;
1829*4882a593Smuzhiyun 
1830*4882a593Smuzhiyun 	nl_key_id = nla_nest_start_noflag(msg, NL802154_DEVKEY_ATTR_ID);
1831*4882a593Smuzhiyun 	if (!nl_key_id)
1832*4882a593Smuzhiyun 		goto nla_put_failure;
1833*4882a593Smuzhiyun 
1834*4882a593Smuzhiyun 	if (ieee802154_llsec_send_key_id(msg, &devkey->key_id) < 0)
1835*4882a593Smuzhiyun 		goto nla_put_failure;
1836*4882a593Smuzhiyun 
1837*4882a593Smuzhiyun 	nla_nest_end(msg, nl_key_id);
1838*4882a593Smuzhiyun 	nla_nest_end(msg, nl_devkey);
1839*4882a593Smuzhiyun 	genlmsg_end(msg, hdr);
1840*4882a593Smuzhiyun 
1841*4882a593Smuzhiyun 	return 0;
1842*4882a593Smuzhiyun 
1843*4882a593Smuzhiyun nla_put_failure:
1844*4882a593Smuzhiyun 	genlmsg_cancel(msg, hdr);
1845*4882a593Smuzhiyun 	return -EMSGSIZE;
1846*4882a593Smuzhiyun }
1847*4882a593Smuzhiyun 
1848*4882a593Smuzhiyun static int
nl802154_dump_llsec_devkey(struct sk_buff * skb,struct netlink_callback * cb)1849*4882a593Smuzhiyun nl802154_dump_llsec_devkey(struct sk_buff *skb, struct netlink_callback *cb)
1850*4882a593Smuzhiyun {
1851*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = NULL;
1852*4882a593Smuzhiyun 	struct ieee802154_llsec_device_key *kpos;
1853*4882a593Smuzhiyun 	struct ieee802154_llsec_device *dpos;
1854*4882a593Smuzhiyun 	struct ieee802154_llsec_table *table;
1855*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev;
1856*4882a593Smuzhiyun 	int err;
1857*4882a593Smuzhiyun 
1858*4882a593Smuzhiyun 	err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
1859*4882a593Smuzhiyun 	if (err)
1860*4882a593Smuzhiyun 		return err;
1861*4882a593Smuzhiyun 
1862*4882a593Smuzhiyun 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) {
1863*4882a593Smuzhiyun 		err = skb->len;
1864*4882a593Smuzhiyun 		goto out_err;
1865*4882a593Smuzhiyun 	}
1866*4882a593Smuzhiyun 
1867*4882a593Smuzhiyun 	if (!wpan_dev->netdev) {
1868*4882a593Smuzhiyun 		err = -EINVAL;
1869*4882a593Smuzhiyun 		goto out_err;
1870*4882a593Smuzhiyun 	}
1871*4882a593Smuzhiyun 
1872*4882a593Smuzhiyun 	rdev_lock_llsec_table(rdev, wpan_dev);
1873*4882a593Smuzhiyun 	rdev_get_llsec_table(rdev, wpan_dev, &table);
1874*4882a593Smuzhiyun 
1875*4882a593Smuzhiyun 	/* TODO make it like station dump */
1876*4882a593Smuzhiyun 	if (cb->args[2])
1877*4882a593Smuzhiyun 		goto out;
1878*4882a593Smuzhiyun 
1879*4882a593Smuzhiyun 	/* TODO look if remove devkey and do some nested attribute */
1880*4882a593Smuzhiyun 	list_for_each_entry(dpos, &table->devices, list) {
1881*4882a593Smuzhiyun 		list_for_each_entry(kpos, &dpos->keys, list) {
1882*4882a593Smuzhiyun 			if (nl802154_send_devkey(skb,
1883*4882a593Smuzhiyun 						 NL802154_CMD_NEW_SEC_LEVEL,
1884*4882a593Smuzhiyun 						 NETLINK_CB(cb->skb).portid,
1885*4882a593Smuzhiyun 						 cb->nlh->nlmsg_seq,
1886*4882a593Smuzhiyun 						 NLM_F_MULTI, rdev,
1887*4882a593Smuzhiyun 						 wpan_dev->netdev,
1888*4882a593Smuzhiyun 						 dpos->hwaddr,
1889*4882a593Smuzhiyun 						 kpos) < 0) {
1890*4882a593Smuzhiyun 				/* TODO */
1891*4882a593Smuzhiyun 				err = -EIO;
1892*4882a593Smuzhiyun 				rdev_unlock_llsec_table(rdev, wpan_dev);
1893*4882a593Smuzhiyun 				goto out_err;
1894*4882a593Smuzhiyun 			}
1895*4882a593Smuzhiyun 		}
1896*4882a593Smuzhiyun 	}
1897*4882a593Smuzhiyun 
1898*4882a593Smuzhiyun 	cb->args[2] = 1;
1899*4882a593Smuzhiyun 
1900*4882a593Smuzhiyun out:
1901*4882a593Smuzhiyun 	rdev_unlock_llsec_table(rdev, wpan_dev);
1902*4882a593Smuzhiyun 	err = skb->len;
1903*4882a593Smuzhiyun out_err:
1904*4882a593Smuzhiyun 	nl802154_finish_wpan_dev_dump(rdev);
1905*4882a593Smuzhiyun 
1906*4882a593Smuzhiyun 	return err;
1907*4882a593Smuzhiyun }
1908*4882a593Smuzhiyun 
1909*4882a593Smuzhiyun static const struct nla_policy nl802154_devkey_policy[NL802154_DEVKEY_ATTR_MAX + 1] = {
1910*4882a593Smuzhiyun 	[NL802154_DEVKEY_ATTR_FRAME_COUNTER] = { NLA_U32 },
1911*4882a593Smuzhiyun 	[NL802154_DEVKEY_ATTR_EXTENDED_ADDR] = { NLA_U64 },
1912*4882a593Smuzhiyun 	[NL802154_DEVKEY_ATTR_ID] = { NLA_NESTED },
1913*4882a593Smuzhiyun };
1914*4882a593Smuzhiyun 
nl802154_add_llsec_devkey(struct sk_buff * skb,struct genl_info * info)1915*4882a593Smuzhiyun static int nl802154_add_llsec_devkey(struct sk_buff *skb, struct genl_info *info)
1916*4882a593Smuzhiyun {
1917*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1918*4882a593Smuzhiyun 	struct net_device *dev = info->user_ptr[1];
1919*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1920*4882a593Smuzhiyun 	struct nlattr *attrs[NL802154_DEVKEY_ATTR_MAX + 1];
1921*4882a593Smuzhiyun 	struct ieee802154_llsec_device_key key;
1922*4882a593Smuzhiyun 	__le64 extended_addr;
1923*4882a593Smuzhiyun 
1924*4882a593Smuzhiyun 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
1925*4882a593Smuzhiyun 		return -EOPNOTSUPP;
1926*4882a593Smuzhiyun 
1927*4882a593Smuzhiyun 	if (!info->attrs[NL802154_ATTR_SEC_DEVKEY] ||
1928*4882a593Smuzhiyun 	    nla_parse_nested_deprecated(attrs, NL802154_DEVKEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_DEVKEY], nl802154_devkey_policy, info->extack) < 0)
1929*4882a593Smuzhiyun 		return -EINVAL;
1930*4882a593Smuzhiyun 
1931*4882a593Smuzhiyun 	if (!attrs[NL802154_DEVKEY_ATTR_FRAME_COUNTER] ||
1932*4882a593Smuzhiyun 	    !attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR])
1933*4882a593Smuzhiyun 		return -EINVAL;
1934*4882a593Smuzhiyun 
1935*4882a593Smuzhiyun 	/* TODO change key.id ? */
1936*4882a593Smuzhiyun 	if (ieee802154_llsec_parse_key_id(attrs[NL802154_DEVKEY_ATTR_ID],
1937*4882a593Smuzhiyun 					  &key.key_id) < 0)
1938*4882a593Smuzhiyun 		return -ENOBUFS;
1939*4882a593Smuzhiyun 
1940*4882a593Smuzhiyun 	/* TODO be32 */
1941*4882a593Smuzhiyun 	key.frame_counter = nla_get_u32(attrs[NL802154_DEVKEY_ATTR_FRAME_COUNTER]);
1942*4882a593Smuzhiyun 	/* TODO change naming hwaddr -> extended_addr
1943*4882a593Smuzhiyun 	 * check unique identifier short+pan OR extended_addr
1944*4882a593Smuzhiyun 	 */
1945*4882a593Smuzhiyun 	extended_addr = nla_get_le64(attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]);
1946*4882a593Smuzhiyun 	return rdev_add_devkey(rdev, wpan_dev, extended_addr, &key);
1947*4882a593Smuzhiyun }
1948*4882a593Smuzhiyun 
nl802154_del_llsec_devkey(struct sk_buff * skb,struct genl_info * info)1949*4882a593Smuzhiyun static int nl802154_del_llsec_devkey(struct sk_buff *skb, struct genl_info *info)
1950*4882a593Smuzhiyun {
1951*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
1952*4882a593Smuzhiyun 	struct net_device *dev = info->user_ptr[1];
1953*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
1954*4882a593Smuzhiyun 	struct nlattr *attrs[NL802154_DEVKEY_ATTR_MAX + 1];
1955*4882a593Smuzhiyun 	struct ieee802154_llsec_device_key key;
1956*4882a593Smuzhiyun 	__le64 extended_addr;
1957*4882a593Smuzhiyun 
1958*4882a593Smuzhiyun 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
1959*4882a593Smuzhiyun 		return -EOPNOTSUPP;
1960*4882a593Smuzhiyun 
1961*4882a593Smuzhiyun 	if (!info->attrs[NL802154_ATTR_SEC_DEVKEY] ||
1962*4882a593Smuzhiyun 	    nla_parse_nested_deprecated(attrs, NL802154_DEVKEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_DEVKEY], nl802154_devkey_policy, info->extack))
1963*4882a593Smuzhiyun 		return -EINVAL;
1964*4882a593Smuzhiyun 
1965*4882a593Smuzhiyun 	if (!attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR])
1966*4882a593Smuzhiyun 		return -EINVAL;
1967*4882a593Smuzhiyun 
1968*4882a593Smuzhiyun 	/* TODO change key.id ? */
1969*4882a593Smuzhiyun 	if (ieee802154_llsec_parse_key_id(attrs[NL802154_DEVKEY_ATTR_ID],
1970*4882a593Smuzhiyun 					  &key.key_id) < 0)
1971*4882a593Smuzhiyun 		return -ENOBUFS;
1972*4882a593Smuzhiyun 
1973*4882a593Smuzhiyun 	/* TODO change naming hwaddr -> extended_addr
1974*4882a593Smuzhiyun 	 * check unique identifier short+pan OR extended_addr
1975*4882a593Smuzhiyun 	 */
1976*4882a593Smuzhiyun 	extended_addr = nla_get_le64(attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]);
1977*4882a593Smuzhiyun 	return rdev_del_devkey(rdev, wpan_dev, extended_addr, &key);
1978*4882a593Smuzhiyun }
1979*4882a593Smuzhiyun 
nl802154_send_seclevel(struct sk_buff * msg,u32 cmd,u32 portid,u32 seq,int flags,struct cfg802154_registered_device * rdev,struct net_device * dev,const struct ieee802154_llsec_seclevel * sl)1980*4882a593Smuzhiyun static int nl802154_send_seclevel(struct sk_buff *msg, u32 cmd, u32 portid,
1981*4882a593Smuzhiyun 				  u32 seq, int flags,
1982*4882a593Smuzhiyun 				  struct cfg802154_registered_device *rdev,
1983*4882a593Smuzhiyun 				  struct net_device *dev,
1984*4882a593Smuzhiyun 				  const struct ieee802154_llsec_seclevel *sl)
1985*4882a593Smuzhiyun {
1986*4882a593Smuzhiyun 	void *hdr;
1987*4882a593Smuzhiyun 	struct nlattr *nl_seclevel;
1988*4882a593Smuzhiyun 
1989*4882a593Smuzhiyun 	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
1990*4882a593Smuzhiyun 	if (!hdr)
1991*4882a593Smuzhiyun 		return -ENOBUFS;
1992*4882a593Smuzhiyun 
1993*4882a593Smuzhiyun 	if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
1994*4882a593Smuzhiyun 		goto nla_put_failure;
1995*4882a593Smuzhiyun 
1996*4882a593Smuzhiyun 	nl_seclevel = nla_nest_start_noflag(msg, NL802154_ATTR_SEC_LEVEL);
1997*4882a593Smuzhiyun 	if (!nl_seclevel)
1998*4882a593Smuzhiyun 		goto nla_put_failure;
1999*4882a593Smuzhiyun 
2000*4882a593Smuzhiyun 	if (nla_put_u32(msg, NL802154_SECLEVEL_ATTR_FRAME, sl->frame_type) ||
2001*4882a593Smuzhiyun 	    nla_put_u32(msg, NL802154_SECLEVEL_ATTR_LEVELS, sl->sec_levels) ||
2002*4882a593Smuzhiyun 	    nla_put_u8(msg, NL802154_SECLEVEL_ATTR_DEV_OVERRIDE,
2003*4882a593Smuzhiyun 		       sl->device_override))
2004*4882a593Smuzhiyun 		goto nla_put_failure;
2005*4882a593Smuzhiyun 
2006*4882a593Smuzhiyun 	if (sl->frame_type == NL802154_FRAME_CMD) {
2007*4882a593Smuzhiyun 		if (nla_put_u32(msg, NL802154_SECLEVEL_ATTR_CMD_FRAME,
2008*4882a593Smuzhiyun 				sl->cmd_frame_id))
2009*4882a593Smuzhiyun 			goto nla_put_failure;
2010*4882a593Smuzhiyun 	}
2011*4882a593Smuzhiyun 
2012*4882a593Smuzhiyun 	nla_nest_end(msg, nl_seclevel);
2013*4882a593Smuzhiyun 	genlmsg_end(msg, hdr);
2014*4882a593Smuzhiyun 
2015*4882a593Smuzhiyun 	return 0;
2016*4882a593Smuzhiyun 
2017*4882a593Smuzhiyun nla_put_failure:
2018*4882a593Smuzhiyun 	genlmsg_cancel(msg, hdr);
2019*4882a593Smuzhiyun 	return -EMSGSIZE;
2020*4882a593Smuzhiyun }
2021*4882a593Smuzhiyun 
2022*4882a593Smuzhiyun static int
nl802154_dump_llsec_seclevel(struct sk_buff * skb,struct netlink_callback * cb)2023*4882a593Smuzhiyun nl802154_dump_llsec_seclevel(struct sk_buff *skb, struct netlink_callback *cb)
2024*4882a593Smuzhiyun {
2025*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = NULL;
2026*4882a593Smuzhiyun 	struct ieee802154_llsec_seclevel *sl;
2027*4882a593Smuzhiyun 	struct ieee802154_llsec_table *table;
2028*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev;
2029*4882a593Smuzhiyun 	int err;
2030*4882a593Smuzhiyun 
2031*4882a593Smuzhiyun 	err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
2032*4882a593Smuzhiyun 	if (err)
2033*4882a593Smuzhiyun 		return err;
2034*4882a593Smuzhiyun 
2035*4882a593Smuzhiyun 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) {
2036*4882a593Smuzhiyun 		err = skb->len;
2037*4882a593Smuzhiyun 		goto out_err;
2038*4882a593Smuzhiyun 	}
2039*4882a593Smuzhiyun 
2040*4882a593Smuzhiyun 	if (!wpan_dev->netdev) {
2041*4882a593Smuzhiyun 		err = -EINVAL;
2042*4882a593Smuzhiyun 		goto out_err;
2043*4882a593Smuzhiyun 	}
2044*4882a593Smuzhiyun 
2045*4882a593Smuzhiyun 	rdev_lock_llsec_table(rdev, wpan_dev);
2046*4882a593Smuzhiyun 	rdev_get_llsec_table(rdev, wpan_dev, &table);
2047*4882a593Smuzhiyun 
2048*4882a593Smuzhiyun 	/* TODO make it like station dump */
2049*4882a593Smuzhiyun 	if (cb->args[2])
2050*4882a593Smuzhiyun 		goto out;
2051*4882a593Smuzhiyun 
2052*4882a593Smuzhiyun 	list_for_each_entry(sl, &table->security_levels, list) {
2053*4882a593Smuzhiyun 		if (nl802154_send_seclevel(skb, NL802154_CMD_NEW_SEC_LEVEL,
2054*4882a593Smuzhiyun 					   NETLINK_CB(cb->skb).portid,
2055*4882a593Smuzhiyun 					   cb->nlh->nlmsg_seq, NLM_F_MULTI,
2056*4882a593Smuzhiyun 					   rdev, wpan_dev->netdev, sl) < 0) {
2057*4882a593Smuzhiyun 			/* TODO */
2058*4882a593Smuzhiyun 			err = -EIO;
2059*4882a593Smuzhiyun 			rdev_unlock_llsec_table(rdev, wpan_dev);
2060*4882a593Smuzhiyun 			goto out_err;
2061*4882a593Smuzhiyun 		}
2062*4882a593Smuzhiyun 	}
2063*4882a593Smuzhiyun 
2064*4882a593Smuzhiyun 	cb->args[2] = 1;
2065*4882a593Smuzhiyun 
2066*4882a593Smuzhiyun out:
2067*4882a593Smuzhiyun 	rdev_unlock_llsec_table(rdev, wpan_dev);
2068*4882a593Smuzhiyun 	err = skb->len;
2069*4882a593Smuzhiyun out_err:
2070*4882a593Smuzhiyun 	nl802154_finish_wpan_dev_dump(rdev);
2071*4882a593Smuzhiyun 
2072*4882a593Smuzhiyun 	return err;
2073*4882a593Smuzhiyun }
2074*4882a593Smuzhiyun 
2075*4882a593Smuzhiyun static const struct nla_policy nl802154_seclevel_policy[NL802154_SECLEVEL_ATTR_MAX + 1] = {
2076*4882a593Smuzhiyun 	[NL802154_SECLEVEL_ATTR_LEVELS] = { .type = NLA_U8 },
2077*4882a593Smuzhiyun 	[NL802154_SECLEVEL_ATTR_FRAME] = { .type = NLA_U32 },
2078*4882a593Smuzhiyun 	[NL802154_SECLEVEL_ATTR_CMD_FRAME] = { .type = NLA_U32 },
2079*4882a593Smuzhiyun 	[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE] = { .type = NLA_U8 },
2080*4882a593Smuzhiyun };
2081*4882a593Smuzhiyun 
2082*4882a593Smuzhiyun static int
llsec_parse_seclevel(struct nlattr * nla,struct ieee802154_llsec_seclevel * sl)2083*4882a593Smuzhiyun llsec_parse_seclevel(struct nlattr *nla, struct ieee802154_llsec_seclevel *sl)
2084*4882a593Smuzhiyun {
2085*4882a593Smuzhiyun 	struct nlattr *attrs[NL802154_SECLEVEL_ATTR_MAX + 1];
2086*4882a593Smuzhiyun 
2087*4882a593Smuzhiyun 	if (!nla || nla_parse_nested_deprecated(attrs, NL802154_SECLEVEL_ATTR_MAX, nla, nl802154_seclevel_policy, NULL))
2088*4882a593Smuzhiyun 		return -EINVAL;
2089*4882a593Smuzhiyun 
2090*4882a593Smuzhiyun 	memset(sl, 0, sizeof(*sl));
2091*4882a593Smuzhiyun 
2092*4882a593Smuzhiyun 	if (!attrs[NL802154_SECLEVEL_ATTR_LEVELS] ||
2093*4882a593Smuzhiyun 	    !attrs[NL802154_SECLEVEL_ATTR_FRAME] ||
2094*4882a593Smuzhiyun 	    !attrs[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE])
2095*4882a593Smuzhiyun 		return -EINVAL;
2096*4882a593Smuzhiyun 
2097*4882a593Smuzhiyun 	sl->sec_levels = nla_get_u8(attrs[NL802154_SECLEVEL_ATTR_LEVELS]);
2098*4882a593Smuzhiyun 	sl->frame_type = nla_get_u32(attrs[NL802154_SECLEVEL_ATTR_FRAME]);
2099*4882a593Smuzhiyun 	sl->device_override = nla_get_u8(attrs[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE]);
2100*4882a593Smuzhiyun 	if (sl->frame_type > NL802154_FRAME_MAX ||
2101*4882a593Smuzhiyun 	    (sl->device_override != 0 && sl->device_override != 1))
2102*4882a593Smuzhiyun 		return -EINVAL;
2103*4882a593Smuzhiyun 
2104*4882a593Smuzhiyun 	if (sl->frame_type == NL802154_FRAME_CMD) {
2105*4882a593Smuzhiyun 		if (!attrs[NL802154_SECLEVEL_ATTR_CMD_FRAME])
2106*4882a593Smuzhiyun 			return -EINVAL;
2107*4882a593Smuzhiyun 
2108*4882a593Smuzhiyun 		sl->cmd_frame_id = nla_get_u32(attrs[NL802154_SECLEVEL_ATTR_CMD_FRAME]);
2109*4882a593Smuzhiyun 		if (sl->cmd_frame_id > NL802154_CMD_FRAME_MAX)
2110*4882a593Smuzhiyun 			return -EINVAL;
2111*4882a593Smuzhiyun 	}
2112*4882a593Smuzhiyun 
2113*4882a593Smuzhiyun 	return 0;
2114*4882a593Smuzhiyun }
2115*4882a593Smuzhiyun 
nl802154_add_llsec_seclevel(struct sk_buff * skb,struct genl_info * info)2116*4882a593Smuzhiyun static int nl802154_add_llsec_seclevel(struct sk_buff *skb,
2117*4882a593Smuzhiyun 				       struct genl_info *info)
2118*4882a593Smuzhiyun {
2119*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
2120*4882a593Smuzhiyun 	struct net_device *dev = info->user_ptr[1];
2121*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
2122*4882a593Smuzhiyun 	struct ieee802154_llsec_seclevel sl;
2123*4882a593Smuzhiyun 
2124*4882a593Smuzhiyun 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
2125*4882a593Smuzhiyun 		return -EOPNOTSUPP;
2126*4882a593Smuzhiyun 
2127*4882a593Smuzhiyun 	if (llsec_parse_seclevel(info->attrs[NL802154_ATTR_SEC_LEVEL],
2128*4882a593Smuzhiyun 				 &sl) < 0)
2129*4882a593Smuzhiyun 		return -EINVAL;
2130*4882a593Smuzhiyun 
2131*4882a593Smuzhiyun 	return rdev_add_seclevel(rdev, wpan_dev, &sl);
2132*4882a593Smuzhiyun }
2133*4882a593Smuzhiyun 
nl802154_del_llsec_seclevel(struct sk_buff * skb,struct genl_info * info)2134*4882a593Smuzhiyun static int nl802154_del_llsec_seclevel(struct sk_buff *skb,
2135*4882a593Smuzhiyun 				       struct genl_info *info)
2136*4882a593Smuzhiyun {
2137*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev = info->user_ptr[0];
2138*4882a593Smuzhiyun 	struct net_device *dev = info->user_ptr[1];
2139*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
2140*4882a593Smuzhiyun 	struct ieee802154_llsec_seclevel sl;
2141*4882a593Smuzhiyun 
2142*4882a593Smuzhiyun 	if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR)
2143*4882a593Smuzhiyun 		return -EOPNOTSUPP;
2144*4882a593Smuzhiyun 
2145*4882a593Smuzhiyun 	if (!info->attrs[NL802154_ATTR_SEC_LEVEL] ||
2146*4882a593Smuzhiyun 	    llsec_parse_seclevel(info->attrs[NL802154_ATTR_SEC_LEVEL],
2147*4882a593Smuzhiyun 				 &sl) < 0)
2148*4882a593Smuzhiyun 		return -EINVAL;
2149*4882a593Smuzhiyun 
2150*4882a593Smuzhiyun 	return rdev_del_seclevel(rdev, wpan_dev, &sl);
2151*4882a593Smuzhiyun }
2152*4882a593Smuzhiyun #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
2153*4882a593Smuzhiyun 
2154*4882a593Smuzhiyun #define NL802154_FLAG_NEED_WPAN_PHY	0x01
2155*4882a593Smuzhiyun #define NL802154_FLAG_NEED_NETDEV	0x02
2156*4882a593Smuzhiyun #define NL802154_FLAG_NEED_RTNL		0x04
2157*4882a593Smuzhiyun #define NL802154_FLAG_CHECK_NETDEV_UP	0x08
2158*4882a593Smuzhiyun #define NL802154_FLAG_NEED_NETDEV_UP	(NL802154_FLAG_NEED_NETDEV |\
2159*4882a593Smuzhiyun 					 NL802154_FLAG_CHECK_NETDEV_UP)
2160*4882a593Smuzhiyun #define NL802154_FLAG_NEED_WPAN_DEV	0x10
2161*4882a593Smuzhiyun #define NL802154_FLAG_NEED_WPAN_DEV_UP	(NL802154_FLAG_NEED_WPAN_DEV |\
2162*4882a593Smuzhiyun 					 NL802154_FLAG_CHECK_NETDEV_UP)
2163*4882a593Smuzhiyun 
nl802154_pre_doit(const struct genl_ops * ops,struct sk_buff * skb,struct genl_info * info)2164*4882a593Smuzhiyun static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
2165*4882a593Smuzhiyun 			     struct genl_info *info)
2166*4882a593Smuzhiyun {
2167*4882a593Smuzhiyun 	struct cfg802154_registered_device *rdev;
2168*4882a593Smuzhiyun 	struct wpan_dev *wpan_dev;
2169*4882a593Smuzhiyun 	struct net_device *dev;
2170*4882a593Smuzhiyun 	bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
2171*4882a593Smuzhiyun 
2172*4882a593Smuzhiyun 	if (rtnl)
2173*4882a593Smuzhiyun 		rtnl_lock();
2174*4882a593Smuzhiyun 
2175*4882a593Smuzhiyun 	if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
2176*4882a593Smuzhiyun 		rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
2177*4882a593Smuzhiyun 		if (IS_ERR(rdev)) {
2178*4882a593Smuzhiyun 			if (rtnl)
2179*4882a593Smuzhiyun 				rtnl_unlock();
2180*4882a593Smuzhiyun 			return PTR_ERR(rdev);
2181*4882a593Smuzhiyun 		}
2182*4882a593Smuzhiyun 		info->user_ptr[0] = rdev;
2183*4882a593Smuzhiyun 	} else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
2184*4882a593Smuzhiyun 		   ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
2185*4882a593Smuzhiyun 		ASSERT_RTNL();
2186*4882a593Smuzhiyun 		wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
2187*4882a593Smuzhiyun 							   info->attrs);
2188*4882a593Smuzhiyun 		if (IS_ERR(wpan_dev)) {
2189*4882a593Smuzhiyun 			if (rtnl)
2190*4882a593Smuzhiyun 				rtnl_unlock();
2191*4882a593Smuzhiyun 			return PTR_ERR(wpan_dev);
2192*4882a593Smuzhiyun 		}
2193*4882a593Smuzhiyun 
2194*4882a593Smuzhiyun 		dev = wpan_dev->netdev;
2195*4882a593Smuzhiyun 		rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
2196*4882a593Smuzhiyun 
2197*4882a593Smuzhiyun 		if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
2198*4882a593Smuzhiyun 			if (!dev) {
2199*4882a593Smuzhiyun 				if (rtnl)
2200*4882a593Smuzhiyun 					rtnl_unlock();
2201*4882a593Smuzhiyun 				return -EINVAL;
2202*4882a593Smuzhiyun 			}
2203*4882a593Smuzhiyun 
2204*4882a593Smuzhiyun 			info->user_ptr[1] = dev;
2205*4882a593Smuzhiyun 		} else {
2206*4882a593Smuzhiyun 			info->user_ptr[1] = wpan_dev;
2207*4882a593Smuzhiyun 		}
2208*4882a593Smuzhiyun 
2209*4882a593Smuzhiyun 		if (dev) {
2210*4882a593Smuzhiyun 			if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
2211*4882a593Smuzhiyun 			    !netif_running(dev)) {
2212*4882a593Smuzhiyun 				if (rtnl)
2213*4882a593Smuzhiyun 					rtnl_unlock();
2214*4882a593Smuzhiyun 				return -ENETDOWN;
2215*4882a593Smuzhiyun 			}
2216*4882a593Smuzhiyun 
2217*4882a593Smuzhiyun 			dev_hold(dev);
2218*4882a593Smuzhiyun 		}
2219*4882a593Smuzhiyun 
2220*4882a593Smuzhiyun 		info->user_ptr[0] = rdev;
2221*4882a593Smuzhiyun 	}
2222*4882a593Smuzhiyun 
2223*4882a593Smuzhiyun 	return 0;
2224*4882a593Smuzhiyun }
2225*4882a593Smuzhiyun 
nl802154_post_doit(const struct genl_ops * ops,struct sk_buff * skb,struct genl_info * info)2226*4882a593Smuzhiyun static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
2227*4882a593Smuzhiyun 			       struct genl_info *info)
2228*4882a593Smuzhiyun {
2229*4882a593Smuzhiyun 	if (info->user_ptr[1]) {
2230*4882a593Smuzhiyun 		if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
2231*4882a593Smuzhiyun 			struct wpan_dev *wpan_dev = info->user_ptr[1];
2232*4882a593Smuzhiyun 
2233*4882a593Smuzhiyun 			if (wpan_dev->netdev)
2234*4882a593Smuzhiyun 				dev_put(wpan_dev->netdev);
2235*4882a593Smuzhiyun 		} else {
2236*4882a593Smuzhiyun 			dev_put(info->user_ptr[1]);
2237*4882a593Smuzhiyun 		}
2238*4882a593Smuzhiyun 	}
2239*4882a593Smuzhiyun 
2240*4882a593Smuzhiyun 	if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
2241*4882a593Smuzhiyun 		rtnl_unlock();
2242*4882a593Smuzhiyun }
2243*4882a593Smuzhiyun 
2244*4882a593Smuzhiyun static const struct genl_ops nl802154_ops[] = {
2245*4882a593Smuzhiyun 	{
2246*4882a593Smuzhiyun 		.cmd = NL802154_CMD_GET_WPAN_PHY,
2247*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT |
2248*4882a593Smuzhiyun 			    GENL_DONT_VALIDATE_DUMP_STRICT,
2249*4882a593Smuzhiyun 		.doit = nl802154_get_wpan_phy,
2250*4882a593Smuzhiyun 		.dumpit = nl802154_dump_wpan_phy,
2251*4882a593Smuzhiyun 		.done = nl802154_dump_wpan_phy_done,
2252*4882a593Smuzhiyun 		/* can be retrieved by unprivileged users */
2253*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2254*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2255*4882a593Smuzhiyun 	},
2256*4882a593Smuzhiyun 	{
2257*4882a593Smuzhiyun 		.cmd = NL802154_CMD_GET_INTERFACE,
2258*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2259*4882a593Smuzhiyun 		.doit = nl802154_get_interface,
2260*4882a593Smuzhiyun 		.dumpit = nl802154_dump_interface,
2261*4882a593Smuzhiyun 		/* can be retrieved by unprivileged users */
2262*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
2263*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2264*4882a593Smuzhiyun 	},
2265*4882a593Smuzhiyun 	{
2266*4882a593Smuzhiyun 		.cmd = NL802154_CMD_NEW_INTERFACE,
2267*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2268*4882a593Smuzhiyun 		.doit = nl802154_new_interface,
2269*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2270*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2271*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2272*4882a593Smuzhiyun 	},
2273*4882a593Smuzhiyun 	{
2274*4882a593Smuzhiyun 		.cmd = NL802154_CMD_DEL_INTERFACE,
2275*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2276*4882a593Smuzhiyun 		.doit = nl802154_del_interface,
2277*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2278*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
2279*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2280*4882a593Smuzhiyun 	},
2281*4882a593Smuzhiyun 	{
2282*4882a593Smuzhiyun 		.cmd = NL802154_CMD_SET_CHANNEL,
2283*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2284*4882a593Smuzhiyun 		.doit = nl802154_set_channel,
2285*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2286*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2287*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2288*4882a593Smuzhiyun 	},
2289*4882a593Smuzhiyun 	{
2290*4882a593Smuzhiyun 		.cmd = NL802154_CMD_SET_CCA_MODE,
2291*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2292*4882a593Smuzhiyun 		.doit = nl802154_set_cca_mode,
2293*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2294*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2295*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2296*4882a593Smuzhiyun 	},
2297*4882a593Smuzhiyun 	{
2298*4882a593Smuzhiyun 		.cmd = NL802154_CMD_SET_CCA_ED_LEVEL,
2299*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2300*4882a593Smuzhiyun 		.doit = nl802154_set_cca_ed_level,
2301*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2302*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2303*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2304*4882a593Smuzhiyun 	},
2305*4882a593Smuzhiyun 	{
2306*4882a593Smuzhiyun 		.cmd = NL802154_CMD_SET_TX_POWER,
2307*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2308*4882a593Smuzhiyun 		.doit = nl802154_set_tx_power,
2309*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2310*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2311*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2312*4882a593Smuzhiyun 	},
2313*4882a593Smuzhiyun 	{
2314*4882a593Smuzhiyun 		.cmd = NL802154_CMD_SET_WPAN_PHY_NETNS,
2315*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2316*4882a593Smuzhiyun 		.doit = nl802154_wpan_phy_netns,
2317*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2318*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
2319*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2320*4882a593Smuzhiyun 	},
2321*4882a593Smuzhiyun 	{
2322*4882a593Smuzhiyun 		.cmd = NL802154_CMD_SET_PAN_ID,
2323*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2324*4882a593Smuzhiyun 		.doit = nl802154_set_pan_id,
2325*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2326*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2327*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2328*4882a593Smuzhiyun 	},
2329*4882a593Smuzhiyun 	{
2330*4882a593Smuzhiyun 		.cmd = NL802154_CMD_SET_SHORT_ADDR,
2331*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2332*4882a593Smuzhiyun 		.doit = nl802154_set_short_addr,
2333*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2334*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2335*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2336*4882a593Smuzhiyun 	},
2337*4882a593Smuzhiyun 	{
2338*4882a593Smuzhiyun 		.cmd = NL802154_CMD_SET_BACKOFF_EXPONENT,
2339*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2340*4882a593Smuzhiyun 		.doit = nl802154_set_backoff_exponent,
2341*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2342*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2343*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2344*4882a593Smuzhiyun 	},
2345*4882a593Smuzhiyun 	{
2346*4882a593Smuzhiyun 		.cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS,
2347*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2348*4882a593Smuzhiyun 		.doit = nl802154_set_max_csma_backoffs,
2349*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2350*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2351*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2352*4882a593Smuzhiyun 	},
2353*4882a593Smuzhiyun 	{
2354*4882a593Smuzhiyun 		.cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES,
2355*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2356*4882a593Smuzhiyun 		.doit = nl802154_set_max_frame_retries,
2357*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2358*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2359*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2360*4882a593Smuzhiyun 	},
2361*4882a593Smuzhiyun 	{
2362*4882a593Smuzhiyun 		.cmd = NL802154_CMD_SET_LBT_MODE,
2363*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2364*4882a593Smuzhiyun 		.doit = nl802154_set_lbt_mode,
2365*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2366*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2367*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2368*4882a593Smuzhiyun 	},
2369*4882a593Smuzhiyun 	{
2370*4882a593Smuzhiyun 		.cmd = NL802154_CMD_SET_ACKREQ_DEFAULT,
2371*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2372*4882a593Smuzhiyun 		.doit = nl802154_set_ackreq_default,
2373*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2374*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2375*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2376*4882a593Smuzhiyun 	},
2377*4882a593Smuzhiyun #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
2378*4882a593Smuzhiyun 	{
2379*4882a593Smuzhiyun 		.cmd = NL802154_CMD_SET_SEC_PARAMS,
2380*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2381*4882a593Smuzhiyun 		.doit = nl802154_set_llsec_params,
2382*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2383*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2384*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2385*4882a593Smuzhiyun 	},
2386*4882a593Smuzhiyun 	{
2387*4882a593Smuzhiyun 		.cmd = NL802154_CMD_GET_SEC_KEY,
2388*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT |
2389*4882a593Smuzhiyun 			    GENL_DONT_VALIDATE_DUMP_STRICT,
2390*4882a593Smuzhiyun 		/* TODO .doit by matching key id? */
2391*4882a593Smuzhiyun 		.dumpit = nl802154_dump_llsec_key,
2392*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2393*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2394*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2395*4882a593Smuzhiyun 	},
2396*4882a593Smuzhiyun 	{
2397*4882a593Smuzhiyun 		.cmd = NL802154_CMD_NEW_SEC_KEY,
2398*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2399*4882a593Smuzhiyun 		.doit = nl802154_add_llsec_key,
2400*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2401*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2402*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2403*4882a593Smuzhiyun 	},
2404*4882a593Smuzhiyun 	{
2405*4882a593Smuzhiyun 		.cmd = NL802154_CMD_DEL_SEC_KEY,
2406*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2407*4882a593Smuzhiyun 		.doit = nl802154_del_llsec_key,
2408*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2409*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2410*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2411*4882a593Smuzhiyun 	},
2412*4882a593Smuzhiyun 	/* TODO unique identifier must short+pan OR extended_addr */
2413*4882a593Smuzhiyun 	{
2414*4882a593Smuzhiyun 		.cmd = NL802154_CMD_GET_SEC_DEV,
2415*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT |
2416*4882a593Smuzhiyun 			    GENL_DONT_VALIDATE_DUMP_STRICT,
2417*4882a593Smuzhiyun 		/* TODO .doit by matching extended_addr? */
2418*4882a593Smuzhiyun 		.dumpit = nl802154_dump_llsec_dev,
2419*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2420*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2421*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2422*4882a593Smuzhiyun 	},
2423*4882a593Smuzhiyun 	{
2424*4882a593Smuzhiyun 		.cmd = NL802154_CMD_NEW_SEC_DEV,
2425*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2426*4882a593Smuzhiyun 		.doit = nl802154_add_llsec_dev,
2427*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2428*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2429*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2430*4882a593Smuzhiyun 	},
2431*4882a593Smuzhiyun 	{
2432*4882a593Smuzhiyun 		.cmd = NL802154_CMD_DEL_SEC_DEV,
2433*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2434*4882a593Smuzhiyun 		.doit = nl802154_del_llsec_dev,
2435*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2436*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2437*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2438*4882a593Smuzhiyun 	},
2439*4882a593Smuzhiyun 	/* TODO remove complete devkey, put it as nested? */
2440*4882a593Smuzhiyun 	{
2441*4882a593Smuzhiyun 		.cmd = NL802154_CMD_GET_SEC_DEVKEY,
2442*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT |
2443*4882a593Smuzhiyun 			    GENL_DONT_VALIDATE_DUMP_STRICT,
2444*4882a593Smuzhiyun 		/* TODO doit by matching ??? */
2445*4882a593Smuzhiyun 		.dumpit = nl802154_dump_llsec_devkey,
2446*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2447*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2448*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2449*4882a593Smuzhiyun 	},
2450*4882a593Smuzhiyun 	{
2451*4882a593Smuzhiyun 		.cmd = NL802154_CMD_NEW_SEC_DEVKEY,
2452*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2453*4882a593Smuzhiyun 		.doit = nl802154_add_llsec_devkey,
2454*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2455*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2456*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2457*4882a593Smuzhiyun 	},
2458*4882a593Smuzhiyun 	{
2459*4882a593Smuzhiyun 		.cmd = NL802154_CMD_DEL_SEC_DEVKEY,
2460*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2461*4882a593Smuzhiyun 		.doit = nl802154_del_llsec_devkey,
2462*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2463*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2464*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2465*4882a593Smuzhiyun 	},
2466*4882a593Smuzhiyun 	{
2467*4882a593Smuzhiyun 		.cmd = NL802154_CMD_GET_SEC_LEVEL,
2468*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT |
2469*4882a593Smuzhiyun 			    GENL_DONT_VALIDATE_DUMP_STRICT,
2470*4882a593Smuzhiyun 		/* TODO .doit by matching frame_type? */
2471*4882a593Smuzhiyun 		.dumpit = nl802154_dump_llsec_seclevel,
2472*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2473*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2474*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2475*4882a593Smuzhiyun 	},
2476*4882a593Smuzhiyun 	{
2477*4882a593Smuzhiyun 		.cmd = NL802154_CMD_NEW_SEC_LEVEL,
2478*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2479*4882a593Smuzhiyun 		.doit = nl802154_add_llsec_seclevel,
2480*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2481*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2482*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2483*4882a593Smuzhiyun 	},
2484*4882a593Smuzhiyun 	{
2485*4882a593Smuzhiyun 		.cmd = NL802154_CMD_DEL_SEC_LEVEL,
2486*4882a593Smuzhiyun 		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
2487*4882a593Smuzhiyun 		/* TODO match frame_type only? */
2488*4882a593Smuzhiyun 		.doit = nl802154_del_llsec_seclevel,
2489*4882a593Smuzhiyun 		.flags = GENL_ADMIN_PERM,
2490*4882a593Smuzhiyun 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
2491*4882a593Smuzhiyun 				  NL802154_FLAG_NEED_RTNL,
2492*4882a593Smuzhiyun 	},
2493*4882a593Smuzhiyun #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
2494*4882a593Smuzhiyun };
2495*4882a593Smuzhiyun 
2496*4882a593Smuzhiyun static struct genl_family nl802154_fam __ro_after_init = {
2497*4882a593Smuzhiyun 	.name = NL802154_GENL_NAME,	/* have users key off the name instead */
2498*4882a593Smuzhiyun 	.hdrsize = 0,			/* no private header */
2499*4882a593Smuzhiyun 	.version = 1,			/* no particular meaning now */
2500*4882a593Smuzhiyun 	.maxattr = NL802154_ATTR_MAX,
2501*4882a593Smuzhiyun 	.policy = nl802154_policy,
2502*4882a593Smuzhiyun 	.netnsok = true,
2503*4882a593Smuzhiyun 	.pre_doit = nl802154_pre_doit,
2504*4882a593Smuzhiyun 	.post_doit = nl802154_post_doit,
2505*4882a593Smuzhiyun 	.module = THIS_MODULE,
2506*4882a593Smuzhiyun 	.ops = nl802154_ops,
2507*4882a593Smuzhiyun 	.n_ops = ARRAY_SIZE(nl802154_ops),
2508*4882a593Smuzhiyun 	.mcgrps = nl802154_mcgrps,
2509*4882a593Smuzhiyun 	.n_mcgrps = ARRAY_SIZE(nl802154_mcgrps),
2510*4882a593Smuzhiyun };
2511*4882a593Smuzhiyun 
2512*4882a593Smuzhiyun /* initialisation/exit functions */
nl802154_init(void)2513*4882a593Smuzhiyun int __init nl802154_init(void)
2514*4882a593Smuzhiyun {
2515*4882a593Smuzhiyun 	return genl_register_family(&nl802154_fam);
2516*4882a593Smuzhiyun }
2517*4882a593Smuzhiyun 
nl802154_exit(void)2518*4882a593Smuzhiyun void nl802154_exit(void)
2519*4882a593Smuzhiyun {
2520*4882a593Smuzhiyun 	genl_unregister_family(&nl802154_fam);
2521*4882a593Smuzhiyun }
2522