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);