1 /* This program is free software; you can redistribute it and/or modify
2 * it under the terms of the GNU General Public License version 2
3 * as published by the Free Software Foundation.
5 * This program is distributed in the hope that it will be useful,
6 * but WITHOUT ANY WARRANTY; without even the implied warranty of
7 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8 * GNU General Public License for more details.
11 * Alexander Aring <aar@pengutronix.de>
13 * Based on: net/wireless/nl80211.c
16 #include <linux/rtnetlink.h>
18 #include <net/cfg802154.h>
19 #include <net/genetlink.h>
20 #include <net/mac802154.h>
21 #include <net/netlink.h>
22 #include <net/nl802154.h>
28 static int nl802154_pre_doit(const struct genl_ops
*ops
, struct sk_buff
*skb
,
29 struct genl_info
*info
);
31 static void nl802154_post_doit(const struct genl_ops
*ops
, struct sk_buff
*skb
,
32 struct genl_info
*info
);
34 /* the netlink family */
35 static struct genl_family nl802154_fam
= {
36 .id
= GENL_ID_GENERATE
, /* don't bother with a hardcoded ID */
37 .name
= NL802154_GENL_NAME
, /* have users key off the name instead */
38 .hdrsize
= 0, /* no private header */
39 .version
= 1, /* no particular meaning now */
40 .maxattr
= NL802154_ATTR_MAX
,
42 .pre_doit
= nl802154_pre_doit
,
43 .post_doit
= nl802154_post_doit
,
46 /* multicast groups */
47 enum nl802154_multicast_groups
{
48 NL802154_MCGRP_CONFIG
,
51 static const struct genl_multicast_group nl802154_mcgrps
[] = {
52 [NL802154_MCGRP_CONFIG
] = { .name
= "config", },
55 /* returns ERR_PTR values */
56 static struct wpan_dev
*
57 __cfg802154_wpan_dev_from_attrs(struct net
*netns
, struct nlattr
**attrs
)
59 struct cfg802154_registered_device
*rdev
;
60 struct wpan_dev
*result
= NULL
;
61 bool have_ifidx
= attrs
[NL802154_ATTR_IFINDEX
];
62 bool have_wpan_dev_id
= attrs
[NL802154_ATTR_WPAN_DEV
];
64 int wpan_phy_idx
= -1;
69 if (!have_ifidx
&& !have_wpan_dev_id
)
70 return ERR_PTR(-EINVAL
);
73 ifidx
= nla_get_u32(attrs
[NL802154_ATTR_IFINDEX
]);
74 if (have_wpan_dev_id
) {
75 wpan_dev_id
= nla_get_u64(attrs
[NL802154_ATTR_WPAN_DEV
]);
76 wpan_phy_idx
= wpan_dev_id
>> 32;
79 list_for_each_entry(rdev
, &cfg802154_rdev_list
, list
) {
80 struct wpan_dev
*wpan_dev
;
82 /* TODO netns compare */
84 if (have_wpan_dev_id
&& rdev
->wpan_phy_idx
!= wpan_phy_idx
)
87 list_for_each_entry(wpan_dev
, &rdev
->wpan_dev_list
, list
) {
88 if (have_ifidx
&& wpan_dev
->netdev
&&
89 wpan_dev
->netdev
->ifindex
== ifidx
) {
93 if (have_wpan_dev_id
&&
94 wpan_dev
->identifier
== (u32
)wpan_dev_id
) {
107 return ERR_PTR(-ENODEV
);
110 static struct cfg802154_registered_device
*
111 __cfg802154_rdev_from_attrs(struct net
*netns
, struct nlattr
**attrs
)
113 struct cfg802154_registered_device
*rdev
= NULL
, *tmp
;
114 struct net_device
*netdev
;
118 if (!attrs
[NL802154_ATTR_WPAN_PHY
] &&
119 !attrs
[NL802154_ATTR_IFINDEX
] &&
120 !attrs
[NL802154_ATTR_WPAN_DEV
])
121 return ERR_PTR(-EINVAL
);
123 if (attrs
[NL802154_ATTR_WPAN_PHY
])
124 rdev
= cfg802154_rdev_by_wpan_phy_idx(
125 nla_get_u32(attrs
[NL802154_ATTR_WPAN_PHY
]));
127 if (attrs
[NL802154_ATTR_WPAN_DEV
]) {
128 u64 wpan_dev_id
= nla_get_u64(attrs
[NL802154_ATTR_WPAN_DEV
]);
129 struct wpan_dev
*wpan_dev
;
132 tmp
= cfg802154_rdev_by_wpan_phy_idx(wpan_dev_id
>> 32);
134 /* make sure wpan_dev exists */
135 list_for_each_entry(wpan_dev
, &tmp
->wpan_dev_list
, list
) {
136 if (wpan_dev
->identifier
!= (u32
)wpan_dev_id
)
145 if (rdev
&& tmp
!= rdev
)
146 return ERR_PTR(-EINVAL
);
151 if (attrs
[NL802154_ATTR_IFINDEX
]) {
152 int ifindex
= nla_get_u32(attrs
[NL802154_ATTR_IFINDEX
]);
154 netdev
= __dev_get_by_index(netns
, ifindex
);
156 if (netdev
->ieee802154_ptr
)
157 tmp
= wpan_phy_to_rdev(
158 netdev
->ieee802154_ptr
->wpan_phy
);
162 /* not wireless device -- return error */
164 return ERR_PTR(-EINVAL
);
166 /* mismatch -- return error */
167 if (rdev
&& tmp
!= rdev
)
168 return ERR_PTR(-EINVAL
);
175 return ERR_PTR(-ENODEV
);
177 /* TODO netns compare */
182 /* This function returns a pointer to the driver
183 * that the genl_info item that is passed refers to.
185 * The result of this can be a PTR_ERR and hence must
186 * be checked with IS_ERR() for errors.
188 static struct cfg802154_registered_device
*
189 cfg802154_get_dev_from_info(struct net
*netns
, struct genl_info
*info
)
191 return __cfg802154_rdev_from_attrs(netns
, info
->attrs
);
194 /* policy for the attributes */
195 static const struct nla_policy nl802154_policy
[NL802154_ATTR_MAX
+1] = {
196 [NL802154_ATTR_WPAN_PHY
] = { .type
= NLA_U32
},
197 [NL802154_ATTR_WPAN_PHY_NAME
] = { .type
= NLA_NUL_STRING
,
200 [NL802154_ATTR_IFINDEX
] = { .type
= NLA_U32
},
202 [NL802154_ATTR_WPAN_DEV
] = { .type
= NLA_U64
},
204 [NL802154_ATTR_PAGE
] = { .type
= NLA_U8
, },
205 [NL802154_ATTR_CHANNEL
] = { .type
= NLA_U8
, },
207 [NL802154_ATTR_TX_POWER
] = { .type
= NLA_S8
, },
209 [NL802154_ATTR_CCA_MODE
] = { .type
= NLA_U8
, },
211 [NL802154_ATTR_SUPPORTED_CHANNEL
] = { .type
= NLA_U32
, },
214 /* message building helper */
215 static inline void *nl802154hdr_put(struct sk_buff
*skb
, u32 portid
, u32 seq
,
218 /* since there is no private header just add the generic one */
219 return genlmsg_put(skb
, portid
, seq
, &nl802154_fam
, flags
, cmd
);
223 nl802154_send_wpan_phy_channels(struct cfg802154_registered_device
*rdev
,
226 struct nlattr
*nl_page
;
229 nl_page
= nla_nest_start(msg
, NL802154_ATTR_CHANNELS_SUPPORTED
);
233 for (page
= 0; page
< WPAN_NUM_PAGES
; page
++) {
234 if (nla_put_u32(msg
, NL802154_ATTR_SUPPORTED_CHANNEL
,
235 rdev
->wpan_phy
.channels_supported
[page
]))
238 nla_nest_end(msg
, nl_page
);
243 static int nl802154_send_wpan_phy(struct cfg802154_registered_device
*rdev
,
244 enum nl802154_commands cmd
,
245 struct sk_buff
*msg
, u32 portid
, u32 seq
,
250 hdr
= nl802154hdr_put(msg
, portid
, seq
, flags
, cmd
);
254 if (nla_put_u32(msg
, NL802154_ATTR_WPAN_PHY
, rdev
->wpan_phy_idx
) ||
255 nla_put_string(msg
, NL802154_ATTR_WPAN_PHY_NAME
,
256 wpan_phy_name(&rdev
->wpan_phy
)) ||
257 nla_put_u32(msg
, NL802154_ATTR_GENERATION
,
258 cfg802154_rdev_list_generation
))
259 goto nla_put_failure
;
261 if (cmd
!= NL802154_CMD_NEW_WPAN_PHY
)
266 /* current channel settings */
267 if (nla_put_u8(msg
, NL802154_ATTR_PAGE
,
268 rdev
->wpan_phy
.current_page
) ||
269 nla_put_u8(msg
, NL802154_ATTR_CHANNEL
,
270 rdev
->wpan_phy
.current_channel
))
271 goto nla_put_failure
;
273 /* supported channels array */
274 if (nl802154_send_wpan_phy_channels(rdev
, msg
))
275 goto nla_put_failure
;
278 if (nla_put_u8(msg
, NL802154_ATTR_CCA_MODE
,
279 rdev
->wpan_phy
.cca_mode
))
280 goto nla_put_failure
;
282 if (nla_put_s8(msg
, NL802154_ATTR_TX_POWER
,
283 rdev
->wpan_phy
.transmit_power
))
284 goto nla_put_failure
;
287 return genlmsg_end(msg
, hdr
);
290 genlmsg_cancel(msg
, hdr
);
294 struct nl802154_dump_wpan_phy_state
{
300 static int nl802154_dump_wpan_phy_parse(struct sk_buff
*skb
,
301 struct netlink_callback
*cb
,
302 struct nl802154_dump_wpan_phy_state
*state
)
304 struct nlattr
**tb
= nl802154_fam
.attrbuf
;
305 int ret
= nlmsg_parse(cb
->nlh
, GENL_HDRLEN
+ nl802154_fam
.hdrsize
,
306 tb
, nl802154_fam
.maxattr
, nl802154_policy
);
308 /* TODO check if we can handle error here,
309 * we have no backward compatibility
314 if (tb
[NL802154_ATTR_WPAN_PHY
])
315 state
->filter_wpan_phy
= nla_get_u32(tb
[NL802154_ATTR_WPAN_PHY
]);
316 if (tb
[NL802154_ATTR_WPAN_DEV
])
317 state
->filter_wpan_phy
= nla_get_u64(tb
[NL802154_ATTR_WPAN_DEV
]) >> 32;
318 if (tb
[NL802154_ATTR_IFINDEX
]) {
319 struct net_device
*netdev
;
320 struct cfg802154_registered_device
*rdev
;
321 int ifidx
= nla_get_u32(tb
[NL802154_ATTR_IFINDEX
]);
324 netdev
= __dev_get_by_index(&init_net
, ifidx
);
327 if (netdev
->ieee802154_ptr
) {
328 rdev
= wpan_phy_to_rdev(
329 netdev
->ieee802154_ptr
->wpan_phy
);
330 state
->filter_wpan_phy
= rdev
->wpan_phy_idx
;
338 nl802154_dump_wpan_phy(struct sk_buff
*skb
, struct netlink_callback
*cb
)
341 struct nl802154_dump_wpan_phy_state
*state
= (void *)cb
->args
[0];
342 struct cfg802154_registered_device
*rdev
;
346 state
= kzalloc(sizeof(*state
), GFP_KERNEL
);
351 state
->filter_wpan_phy
= -1;
352 ret
= nl802154_dump_wpan_phy_parse(skb
, cb
, state
);
358 cb
->args
[0] = (long)state
;
361 list_for_each_entry(rdev
, &cfg802154_rdev_list
, list
) {
362 /* TODO net ns compare */
363 if (++idx
<= state
->start
)
365 if (state
->filter_wpan_phy
!= -1 &&
366 state
->filter_wpan_phy
!= rdev
->wpan_phy_idx
)
368 /* attempt to fit multiple wpan_phy data chunks into the skb */
369 ret
= nl802154_send_wpan_phy(rdev
,
370 NL802154_CMD_NEW_WPAN_PHY
,
372 NETLINK_CB(cb
->skb
).portid
,
373 cb
->nlh
->nlmsg_seq
, NLM_F_MULTI
);
375 if ((ret
== -ENOBUFS
|| ret
== -EMSGSIZE
) &&
376 !skb
->len
&& cb
->min_dump_alloc
< 4096) {
377 cb
->min_dump_alloc
= 4096;
393 static int nl802154_dump_wpan_phy_done(struct netlink_callback
*cb
)
395 kfree((void *)cb
->args
[0]);
399 static int nl802154_get_wpan_phy(struct sk_buff
*skb
, struct genl_info
*info
)
402 struct cfg802154_registered_device
*rdev
= info
->user_ptr
[0];
404 msg
= nlmsg_new(NLMSG_DEFAULT_SIZE
, GFP_KERNEL
);
408 if (nl802154_send_wpan_phy(rdev
, NL802154_CMD_NEW_WPAN_PHY
, msg
,
409 info
->snd_portid
, info
->snd_seq
, 0) < 0) {
414 return genlmsg_reply(msg
, info
);
417 #define NL802154_FLAG_NEED_WPAN_PHY 0x01
418 #define NL802154_FLAG_NEED_NETDEV 0x02
419 #define NL802154_FLAG_NEED_RTNL 0x04
420 #define NL802154_FLAG_CHECK_NETDEV_UP 0x08
421 #define NL802154_FLAG_NEED_NETDEV_UP (NL802154_FLAG_NEED_NETDEV |\
422 NL802154_FLAG_CHECK_NETDEV_UP)
423 #define NL802154_FLAG_NEED_WPAN_DEV 0x10
424 #define NL802154_FLAG_NEED_WPAN_DEV_UP (NL802154_FLAG_NEED_WPAN_DEV |\
425 NL802154_FLAG_CHECK_NETDEV_UP)
427 static int nl802154_pre_doit(const struct genl_ops
*ops
, struct sk_buff
*skb
,
428 struct genl_info
*info
)
430 struct cfg802154_registered_device
*rdev
;
431 struct wpan_dev
*wpan_dev
;
432 struct net_device
*dev
;
433 bool rtnl
= ops
->internal_flags
& NL802154_FLAG_NEED_RTNL
;
438 if (ops
->internal_flags
& NL802154_FLAG_NEED_WPAN_PHY
) {
439 rdev
= cfg802154_get_dev_from_info(genl_info_net(info
), info
);
443 return PTR_ERR(rdev
);
445 info
->user_ptr
[0] = rdev
;
446 } else if (ops
->internal_flags
& NL802154_FLAG_NEED_NETDEV
||
447 ops
->internal_flags
& NL802154_FLAG_NEED_WPAN_DEV
) {
449 wpan_dev
= __cfg802154_wpan_dev_from_attrs(genl_info_net(info
),
451 if (IS_ERR(wpan_dev
)) {
454 return PTR_ERR(wpan_dev
);
457 dev
= wpan_dev
->netdev
;
458 rdev
= wpan_phy_to_rdev(wpan_dev
->wpan_phy
);
460 if (ops
->internal_flags
& NL802154_FLAG_NEED_NETDEV
) {
467 info
->user_ptr
[1] = dev
;
469 info
->user_ptr
[1] = wpan_dev
;
473 if (ops
->internal_flags
& NL802154_FLAG_CHECK_NETDEV_UP
&&
474 !netif_running(dev
)) {
483 info
->user_ptr
[0] = rdev
;
489 static void nl802154_post_doit(const struct genl_ops
*ops
, struct sk_buff
*skb
,
490 struct genl_info
*info
)
492 if (info
->user_ptr
[1]) {
493 if (ops
->internal_flags
& NL802154_FLAG_NEED_WPAN_DEV
) {
494 struct wpan_dev
*wpan_dev
= info
->user_ptr
[1];
496 if (wpan_dev
->netdev
)
497 dev_put(wpan_dev
->netdev
);
499 dev_put(info
->user_ptr
[1]);
503 if (ops
->internal_flags
& NL802154_FLAG_NEED_RTNL
)
507 static const struct genl_ops nl802154_ops
[] = {
509 .cmd
= NL802154_CMD_GET_WPAN_PHY
,
510 .doit
= nl802154_get_wpan_phy
,
511 .dumpit
= nl802154_dump_wpan_phy
,
512 .done
= nl802154_dump_wpan_phy_done
,
513 .policy
= nl802154_policy
,
514 /* can be retrieved by unprivileged users */
515 .internal_flags
= NL802154_FLAG_NEED_WPAN_PHY
|
516 NL802154_FLAG_NEED_RTNL
,
520 /* initialisation/exit functions */
521 int nl802154_init(void)
523 return genl_register_family_with_ops_groups(&nl802154_fam
, nl802154_ops
,
527 void nl802154_exit(void)
529 genl_unregister_family(&nl802154_fam
);