diff options
Diffstat (limited to 'net/ieee802154/netlink.c')
-rw-r--r-- | net/ieee802154/netlink.c | 172 |
1 files changed, 164 insertions, 8 deletions
diff --git a/net/ieee802154/netlink.c b/net/ieee802154/netlink.c index 27eda9fdf3c2..2106ecbf0308 100644 --- a/net/ieee802154/netlink.c +++ b/net/ieee802154/netlink.c | |||
@@ -19,6 +19,7 @@ | |||
19 | * Written by: | 19 | * Written by: |
20 | * Sergey Lapin <slapin@ossfans.org> | 20 | * Sergey Lapin <slapin@ossfans.org> |
21 | * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> | 21 | * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> |
22 | * Maxim Osipov <maxim.osipov@siemens.com> | ||
22 | */ | 23 | */ |
23 | 24 | ||
24 | #include <linux/kernel.h> | 25 | #include <linux/kernel.h> |
@@ -26,10 +27,12 @@ | |||
26 | #include <linux/netdevice.h> | 27 | #include <linux/netdevice.h> |
27 | #include <net/netlink.h> | 28 | #include <net/netlink.h> |
28 | #include <net/genetlink.h> | 29 | #include <net/genetlink.h> |
30 | #include <net/sock.h> | ||
29 | #include <linux/nl802154.h> | 31 | #include <linux/nl802154.h> |
30 | #include <net/ieee802154/af_ieee802154.h> | 32 | #include <net/af_ieee802154.h> |
31 | #include <net/ieee802154/nl802154.h> | 33 | #include <net/nl802154.h> |
32 | #include <net/ieee802154/netdevice.h> | 34 | #include <net/ieee802154.h> |
35 | #include <net/ieee802154_netdev.h> | ||
33 | 36 | ||
34 | static unsigned int ieee802154_seq_num; | 37 | static unsigned int ieee802154_seq_num; |
35 | 38 | ||
@@ -73,7 +76,7 @@ static int ieee802154_nl_finish(struct sk_buff *msg) | |||
73 | /* XXX: nlh is right at the start of msg */ | 76 | /* XXX: nlh is right at the start of msg */ |
74 | void *hdr = genlmsg_data(NLMSG_DATA(msg->data)); | 77 | void *hdr = genlmsg_data(NLMSG_DATA(msg->data)); |
75 | 78 | ||
76 | if (!genlmsg_end(msg, hdr)) | 79 | if (genlmsg_end(msg, hdr) < 0) |
77 | goto out; | 80 | goto out; |
78 | 81 | ||
79 | return genlmsg_multicast(msg, 0, ieee802154_coord_mcgrp.id, | 82 | return genlmsg_multicast(msg, 0, ieee802154_coord_mcgrp.id, |
@@ -229,7 +232,7 @@ nla_put_failure: | |||
229 | EXPORT_SYMBOL(ieee802154_nl_beacon_indic); | 232 | EXPORT_SYMBOL(ieee802154_nl_beacon_indic); |
230 | 233 | ||
231 | int ieee802154_nl_scan_confirm(struct net_device *dev, | 234 | int ieee802154_nl_scan_confirm(struct net_device *dev, |
232 | u8 status, u8 scan_type, u32 unscanned, | 235 | u8 status, u8 scan_type, u32 unscanned, u8 page, |
233 | u8 *edl/* , struct list_head *pan_desc_list */) | 236 | u8 *edl/* , struct list_head *pan_desc_list */) |
234 | { | 237 | { |
235 | struct sk_buff *msg; | 238 | struct sk_buff *msg; |
@@ -248,6 +251,7 @@ int ieee802154_nl_scan_confirm(struct net_device *dev, | |||
248 | NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status); | 251 | NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status); |
249 | NLA_PUT_U8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type); | 252 | NLA_PUT_U8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type); |
250 | NLA_PUT_U32(msg, IEEE802154_ATTR_CHANNELS, unscanned); | 253 | NLA_PUT_U32(msg, IEEE802154_ATTR_CHANNELS, unscanned); |
254 | NLA_PUT_U8(msg, IEEE802154_ATTR_PAGE, page); | ||
251 | 255 | ||
252 | if (edl) | 256 | if (edl) |
253 | NLA_PUT(msg, IEEE802154_ATTR_ED_LIST, 27, edl); | 257 | NLA_PUT(msg, IEEE802154_ATTR_ED_LIST, 27, edl); |
@@ -260,6 +264,60 @@ nla_put_failure: | |||
260 | } | 264 | } |
261 | EXPORT_SYMBOL(ieee802154_nl_scan_confirm); | 265 | EXPORT_SYMBOL(ieee802154_nl_scan_confirm); |
262 | 266 | ||
267 | int ieee802154_nl_start_confirm(struct net_device *dev, u8 status) | ||
268 | { | ||
269 | struct sk_buff *msg; | ||
270 | |||
271 | pr_debug("%s\n", __func__); | ||
272 | |||
273 | msg = ieee802154_nl_create(0, IEEE802154_START_CONF); | ||
274 | if (!msg) | ||
275 | return -ENOBUFS; | ||
276 | |||
277 | NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); | ||
278 | NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); | ||
279 | NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, | ||
280 | dev->dev_addr); | ||
281 | |||
282 | NLA_PUT_U8(msg, IEEE802154_ATTR_STATUS, status); | ||
283 | |||
284 | return ieee802154_nl_finish(msg); | ||
285 | |||
286 | nla_put_failure: | ||
287 | nlmsg_free(msg); | ||
288 | return -ENOBUFS; | ||
289 | } | ||
290 | EXPORT_SYMBOL(ieee802154_nl_start_confirm); | ||
291 | |||
292 | static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 pid, | ||
293 | u32 seq, int flags, struct net_device *dev) | ||
294 | { | ||
295 | void *hdr; | ||
296 | |||
297 | pr_debug("%s\n", __func__); | ||
298 | |||
299 | hdr = genlmsg_put(msg, 0, seq, &ieee802154_coordinator_family, flags, | ||
300 | IEEE802154_LIST_IFACE); | ||
301 | if (!hdr) | ||
302 | goto out; | ||
303 | |||
304 | NLA_PUT_STRING(msg, IEEE802154_ATTR_DEV_NAME, dev->name); | ||
305 | NLA_PUT_U32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex); | ||
306 | |||
307 | NLA_PUT(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN, | ||
308 | dev->dev_addr); | ||
309 | NLA_PUT_U16(msg, IEEE802154_ATTR_SHORT_ADDR, | ||
310 | ieee802154_mlme_ops(dev)->get_short_addr(dev)); | ||
311 | NLA_PUT_U16(msg, IEEE802154_ATTR_PAN_ID, | ||
312 | ieee802154_mlme_ops(dev)->get_pan_id(dev)); | ||
313 | return genlmsg_end(msg, hdr); | ||
314 | |||
315 | nla_put_failure: | ||
316 | genlmsg_cancel(msg, hdr); | ||
317 | out: | ||
318 | return -EMSGSIZE; | ||
319 | } | ||
320 | |||
263 | /* Requests from userspace */ | 321 | /* Requests from userspace */ |
264 | static struct net_device *ieee802154_nl_get_dev(struct genl_info *info) | 322 | static struct net_device *ieee802154_nl_get_dev(struct genl_info *info) |
265 | { | 323 | { |
@@ -272,7 +330,7 @@ static struct net_device *ieee802154_nl_get_dev(struct genl_info *info) | |||
272 | dev = dev_get_by_name(&init_net, name); | 330 | dev = dev_get_by_name(&init_net, name); |
273 | } else if (info->attrs[IEEE802154_ATTR_DEV_INDEX]) | 331 | } else if (info->attrs[IEEE802154_ATTR_DEV_INDEX]) |
274 | dev = dev_get_by_index(&init_net, | 332 | dev = dev_get_by_index(&init_net, |
275 | nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX])); | 333 | nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX])); |
276 | else | 334 | else |
277 | return NULL; | 335 | return NULL; |
278 | 336 | ||
@@ -292,6 +350,7 @@ static int ieee802154_associate_req(struct sk_buff *skb, | |||
292 | { | 350 | { |
293 | struct net_device *dev; | 351 | struct net_device *dev; |
294 | struct ieee802154_addr addr; | 352 | struct ieee802154_addr addr; |
353 | u8 page; | ||
295 | int ret = -EINVAL; | 354 | int ret = -EINVAL; |
296 | 355 | ||
297 | if (!info->attrs[IEEE802154_ATTR_CHANNEL] || | 356 | if (!info->attrs[IEEE802154_ATTR_CHANNEL] || |
@@ -317,8 +376,14 @@ static int ieee802154_associate_req(struct sk_buff *skb, | |||
317 | } | 376 | } |
318 | addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]); | 377 | addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]); |
319 | 378 | ||
379 | if (info->attrs[IEEE802154_ATTR_PAGE]) | ||
380 | page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]); | ||
381 | else | ||
382 | page = 0; | ||
383 | |||
320 | ret = ieee802154_mlme_ops(dev)->assoc_req(dev, &addr, | 384 | ret = ieee802154_mlme_ops(dev)->assoc_req(dev, &addr, |
321 | nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]), | 385 | nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]), |
386 | page, | ||
322 | nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY])); | 387 | nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY])); |
323 | 388 | ||
324 | dev_put(dev); | 389 | dev_put(dev); |
@@ -401,6 +466,7 @@ static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info) | |||
401 | struct ieee802154_addr addr; | 466 | struct ieee802154_addr addr; |
402 | 467 | ||
403 | u8 channel, bcn_ord, sf_ord; | 468 | u8 channel, bcn_ord, sf_ord; |
469 | u8 page; | ||
404 | int pan_coord, blx, coord_realign; | 470 | int pan_coord, blx, coord_realign; |
405 | int ret; | 471 | int ret; |
406 | 472 | ||
@@ -431,7 +497,19 @@ static int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info) | |||
431 | blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]); | 497 | blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]); |
432 | coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]); | 498 | coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]); |
433 | 499 | ||
434 | ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, | 500 | if (info->attrs[IEEE802154_ATTR_PAGE]) |
501 | page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]); | ||
502 | else | ||
503 | page = 0; | ||
504 | |||
505 | |||
506 | if (addr.short_addr == IEEE802154_ADDR_BROADCAST) { | ||
507 | ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS); | ||
508 | dev_put(dev); | ||
509 | return -EINVAL; | ||
510 | } | ||
511 | |||
512 | ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page, | ||
435 | bcn_ord, sf_ord, pan_coord, blx, coord_realign); | 513 | bcn_ord, sf_ord, pan_coord, blx, coord_realign); |
436 | 514 | ||
437 | dev_put(dev); | 515 | dev_put(dev); |
@@ -445,6 +523,7 @@ static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info) | |||
445 | u8 type; | 523 | u8 type; |
446 | u32 channels; | 524 | u32 channels; |
447 | u8 duration; | 525 | u8 duration; |
526 | u8 page; | ||
448 | 527 | ||
449 | if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] || | 528 | if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] || |
450 | !info->attrs[IEEE802154_ATTR_CHANNELS] || | 529 | !info->attrs[IEEE802154_ATTR_CHANNELS] || |
@@ -459,13 +538,80 @@ static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info) | |||
459 | channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]); | 538 | channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]); |
460 | duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]); | 539 | duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]); |
461 | 540 | ||
462 | ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, | 541 | if (info->attrs[IEEE802154_ATTR_PAGE]) |
542 | page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]); | ||
543 | else | ||
544 | page = 0; | ||
545 | |||
546 | |||
547 | ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, page, | ||
463 | duration); | 548 | duration); |
464 | 549 | ||
465 | dev_put(dev); | 550 | dev_put(dev); |
466 | return ret; | 551 | return ret; |
467 | } | 552 | } |
468 | 553 | ||
554 | static int ieee802154_list_iface(struct sk_buff *skb, | ||
555 | struct genl_info *info) | ||
556 | { | ||
557 | /* Request for interface name, index, type, IEEE address, | ||
558 | PAN Id, short address */ | ||
559 | struct sk_buff *msg; | ||
560 | struct net_device *dev = NULL; | ||
561 | int rc = -ENOBUFS; | ||
562 | |||
563 | pr_debug("%s\n", __func__); | ||
564 | |||
565 | dev = ieee802154_nl_get_dev(info); | ||
566 | if (!dev) | ||
567 | return -ENODEV; | ||
568 | |||
569 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
570 | if (!msg) | ||
571 | goto out_dev; | ||
572 | |||
573 | rc = ieee802154_nl_fill_iface(msg, info->snd_pid, info->snd_seq, | ||
574 | 0, dev); | ||
575 | if (rc < 0) | ||
576 | goto out_free; | ||
577 | |||
578 | dev_put(dev); | ||
579 | |||
580 | return genlmsg_unicast(&init_net, msg, info->snd_pid); | ||
581 | out_free: | ||
582 | nlmsg_free(msg); | ||
583 | out_dev: | ||
584 | dev_put(dev); | ||
585 | return rc; | ||
586 | |||
587 | } | ||
588 | |||
589 | static int ieee802154_dump_iface(struct sk_buff *skb, | ||
590 | struct netlink_callback *cb) | ||
591 | { | ||
592 | struct net *net = sock_net(skb->sk); | ||
593 | struct net_device *dev; | ||
594 | int idx; | ||
595 | int s_idx = cb->args[0]; | ||
596 | |||
597 | pr_debug("%s\n", __func__); | ||
598 | |||
599 | idx = 0; | ||
600 | for_each_netdev(net, dev) { | ||
601 | if (idx < s_idx || (dev->type != ARPHRD_IEEE802154)) | ||
602 | goto cont; | ||
603 | |||
604 | if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).pid, | ||
605 | cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0) | ||
606 | break; | ||
607 | cont: | ||
608 | idx++; | ||
609 | } | ||
610 | cb->args[0] = idx; | ||
611 | |||
612 | return skb->len; | ||
613 | } | ||
614 | |||
469 | #define IEEE802154_OP(_cmd, _func) \ | 615 | #define IEEE802154_OP(_cmd, _func) \ |
470 | { \ | 616 | { \ |
471 | .cmd = _cmd, \ | 617 | .cmd = _cmd, \ |
@@ -475,12 +621,22 @@ static int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info) | |||
475 | .flags = GENL_ADMIN_PERM, \ | 621 | .flags = GENL_ADMIN_PERM, \ |
476 | } | 622 | } |
477 | 623 | ||
624 | #define IEEE802154_DUMP(_cmd, _func, _dump) \ | ||
625 | { \ | ||
626 | .cmd = _cmd, \ | ||
627 | .policy = ieee802154_policy, \ | ||
628 | .doit = _func, \ | ||
629 | .dumpit = _dump, \ | ||
630 | } | ||
631 | |||
478 | static struct genl_ops ieee802154_coordinator_ops[] = { | 632 | static struct genl_ops ieee802154_coordinator_ops[] = { |
479 | IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req), | 633 | IEEE802154_OP(IEEE802154_ASSOCIATE_REQ, ieee802154_associate_req), |
480 | IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp), | 634 | IEEE802154_OP(IEEE802154_ASSOCIATE_RESP, ieee802154_associate_resp), |
481 | IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req), | 635 | IEEE802154_OP(IEEE802154_DISASSOCIATE_REQ, ieee802154_disassociate_req), |
482 | IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req), | 636 | IEEE802154_OP(IEEE802154_SCAN_REQ, ieee802154_scan_req), |
483 | IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req), | 637 | IEEE802154_OP(IEEE802154_START_REQ, ieee802154_start_req), |
638 | IEEE802154_DUMP(IEEE802154_LIST_IFACE, ieee802154_list_iface, | ||
639 | ieee802154_dump_iface), | ||
484 | }; | 640 | }; |
485 | 641 | ||
486 | static int __init ieee802154_nl_init(void) | 642 | static int __init ieee802154_nl_init(void) |