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, ¶ms);
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, ¶ms.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 ¶ms.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, ¶ms, 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