11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 21802d0beSThomas Gleixner /* 379fe1a2aSAlexander Aring * 479fe1a2aSAlexander Aring * Authors: 579fe1a2aSAlexander Aring * Alexander Aring <aar@pengutronix.de> 679fe1a2aSAlexander Aring * 779fe1a2aSAlexander Aring * Based on: net/wireless/nl80211.c 879fe1a2aSAlexander Aring */ 979fe1a2aSAlexander Aring 1079fe1a2aSAlexander Aring #include <linux/rtnetlink.h> 1179fe1a2aSAlexander Aring 1279fe1a2aSAlexander Aring #include <net/cfg802154.h> 1379fe1a2aSAlexander Aring #include <net/genetlink.h> 1479fe1a2aSAlexander Aring #include <net/mac802154.h> 1579fe1a2aSAlexander Aring #include <net/netlink.h> 1679fe1a2aSAlexander Aring #include <net/nl802154.h> 1779fe1a2aSAlexander Aring #include <net/sock.h> 1879fe1a2aSAlexander Aring 1979fe1a2aSAlexander Aring #include "nl802154.h" 20ab0bd561SAlexander Aring #include "rdev-ops.h" 2179fe1a2aSAlexander Aring #include "core.h" 2279fe1a2aSAlexander Aring 2379fe1a2aSAlexander Aring /* the netlink family */ 24489111e5SJohannes Berg static struct genl_family nl802154_fam; 2579fe1a2aSAlexander Aring 2679fe1a2aSAlexander Aring /* multicast groups */ 2779fe1a2aSAlexander Aring enum nl802154_multicast_groups { 2879fe1a2aSAlexander Aring NL802154_MCGRP_CONFIG, 2951147284SMiquel Raynal NL802154_MCGRP_SCAN, 3079fe1a2aSAlexander Aring }; 3179fe1a2aSAlexander Aring 3279fe1a2aSAlexander Aring static const struct genl_multicast_group nl802154_mcgrps[] = { 3379fe1a2aSAlexander Aring [NL802154_MCGRP_CONFIG] = { .name = "config", }, 3451147284SMiquel Raynal [NL802154_MCGRP_SCAN] = { .name = "scan", }, 3579fe1a2aSAlexander Aring }; 3679fe1a2aSAlexander Aring 3779fe1a2aSAlexander Aring /* returns ERR_PTR values */ 3879fe1a2aSAlexander Aring static struct wpan_dev * 3979fe1a2aSAlexander Aring __cfg802154_wpan_dev_from_attrs(struct net *netns, struct nlattr **attrs) 4079fe1a2aSAlexander Aring { 4179fe1a2aSAlexander Aring struct cfg802154_registered_device *rdev; 4279fe1a2aSAlexander Aring struct wpan_dev *result = NULL; 4379fe1a2aSAlexander Aring bool have_ifidx = attrs[NL802154_ATTR_IFINDEX]; 4479fe1a2aSAlexander Aring bool have_wpan_dev_id = attrs[NL802154_ATTR_WPAN_DEV]; 4579fe1a2aSAlexander Aring u64 wpan_dev_id; 4679fe1a2aSAlexander Aring int wpan_phy_idx = -1; 4779fe1a2aSAlexander Aring int ifidx = -1; 4879fe1a2aSAlexander Aring 4979fe1a2aSAlexander Aring ASSERT_RTNL(); 5079fe1a2aSAlexander Aring 5179fe1a2aSAlexander Aring if (!have_ifidx && !have_wpan_dev_id) 5279fe1a2aSAlexander Aring return ERR_PTR(-EINVAL); 5379fe1a2aSAlexander Aring 5479fe1a2aSAlexander Aring if (have_ifidx) 5579fe1a2aSAlexander Aring ifidx = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]); 5679fe1a2aSAlexander Aring if (have_wpan_dev_id) { 5779fe1a2aSAlexander Aring wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]); 5879fe1a2aSAlexander Aring wpan_phy_idx = wpan_dev_id >> 32; 5979fe1a2aSAlexander Aring } 6079fe1a2aSAlexander Aring 6179fe1a2aSAlexander Aring list_for_each_entry(rdev, &cfg802154_rdev_list, list) { 6279fe1a2aSAlexander Aring struct wpan_dev *wpan_dev; 6379fe1a2aSAlexander Aring 6466e5c267SAlexander Aring if (wpan_phy_net(&rdev->wpan_phy) != netns) 6566e5c267SAlexander Aring continue; 6679fe1a2aSAlexander Aring 6779fe1a2aSAlexander Aring if (have_wpan_dev_id && rdev->wpan_phy_idx != wpan_phy_idx) 6879fe1a2aSAlexander Aring continue; 6979fe1a2aSAlexander Aring 7079fe1a2aSAlexander Aring list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) { 7179fe1a2aSAlexander Aring if (have_ifidx && wpan_dev->netdev && 7279fe1a2aSAlexander Aring wpan_dev->netdev->ifindex == ifidx) { 7379fe1a2aSAlexander Aring result = wpan_dev; 7479fe1a2aSAlexander Aring break; 7579fe1a2aSAlexander Aring } 7679fe1a2aSAlexander Aring if (have_wpan_dev_id && 7779fe1a2aSAlexander Aring wpan_dev->identifier == (u32)wpan_dev_id) { 7879fe1a2aSAlexander Aring result = wpan_dev; 7979fe1a2aSAlexander Aring break; 8079fe1a2aSAlexander Aring } 8179fe1a2aSAlexander Aring } 8279fe1a2aSAlexander Aring 8379fe1a2aSAlexander Aring if (result) 8479fe1a2aSAlexander Aring break; 8579fe1a2aSAlexander Aring } 8679fe1a2aSAlexander Aring 8779fe1a2aSAlexander Aring if (result) 8879fe1a2aSAlexander Aring return result; 8979fe1a2aSAlexander Aring 9079fe1a2aSAlexander Aring return ERR_PTR(-ENODEV); 9179fe1a2aSAlexander Aring } 9279fe1a2aSAlexander Aring 9379fe1a2aSAlexander Aring static struct cfg802154_registered_device * 9479fe1a2aSAlexander Aring __cfg802154_rdev_from_attrs(struct net *netns, struct nlattr **attrs) 9579fe1a2aSAlexander Aring { 9679fe1a2aSAlexander Aring struct cfg802154_registered_device *rdev = NULL, *tmp; 9779fe1a2aSAlexander Aring struct net_device *netdev; 9879fe1a2aSAlexander Aring 9979fe1a2aSAlexander Aring ASSERT_RTNL(); 10079fe1a2aSAlexander Aring 10179fe1a2aSAlexander Aring if (!attrs[NL802154_ATTR_WPAN_PHY] && 10279fe1a2aSAlexander Aring !attrs[NL802154_ATTR_IFINDEX] && 10379fe1a2aSAlexander Aring !attrs[NL802154_ATTR_WPAN_DEV]) 10479fe1a2aSAlexander Aring return ERR_PTR(-EINVAL); 10579fe1a2aSAlexander Aring 10679fe1a2aSAlexander Aring if (attrs[NL802154_ATTR_WPAN_PHY]) 10779fe1a2aSAlexander Aring rdev = cfg802154_rdev_by_wpan_phy_idx( 10879fe1a2aSAlexander Aring nla_get_u32(attrs[NL802154_ATTR_WPAN_PHY])); 10979fe1a2aSAlexander Aring 11079fe1a2aSAlexander Aring if (attrs[NL802154_ATTR_WPAN_DEV]) { 11179fe1a2aSAlexander Aring u64 wpan_dev_id = nla_get_u64(attrs[NL802154_ATTR_WPAN_DEV]); 11279fe1a2aSAlexander Aring struct wpan_dev *wpan_dev; 11379fe1a2aSAlexander Aring bool found = false; 11479fe1a2aSAlexander Aring 11579fe1a2aSAlexander Aring tmp = cfg802154_rdev_by_wpan_phy_idx(wpan_dev_id >> 32); 11679fe1a2aSAlexander Aring if (tmp) { 11779fe1a2aSAlexander Aring /* make sure wpan_dev exists */ 11879fe1a2aSAlexander Aring list_for_each_entry(wpan_dev, &tmp->wpan_dev_list, list) { 11979fe1a2aSAlexander Aring if (wpan_dev->identifier != (u32)wpan_dev_id) 12079fe1a2aSAlexander Aring continue; 12179fe1a2aSAlexander Aring found = true; 12279fe1a2aSAlexander Aring break; 12379fe1a2aSAlexander Aring } 12479fe1a2aSAlexander Aring 12579fe1a2aSAlexander Aring if (!found) 12679fe1a2aSAlexander Aring tmp = NULL; 12779fe1a2aSAlexander Aring 12879fe1a2aSAlexander Aring if (rdev && tmp != rdev) 12979fe1a2aSAlexander Aring return ERR_PTR(-EINVAL); 13079fe1a2aSAlexander Aring rdev = tmp; 13179fe1a2aSAlexander Aring } 13279fe1a2aSAlexander Aring } 13379fe1a2aSAlexander Aring 13479fe1a2aSAlexander Aring if (attrs[NL802154_ATTR_IFINDEX]) { 13579fe1a2aSAlexander Aring int ifindex = nla_get_u32(attrs[NL802154_ATTR_IFINDEX]); 13679fe1a2aSAlexander Aring 13779fe1a2aSAlexander Aring netdev = __dev_get_by_index(netns, ifindex); 13879fe1a2aSAlexander Aring if (netdev) { 13979fe1a2aSAlexander Aring if (netdev->ieee802154_ptr) 14079fe1a2aSAlexander Aring tmp = wpan_phy_to_rdev( 14179fe1a2aSAlexander Aring netdev->ieee802154_ptr->wpan_phy); 14279fe1a2aSAlexander Aring else 14379fe1a2aSAlexander Aring tmp = NULL; 14479fe1a2aSAlexander Aring 14579fe1a2aSAlexander Aring /* not wireless device -- return error */ 14679fe1a2aSAlexander Aring if (!tmp) 14779fe1a2aSAlexander Aring return ERR_PTR(-EINVAL); 14879fe1a2aSAlexander Aring 14979fe1a2aSAlexander Aring /* mismatch -- return error */ 15079fe1a2aSAlexander Aring if (rdev && tmp != rdev) 15179fe1a2aSAlexander Aring return ERR_PTR(-EINVAL); 15279fe1a2aSAlexander Aring 15379fe1a2aSAlexander Aring rdev = tmp; 15479fe1a2aSAlexander Aring } 15579fe1a2aSAlexander Aring } 15679fe1a2aSAlexander Aring 15779fe1a2aSAlexander Aring if (!rdev) 15879fe1a2aSAlexander Aring return ERR_PTR(-ENODEV); 15979fe1a2aSAlexander Aring 16066e5c267SAlexander Aring if (netns != wpan_phy_net(&rdev->wpan_phy)) 16166e5c267SAlexander Aring return ERR_PTR(-ENODEV); 16279fe1a2aSAlexander Aring 16379fe1a2aSAlexander Aring return rdev; 16479fe1a2aSAlexander Aring } 16579fe1a2aSAlexander Aring 16679fe1a2aSAlexander Aring /* This function returns a pointer to the driver 16779fe1a2aSAlexander Aring * that the genl_info item that is passed refers to. 16879fe1a2aSAlexander Aring * 16979fe1a2aSAlexander Aring * The result of this can be a PTR_ERR and hence must 17079fe1a2aSAlexander Aring * be checked with IS_ERR() for errors. 17179fe1a2aSAlexander Aring */ 17279fe1a2aSAlexander Aring static struct cfg802154_registered_device * 17379fe1a2aSAlexander Aring cfg802154_get_dev_from_info(struct net *netns, struct genl_info *info) 17479fe1a2aSAlexander Aring { 17579fe1a2aSAlexander Aring return __cfg802154_rdev_from_attrs(netns, info->attrs); 17679fe1a2aSAlexander Aring } 17779fe1a2aSAlexander Aring 17879fe1a2aSAlexander Aring /* policy for the attributes */ 17979fe1a2aSAlexander Aring static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = { 180ca20ce20SAlexander Aring [NL802154_ATTR_WPAN_PHY] = { .type = NLA_U32 }, 181ca20ce20SAlexander Aring [NL802154_ATTR_WPAN_PHY_NAME] = { .type = NLA_NUL_STRING, 182ca20ce20SAlexander Aring .len = 20-1 }, 183ca20ce20SAlexander Aring 184ca20ce20SAlexander Aring [NL802154_ATTR_IFINDEX] = { .type = NLA_U32 }, 1854b96aea0SAlexander Aring [NL802154_ATTR_IFTYPE] = { .type = NLA_U32 }, 1864b96aea0SAlexander Aring [NL802154_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 }, 187ca20ce20SAlexander Aring 188ca20ce20SAlexander Aring [NL802154_ATTR_WPAN_DEV] = { .type = NLA_U64 }, 189ca20ce20SAlexander Aring 190648324c9SMiquel Raynal [NL802154_ATTR_PAGE] = NLA_POLICY_MAX(NLA_U8, IEEE802154_MAX_PAGE), 191648324c9SMiquel Raynal [NL802154_ATTR_CHANNEL] = NLA_POLICY_MAX(NLA_U8, IEEE802154_MAX_CHANNEL), 192ca20ce20SAlexander Aring 1931a19cb68SAlexander Aring [NL802154_ATTR_TX_POWER] = { .type = NLA_S32, }, 194ca20ce20SAlexander Aring 195ba2a9506SAlexander Aring [NL802154_ATTR_CCA_MODE] = { .type = NLA_U32, }, 196ba2a9506SAlexander Aring [NL802154_ATTR_CCA_OPT] = { .type = NLA_U32, }, 197e4390592SAlexander Aring [NL802154_ATTR_CCA_ED_LEVEL] = { .type = NLA_S32, }, 198ca20ce20SAlexander Aring 199ca20ce20SAlexander Aring [NL802154_ATTR_SUPPORTED_CHANNEL] = { .type = NLA_U32, }, 2004b96aea0SAlexander Aring 2014b96aea0SAlexander Aring [NL802154_ATTR_PAN_ID] = { .type = NLA_U16, }, 2024b96aea0SAlexander Aring [NL802154_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 }, 2034b96aea0SAlexander Aring [NL802154_ATTR_SHORT_ADDR] = { .type = NLA_U16, }, 2044b96aea0SAlexander Aring 2054b96aea0SAlexander Aring [NL802154_ATTR_MIN_BE] = { .type = NLA_U8, }, 2064b96aea0SAlexander Aring [NL802154_ATTR_MAX_BE] = { .type = NLA_U8, }, 2074b96aea0SAlexander Aring [NL802154_ATTR_MAX_CSMA_BACKOFFS] = { .type = NLA_U8, }, 2084b96aea0SAlexander Aring 2094b96aea0SAlexander Aring [NL802154_ATTR_MAX_FRAME_RETRIES] = { .type = NLA_S8, }, 2104b96aea0SAlexander Aring 2114b96aea0SAlexander Aring [NL802154_ATTR_LBT_MODE] = { .type = NLA_U8, }, 2120e665457SAlexander Aring 2130e665457SAlexander Aring [NL802154_ATTR_WPAN_PHY_CAPS] = { .type = NLA_NESTED }, 214133be026SVarka Bhadram 215133be026SVarka Bhadram [NL802154_ATTR_SUPPORTED_COMMANDS] = { .type = NLA_NESTED }, 216c91208d8SAlexander Aring 217c91208d8SAlexander Aring [NL802154_ATTR_ACKREQ_DEFAULT] = { .type = NLA_U8 }, 218a26c5fd7SAlexander Aring 21966e5c267SAlexander Aring [NL802154_ATTR_PID] = { .type = NLA_U32 }, 22066e5c267SAlexander Aring [NL802154_ATTR_NETNS_FD] = { .type = NLA_U32 }, 22151147284SMiquel Raynal 22251147284SMiquel Raynal [NL802154_ATTR_COORDINATOR] = { .type = NLA_NESTED }, 22351147284SMiquel Raynal 224648324c9SMiquel Raynal [NL802154_ATTR_SCAN_TYPE] = 225648324c9SMiquel Raynal NLA_POLICY_RANGE(NLA_U8, NL802154_SCAN_ED, NL802154_SCAN_RIT_PASSIVE), 226648324c9SMiquel Raynal [NL802154_ATTR_SCAN_CHANNELS] = 227648324c9SMiquel Raynal NLA_POLICY_MASK(NLA_U32, GENMASK(IEEE802154_MAX_CHANNEL, 0)), 228648324c9SMiquel Raynal [NL802154_ATTR_SCAN_PREAMBLE_CODES] = { .type = NLA_REJECT }, 229648324c9SMiquel Raynal [NL802154_ATTR_SCAN_MEAN_PRF] = { .type = NLA_REJECT }, 230648324c9SMiquel Raynal [NL802154_ATTR_SCAN_DURATION] = 231648324c9SMiquel Raynal NLA_POLICY_MAX(NLA_U8, IEEE802154_MAX_SCAN_DURATION), 232648324c9SMiquel Raynal [NL802154_ATTR_SCAN_DONE_REASON] = 233648324c9SMiquel Raynal NLA_POLICY_RANGE(NLA_U8, NL802154_SCAN_DONE_REASON_FINISHED, 234648324c9SMiquel Raynal NL802154_SCAN_DONE_REASON_ABORTED), 235648324c9SMiquel Raynal [NL802154_ATTR_BEACON_INTERVAL] = 236648324c9SMiquel Raynal NLA_POLICY_MAX(NLA_U8, IEEE802154_MAX_SCAN_DURATION), 237ed3557c9SMiquel Raynal 238a26c5fd7SAlexander Aring #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL 239a26c5fd7SAlexander Aring [NL802154_ATTR_SEC_ENABLED] = { .type = NLA_U8, }, 240a26c5fd7SAlexander Aring [NL802154_ATTR_SEC_OUT_LEVEL] = { .type = NLA_U32, }, 241a26c5fd7SAlexander Aring [NL802154_ATTR_SEC_OUT_KEY_ID] = { .type = NLA_NESTED, }, 242a26c5fd7SAlexander Aring [NL802154_ATTR_SEC_FRAME_COUNTER] = { .type = NLA_U32 }, 243a26c5fd7SAlexander Aring 244a26c5fd7SAlexander Aring [NL802154_ATTR_SEC_LEVEL] = { .type = NLA_NESTED }, 245a26c5fd7SAlexander Aring [NL802154_ATTR_SEC_DEVICE] = { .type = NLA_NESTED }, 246a26c5fd7SAlexander Aring [NL802154_ATTR_SEC_DEVKEY] = { .type = NLA_NESTED }, 247a26c5fd7SAlexander Aring [NL802154_ATTR_SEC_KEY] = { .type = NLA_NESTED }, 248a26c5fd7SAlexander Aring #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ 24979fe1a2aSAlexander Aring }; 25079fe1a2aSAlexander Aring 251a26c5fd7SAlexander Aring #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL 252a26c5fd7SAlexander Aring static int 253a26c5fd7SAlexander Aring nl802154_prepare_wpan_dev_dump(struct sk_buff *skb, 254a26c5fd7SAlexander Aring struct netlink_callback *cb, 255a26c5fd7SAlexander Aring struct cfg802154_registered_device **rdev, 256a26c5fd7SAlexander Aring struct wpan_dev **wpan_dev) 257a26c5fd7SAlexander Aring { 25875cdbdd0SJiri Pirko const struct genl_dumpit_info *info = genl_dumpit_info(cb); 259a26c5fd7SAlexander Aring int err; 260a26c5fd7SAlexander Aring 261a26c5fd7SAlexander Aring rtnl_lock(); 262a26c5fd7SAlexander Aring 263a26c5fd7SAlexander Aring if (!cb->args[0]) { 264a26c5fd7SAlexander Aring *wpan_dev = __cfg802154_wpan_dev_from_attrs(sock_net(skb->sk), 26575cdbdd0SJiri Pirko info->attrs); 266a26c5fd7SAlexander Aring if (IS_ERR(*wpan_dev)) { 267a26c5fd7SAlexander Aring err = PTR_ERR(*wpan_dev); 268a26c5fd7SAlexander Aring goto out_unlock; 269a26c5fd7SAlexander Aring } 270a26c5fd7SAlexander Aring *rdev = wpan_phy_to_rdev((*wpan_dev)->wpan_phy); 271a26c5fd7SAlexander Aring /* 0 is the first index - add 1 to parse only once */ 272a26c5fd7SAlexander Aring cb->args[0] = (*rdev)->wpan_phy_idx + 1; 273a26c5fd7SAlexander Aring cb->args[1] = (*wpan_dev)->identifier; 274a26c5fd7SAlexander Aring } else { 275a26c5fd7SAlexander Aring /* subtract the 1 again here */ 276a26c5fd7SAlexander Aring struct wpan_phy *wpan_phy = wpan_phy_idx_to_wpan_phy(cb->args[0] - 1); 277a26c5fd7SAlexander Aring struct wpan_dev *tmp; 278a26c5fd7SAlexander Aring 279a26c5fd7SAlexander Aring if (!wpan_phy) { 280a26c5fd7SAlexander Aring err = -ENODEV; 281a26c5fd7SAlexander Aring goto out_unlock; 282a26c5fd7SAlexander Aring } 283a26c5fd7SAlexander Aring *rdev = wpan_phy_to_rdev(wpan_phy); 284a26c5fd7SAlexander Aring *wpan_dev = NULL; 285a26c5fd7SAlexander Aring 286a26c5fd7SAlexander Aring list_for_each_entry(tmp, &(*rdev)->wpan_dev_list, list) { 287a26c5fd7SAlexander Aring if (tmp->identifier == cb->args[1]) { 288a26c5fd7SAlexander Aring *wpan_dev = tmp; 289a26c5fd7SAlexander Aring break; 290a26c5fd7SAlexander Aring } 291a26c5fd7SAlexander Aring } 292a26c5fd7SAlexander Aring 293a26c5fd7SAlexander Aring if (!*wpan_dev) { 294a26c5fd7SAlexander Aring err = -ENODEV; 295a26c5fd7SAlexander Aring goto out_unlock; 296a26c5fd7SAlexander Aring } 297a26c5fd7SAlexander Aring } 298a26c5fd7SAlexander Aring 299a26c5fd7SAlexander Aring return 0; 300a26c5fd7SAlexander Aring out_unlock: 301a26c5fd7SAlexander Aring rtnl_unlock(); 302a26c5fd7SAlexander Aring return err; 303a26c5fd7SAlexander Aring } 304a26c5fd7SAlexander Aring 305a26c5fd7SAlexander Aring static void 306a26c5fd7SAlexander Aring nl802154_finish_wpan_dev_dump(struct cfg802154_registered_device *rdev) 307a26c5fd7SAlexander Aring { 308a26c5fd7SAlexander Aring rtnl_unlock(); 309a26c5fd7SAlexander Aring } 310a26c5fd7SAlexander Aring #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ 311a26c5fd7SAlexander Aring 31279fe1a2aSAlexander Aring /* message building helper */ 31379fe1a2aSAlexander Aring static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq, 31479fe1a2aSAlexander Aring int flags, u8 cmd) 31579fe1a2aSAlexander Aring { 31679fe1a2aSAlexander Aring /* since there is no private header just add the generic one */ 31779fe1a2aSAlexander Aring return genlmsg_put(skb, portid, seq, &nl802154_fam, flags, cmd); 31879fe1a2aSAlexander Aring } 31979fe1a2aSAlexander Aring 320ca20ce20SAlexander Aring static int 3210e665457SAlexander Aring nl802154_put_flags(struct sk_buff *msg, int attr, u32 mask) 3220e665457SAlexander Aring { 323ae0be8deSMichal Kubecek struct nlattr *nl_flags = nla_nest_start_noflag(msg, attr); 3240e665457SAlexander Aring int i; 3250e665457SAlexander Aring 3260e665457SAlexander Aring if (!nl_flags) 3270e665457SAlexander Aring return -ENOBUFS; 3280e665457SAlexander Aring 3290e665457SAlexander Aring i = 0; 3300e665457SAlexander Aring while (mask) { 3310e665457SAlexander Aring if ((mask & 1) && nla_put_flag(msg, i)) 3320e665457SAlexander Aring return -ENOBUFS; 3330e665457SAlexander Aring 3340e665457SAlexander Aring mask >>= 1; 3350e665457SAlexander Aring i++; 3360e665457SAlexander Aring } 3370e665457SAlexander Aring 3380e665457SAlexander Aring nla_nest_end(msg, nl_flags); 3390e665457SAlexander Aring return 0; 3400e665457SAlexander Aring } 3410e665457SAlexander Aring 3420e665457SAlexander Aring static int 343ca20ce20SAlexander Aring nl802154_send_wpan_phy_channels(struct cfg802154_registered_device *rdev, 344ca20ce20SAlexander Aring struct sk_buff *msg) 345ca20ce20SAlexander Aring { 346ca20ce20SAlexander Aring struct nlattr *nl_page; 347ca20ce20SAlexander Aring unsigned long page; 348ca20ce20SAlexander Aring 349ae0be8deSMichal Kubecek nl_page = nla_nest_start_noflag(msg, NL802154_ATTR_CHANNELS_SUPPORTED); 350ca20ce20SAlexander Aring if (!nl_page) 351ca20ce20SAlexander Aring return -ENOBUFS; 352ca20ce20SAlexander Aring 353cb41c8ddSAlexander Aring for (page = 0; page <= IEEE802154_MAX_PAGE; page++) { 354ca20ce20SAlexander Aring if (nla_put_u32(msg, NL802154_ATTR_SUPPORTED_CHANNEL, 35572f655e4SAlexander Aring rdev->wpan_phy.supported.channels[page])) 356ca20ce20SAlexander Aring return -ENOBUFS; 357ca20ce20SAlexander Aring } 358ca20ce20SAlexander Aring nla_nest_end(msg, nl_page); 359ca20ce20SAlexander Aring 360ca20ce20SAlexander Aring return 0; 361ca20ce20SAlexander Aring } 362ca20ce20SAlexander Aring 3630e665457SAlexander Aring static int 3640e665457SAlexander Aring nl802154_put_capabilities(struct sk_buff *msg, 3650e665457SAlexander Aring struct cfg802154_registered_device *rdev) 3660e665457SAlexander Aring { 3670e665457SAlexander Aring const struct wpan_phy_supported *caps = &rdev->wpan_phy.supported; 3680e665457SAlexander Aring struct nlattr *nl_caps, *nl_channels; 3690e665457SAlexander Aring int i; 3700e665457SAlexander Aring 371ae0be8deSMichal Kubecek nl_caps = nla_nest_start_noflag(msg, NL802154_ATTR_WPAN_PHY_CAPS); 3720e665457SAlexander Aring if (!nl_caps) 3730e665457SAlexander Aring return -ENOBUFS; 3740e665457SAlexander Aring 375ae0be8deSMichal Kubecek nl_channels = nla_nest_start_noflag(msg, NL802154_CAP_ATTR_CHANNELS); 3760e665457SAlexander Aring if (!nl_channels) 3770e665457SAlexander Aring return -ENOBUFS; 3780e665457SAlexander Aring 3790e665457SAlexander Aring for (i = 0; i <= IEEE802154_MAX_PAGE; i++) { 3800e665457SAlexander Aring if (caps->channels[i]) { 3810e665457SAlexander Aring if (nl802154_put_flags(msg, i, caps->channels[i])) 3820e665457SAlexander Aring return -ENOBUFS; 3830e665457SAlexander Aring } 3840e665457SAlexander Aring } 3850e665457SAlexander Aring 3860e665457SAlexander Aring nla_nest_end(msg, nl_channels); 3870e665457SAlexander Aring 3880e665457SAlexander Aring if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_ED_LEVEL) { 3890e665457SAlexander Aring struct nlattr *nl_ed_lvls; 3900e665457SAlexander Aring 391ae0be8deSMichal Kubecek nl_ed_lvls = nla_nest_start_noflag(msg, 3920e665457SAlexander Aring NL802154_CAP_ATTR_CCA_ED_LEVELS); 3930e665457SAlexander Aring if (!nl_ed_lvls) 3940e665457SAlexander Aring return -ENOBUFS; 3950e665457SAlexander Aring 3960e665457SAlexander Aring for (i = 0; i < caps->cca_ed_levels_size; i++) { 3970e665457SAlexander Aring if (nla_put_s32(msg, i, caps->cca_ed_levels[i])) 3980e665457SAlexander Aring return -ENOBUFS; 3990e665457SAlexander Aring } 4000e665457SAlexander Aring 4010e665457SAlexander Aring nla_nest_end(msg, nl_ed_lvls); 4020e665457SAlexander Aring } 4030e665457SAlexander Aring 4040e665457SAlexander Aring if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_TXPOWER) { 4050e665457SAlexander Aring struct nlattr *nl_tx_pwrs; 4060e665457SAlexander Aring 407ae0be8deSMichal Kubecek nl_tx_pwrs = nla_nest_start_noflag(msg, 408ae0be8deSMichal Kubecek NL802154_CAP_ATTR_TX_POWERS); 4090e665457SAlexander Aring if (!nl_tx_pwrs) 4100e665457SAlexander Aring return -ENOBUFS; 4110e665457SAlexander Aring 4120e665457SAlexander Aring for (i = 0; i < caps->tx_powers_size; i++) { 4130e665457SAlexander Aring if (nla_put_s32(msg, i, caps->tx_powers[i])) 4140e665457SAlexander Aring return -ENOBUFS; 4150e665457SAlexander Aring } 4160e665457SAlexander Aring 4170e665457SAlexander Aring nla_nest_end(msg, nl_tx_pwrs); 4180e665457SAlexander Aring } 4190e665457SAlexander Aring 4200e665457SAlexander Aring if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE) { 4210e665457SAlexander Aring if (nl802154_put_flags(msg, NL802154_CAP_ATTR_CCA_MODES, 4220e665457SAlexander Aring caps->cca_modes) || 4230e665457SAlexander Aring nl802154_put_flags(msg, NL802154_CAP_ATTR_CCA_OPTS, 4240e665457SAlexander Aring caps->cca_opts)) 4250e665457SAlexander Aring return -ENOBUFS; 4260e665457SAlexander Aring } 4270e665457SAlexander Aring 4280e665457SAlexander Aring if (nla_put_u8(msg, NL802154_CAP_ATTR_MIN_MINBE, caps->min_minbe) || 4290e665457SAlexander Aring nla_put_u8(msg, NL802154_CAP_ATTR_MAX_MINBE, caps->max_minbe) || 4300e665457SAlexander Aring nla_put_u8(msg, NL802154_CAP_ATTR_MIN_MAXBE, caps->min_maxbe) || 4310e665457SAlexander Aring nla_put_u8(msg, NL802154_CAP_ATTR_MAX_MAXBE, caps->max_maxbe) || 4320e665457SAlexander Aring nla_put_u8(msg, NL802154_CAP_ATTR_MIN_CSMA_BACKOFFS, 4330e665457SAlexander Aring caps->min_csma_backoffs) || 4340e665457SAlexander Aring nla_put_u8(msg, NL802154_CAP_ATTR_MAX_CSMA_BACKOFFS, 4350e665457SAlexander Aring caps->max_csma_backoffs) || 4360e665457SAlexander Aring nla_put_s8(msg, NL802154_CAP_ATTR_MIN_FRAME_RETRIES, 4370e665457SAlexander Aring caps->min_frame_retries) || 4380e665457SAlexander Aring nla_put_s8(msg, NL802154_CAP_ATTR_MAX_FRAME_RETRIES, 4390e665457SAlexander Aring caps->max_frame_retries) || 4400e665457SAlexander Aring nl802154_put_flags(msg, NL802154_CAP_ATTR_IFTYPES, 4410e665457SAlexander Aring caps->iftypes) || 4420e665457SAlexander Aring nla_put_u32(msg, NL802154_CAP_ATTR_LBT, caps->lbt)) 4430e665457SAlexander Aring return -ENOBUFS; 4440e665457SAlexander Aring 4450e665457SAlexander Aring nla_nest_end(msg, nl_caps); 4460e665457SAlexander Aring 4470e665457SAlexander Aring return 0; 4480e665457SAlexander Aring } 4490e665457SAlexander Aring 450ca20ce20SAlexander Aring static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev, 451ca20ce20SAlexander Aring enum nl802154_commands cmd, 452ca20ce20SAlexander Aring struct sk_buff *msg, u32 portid, u32 seq, 453ca20ce20SAlexander Aring int flags) 454ca20ce20SAlexander Aring { 455133be026SVarka Bhadram struct nlattr *nl_cmds; 456ca20ce20SAlexander Aring void *hdr; 457133be026SVarka Bhadram int i; 458ca20ce20SAlexander Aring 459ca20ce20SAlexander Aring hdr = nl802154hdr_put(msg, portid, seq, flags, cmd); 460ca20ce20SAlexander Aring if (!hdr) 461ca20ce20SAlexander Aring return -ENOBUFS; 462ca20ce20SAlexander Aring 463ca20ce20SAlexander Aring if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) || 464ca20ce20SAlexander Aring nla_put_string(msg, NL802154_ATTR_WPAN_PHY_NAME, 465ca20ce20SAlexander Aring wpan_phy_name(&rdev->wpan_phy)) || 466ca20ce20SAlexander Aring nla_put_u32(msg, NL802154_ATTR_GENERATION, 467ca20ce20SAlexander Aring cfg802154_rdev_list_generation)) 468ca20ce20SAlexander Aring goto nla_put_failure; 469ca20ce20SAlexander Aring 470ca20ce20SAlexander Aring if (cmd != NL802154_CMD_NEW_WPAN_PHY) 471ca20ce20SAlexander Aring goto finish; 472ca20ce20SAlexander Aring 473ca20ce20SAlexander Aring /* DUMP PHY PIB */ 474ca20ce20SAlexander Aring 475ca20ce20SAlexander Aring /* current channel settings */ 476ca20ce20SAlexander Aring if (nla_put_u8(msg, NL802154_ATTR_PAGE, 477ca20ce20SAlexander Aring rdev->wpan_phy.current_page) || 478ca20ce20SAlexander Aring nla_put_u8(msg, NL802154_ATTR_CHANNEL, 479ca20ce20SAlexander Aring rdev->wpan_phy.current_channel)) 480ca20ce20SAlexander Aring goto nla_put_failure; 481ca20ce20SAlexander Aring 4820e665457SAlexander Aring /* TODO remove this behaviour, we still keep support it for a while 4830e665457SAlexander Aring * so users can change the behaviour to the new one. 4840e665457SAlexander Aring */ 485ca20ce20SAlexander Aring if (nl802154_send_wpan_phy_channels(rdev, msg)) 486ca20ce20SAlexander Aring goto nla_put_failure; 487ca20ce20SAlexander Aring 488ca20ce20SAlexander Aring /* cca mode */ 489edea8f7cSAlexander Aring if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE) { 490ba2a9506SAlexander Aring if (nla_put_u32(msg, NL802154_ATTR_CCA_MODE, 4917fe9a388SAlexander Aring rdev->wpan_phy.cca.mode)) 492ca20ce20SAlexander Aring goto nla_put_failure; 493ca20ce20SAlexander Aring 494ba2a9506SAlexander Aring if (rdev->wpan_phy.cca.mode == NL802154_CCA_ENERGY_CARRIER) { 495ba2a9506SAlexander Aring if (nla_put_u32(msg, NL802154_ATTR_CCA_OPT, 496ba2a9506SAlexander Aring rdev->wpan_phy.cca.opt)) 497ba2a9506SAlexander Aring goto nla_put_failure; 498ba2a9506SAlexander Aring } 499edea8f7cSAlexander Aring } 500ba2a9506SAlexander Aring 501edea8f7cSAlexander Aring if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_TXPOWER) { 5021a19cb68SAlexander Aring if (nla_put_s32(msg, NL802154_ATTR_TX_POWER, 503ca20ce20SAlexander Aring rdev->wpan_phy.transmit_power)) 504ca20ce20SAlexander Aring goto nla_put_failure; 505edea8f7cSAlexander Aring } 506ca20ce20SAlexander Aring 507e4390592SAlexander Aring if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_ED_LEVEL) { 508e4390592SAlexander Aring if (nla_put_s32(msg, NL802154_ATTR_CCA_ED_LEVEL, 509e4390592SAlexander Aring rdev->wpan_phy.cca_ed_level)) 510e4390592SAlexander Aring goto nla_put_failure; 511e4390592SAlexander Aring } 512e4390592SAlexander Aring 5130e665457SAlexander Aring if (nl802154_put_capabilities(msg, rdev)) 5140e665457SAlexander Aring goto nla_put_failure; 5150e665457SAlexander Aring 516ae0be8deSMichal Kubecek nl_cmds = nla_nest_start_noflag(msg, NL802154_ATTR_SUPPORTED_COMMANDS); 517133be026SVarka Bhadram if (!nl_cmds) 518133be026SVarka Bhadram goto nla_put_failure; 519133be026SVarka Bhadram 520133be026SVarka Bhadram i = 0; 521133be026SVarka Bhadram #define CMD(op, n) \ 522133be026SVarka Bhadram do { \ 523133be026SVarka Bhadram if (rdev->ops->op) { \ 524133be026SVarka Bhadram i++; \ 525133be026SVarka Bhadram if (nla_put_u32(msg, i, NL802154_CMD_ ## n)) \ 526133be026SVarka Bhadram goto nla_put_failure; \ 527133be026SVarka Bhadram } \ 528133be026SVarka Bhadram } while (0) 529133be026SVarka Bhadram 530133be026SVarka Bhadram CMD(add_virtual_intf, NEW_INTERFACE); 531133be026SVarka Bhadram CMD(del_virtual_intf, DEL_INTERFACE); 532133be026SVarka Bhadram CMD(set_channel, SET_CHANNEL); 533133be026SVarka Bhadram CMD(set_pan_id, SET_PAN_ID); 534133be026SVarka Bhadram CMD(set_short_addr, SET_SHORT_ADDR); 535133be026SVarka Bhadram CMD(set_backoff_exponent, SET_BACKOFF_EXPONENT); 536133be026SVarka Bhadram CMD(set_max_csma_backoffs, SET_MAX_CSMA_BACKOFFS); 537133be026SVarka Bhadram CMD(set_max_frame_retries, SET_MAX_FRAME_RETRIES); 538133be026SVarka Bhadram CMD(set_lbt_mode, SET_LBT_MODE); 539c91208d8SAlexander Aring CMD(set_ackreq_default, SET_ACKREQ_DEFAULT); 540133be026SVarka Bhadram 541133be026SVarka Bhadram if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_TXPOWER) 542133be026SVarka Bhadram CMD(set_tx_power, SET_TX_POWER); 543133be026SVarka Bhadram 544133be026SVarka Bhadram if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_ED_LEVEL) 545133be026SVarka Bhadram CMD(set_cca_ed_level, SET_CCA_ED_LEVEL); 546133be026SVarka Bhadram 547133be026SVarka Bhadram if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE) 548133be026SVarka Bhadram CMD(set_cca_mode, SET_CCA_MODE); 549133be026SVarka Bhadram 550133be026SVarka Bhadram #undef CMD 551133be026SVarka Bhadram nla_nest_end(msg, nl_cmds); 552133be026SVarka Bhadram 553ca20ce20SAlexander Aring finish: 554053c095aSJohannes Berg genlmsg_end(msg, hdr); 555053c095aSJohannes Berg return 0; 556ca20ce20SAlexander Aring 557ca20ce20SAlexander Aring nla_put_failure: 558ca20ce20SAlexander Aring genlmsg_cancel(msg, hdr); 559ca20ce20SAlexander Aring return -EMSGSIZE; 560ca20ce20SAlexander Aring } 561ca20ce20SAlexander Aring 562ca20ce20SAlexander Aring struct nl802154_dump_wpan_phy_state { 563ca20ce20SAlexander Aring s64 filter_wpan_phy; 564ca20ce20SAlexander Aring long start; 565ca20ce20SAlexander Aring 566ca20ce20SAlexander Aring }; 567ca20ce20SAlexander Aring 568ca20ce20SAlexander Aring static int nl802154_dump_wpan_phy_parse(struct sk_buff *skb, 569ca20ce20SAlexander Aring struct netlink_callback *cb, 570ca20ce20SAlexander Aring struct nl802154_dump_wpan_phy_state *state) 571ca20ce20SAlexander Aring { 57275cdbdd0SJiri Pirko const struct genl_dumpit_info *info = genl_dumpit_info(cb); 57375cdbdd0SJiri Pirko struct nlattr **tb = info->attrs; 574ca20ce20SAlexander Aring 575ca20ce20SAlexander Aring if (tb[NL802154_ATTR_WPAN_PHY]) 576ca20ce20SAlexander Aring state->filter_wpan_phy = nla_get_u32(tb[NL802154_ATTR_WPAN_PHY]); 577ca20ce20SAlexander Aring if (tb[NL802154_ATTR_WPAN_DEV]) 578ca20ce20SAlexander Aring state->filter_wpan_phy = nla_get_u64(tb[NL802154_ATTR_WPAN_DEV]) >> 32; 579ca20ce20SAlexander Aring if (tb[NL802154_ATTR_IFINDEX]) { 580ca20ce20SAlexander Aring struct net_device *netdev; 581ca20ce20SAlexander Aring struct cfg802154_registered_device *rdev; 582ca20ce20SAlexander Aring int ifidx = nla_get_u32(tb[NL802154_ATTR_IFINDEX]); 583ca20ce20SAlexander Aring 584ca20ce20SAlexander Aring netdev = __dev_get_by_index(&init_net, ifidx); 585ca20ce20SAlexander Aring if (!netdev) 586ca20ce20SAlexander Aring return -ENODEV; 587ca20ce20SAlexander Aring if (netdev->ieee802154_ptr) { 588ca20ce20SAlexander Aring rdev = wpan_phy_to_rdev( 589ca20ce20SAlexander Aring netdev->ieee802154_ptr->wpan_phy); 590ca20ce20SAlexander Aring state->filter_wpan_phy = rdev->wpan_phy_idx; 591ca20ce20SAlexander Aring } 592ca20ce20SAlexander Aring } 593ca20ce20SAlexander Aring 594ca20ce20SAlexander Aring return 0; 595ca20ce20SAlexander Aring } 596ca20ce20SAlexander Aring 597ca20ce20SAlexander Aring static int 598ca20ce20SAlexander Aring nl802154_dump_wpan_phy(struct sk_buff *skb, struct netlink_callback *cb) 599ca20ce20SAlexander Aring { 600ca20ce20SAlexander Aring int idx = 0, ret; 601ca20ce20SAlexander Aring struct nl802154_dump_wpan_phy_state *state = (void *)cb->args[0]; 602ca20ce20SAlexander Aring struct cfg802154_registered_device *rdev; 603ca20ce20SAlexander Aring 604ca20ce20SAlexander Aring rtnl_lock(); 605ca20ce20SAlexander Aring if (!state) { 606ca20ce20SAlexander Aring state = kzalloc(sizeof(*state), GFP_KERNEL); 607ca20ce20SAlexander Aring if (!state) { 608ca20ce20SAlexander Aring rtnl_unlock(); 609ca20ce20SAlexander Aring return -ENOMEM; 610ca20ce20SAlexander Aring } 611ca20ce20SAlexander Aring state->filter_wpan_phy = -1; 612ca20ce20SAlexander Aring ret = nl802154_dump_wpan_phy_parse(skb, cb, state); 613ca20ce20SAlexander Aring if (ret) { 614ca20ce20SAlexander Aring kfree(state); 615ca20ce20SAlexander Aring rtnl_unlock(); 616ca20ce20SAlexander Aring return ret; 617ca20ce20SAlexander Aring } 618ca20ce20SAlexander Aring cb->args[0] = (long)state; 619ca20ce20SAlexander Aring } 620ca20ce20SAlexander Aring 621ca20ce20SAlexander Aring list_for_each_entry(rdev, &cfg802154_rdev_list, list) { 62266e5c267SAlexander Aring if (!net_eq(wpan_phy_net(&rdev->wpan_phy), sock_net(skb->sk))) 62366e5c267SAlexander Aring continue; 624ca20ce20SAlexander Aring if (++idx <= state->start) 625ca20ce20SAlexander Aring continue; 626ca20ce20SAlexander Aring if (state->filter_wpan_phy != -1 && 627ca20ce20SAlexander Aring state->filter_wpan_phy != rdev->wpan_phy_idx) 628ca20ce20SAlexander Aring continue; 629ca20ce20SAlexander Aring /* attempt to fit multiple wpan_phy data chunks into the skb */ 630ca20ce20SAlexander Aring ret = nl802154_send_wpan_phy(rdev, 631ca20ce20SAlexander Aring NL802154_CMD_NEW_WPAN_PHY, 632ca20ce20SAlexander Aring skb, 633ca20ce20SAlexander Aring NETLINK_CB(cb->skb).portid, 634ca20ce20SAlexander Aring cb->nlh->nlmsg_seq, NLM_F_MULTI); 635ca20ce20SAlexander Aring if (ret < 0) { 636ca20ce20SAlexander Aring if ((ret == -ENOBUFS || ret == -EMSGSIZE) && 637ca20ce20SAlexander Aring !skb->len && cb->min_dump_alloc < 4096) { 638ca20ce20SAlexander Aring cb->min_dump_alloc = 4096; 639ca20ce20SAlexander Aring rtnl_unlock(); 640ca20ce20SAlexander Aring return 1; 641ca20ce20SAlexander Aring } 642ca20ce20SAlexander Aring idx--; 643ca20ce20SAlexander Aring break; 644ca20ce20SAlexander Aring } 645ca20ce20SAlexander Aring break; 646ca20ce20SAlexander Aring } 647ca20ce20SAlexander Aring rtnl_unlock(); 648ca20ce20SAlexander Aring 649ca20ce20SAlexander Aring state->start = idx; 650ca20ce20SAlexander Aring 651ca20ce20SAlexander Aring return skb->len; 652ca20ce20SAlexander Aring } 653ca20ce20SAlexander Aring 654ca20ce20SAlexander Aring static int nl802154_dump_wpan_phy_done(struct netlink_callback *cb) 655ca20ce20SAlexander Aring { 656ca20ce20SAlexander Aring kfree((void *)cb->args[0]); 657ca20ce20SAlexander Aring return 0; 658ca20ce20SAlexander Aring } 659ca20ce20SAlexander Aring 660ca20ce20SAlexander Aring static int nl802154_get_wpan_phy(struct sk_buff *skb, struct genl_info *info) 661ca20ce20SAlexander Aring { 662ca20ce20SAlexander Aring struct sk_buff *msg; 663ca20ce20SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 664ca20ce20SAlexander Aring 665ca20ce20SAlexander Aring msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 666ca20ce20SAlexander Aring if (!msg) 667ca20ce20SAlexander Aring return -ENOMEM; 668ca20ce20SAlexander Aring 669ca20ce20SAlexander Aring if (nl802154_send_wpan_phy(rdev, NL802154_CMD_NEW_WPAN_PHY, msg, 670ca20ce20SAlexander Aring info->snd_portid, info->snd_seq, 0) < 0) { 671ca20ce20SAlexander Aring nlmsg_free(msg); 672ca20ce20SAlexander Aring return -ENOBUFS; 673ca20ce20SAlexander Aring } 674ca20ce20SAlexander Aring 675ca20ce20SAlexander Aring return genlmsg_reply(msg, info); 676ca20ce20SAlexander Aring } 677ca20ce20SAlexander Aring 6784b96aea0SAlexander Aring static inline u64 wpan_dev_id(struct wpan_dev *wpan_dev) 6794b96aea0SAlexander Aring { 6804b96aea0SAlexander Aring return (u64)wpan_dev->identifier | 6814b96aea0SAlexander Aring ((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32); 6824b96aea0SAlexander Aring } 6834b96aea0SAlexander Aring 684a26c5fd7SAlexander Aring #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL 685a26c5fd7SAlexander Aring #include <net/ieee802154_netdev.h> 686a26c5fd7SAlexander Aring 687a26c5fd7SAlexander Aring static int 688a26c5fd7SAlexander Aring ieee802154_llsec_send_key_id(struct sk_buff *msg, 689a26c5fd7SAlexander Aring const struct ieee802154_llsec_key_id *desc) 690a26c5fd7SAlexander Aring { 691a26c5fd7SAlexander Aring struct nlattr *nl_dev_addr; 692a26c5fd7SAlexander Aring 693a26c5fd7SAlexander Aring if (nla_put_u32(msg, NL802154_KEY_ID_ATTR_MODE, desc->mode)) 694a26c5fd7SAlexander Aring return -ENOBUFS; 695a26c5fd7SAlexander Aring 696a26c5fd7SAlexander Aring switch (desc->mode) { 697a26c5fd7SAlexander Aring case NL802154_KEY_ID_MODE_IMPLICIT: 698ae0be8deSMichal Kubecek nl_dev_addr = nla_nest_start_noflag(msg, 699ae0be8deSMichal Kubecek NL802154_KEY_ID_ATTR_IMPLICIT); 700a26c5fd7SAlexander Aring if (!nl_dev_addr) 701a26c5fd7SAlexander Aring return -ENOBUFS; 702a26c5fd7SAlexander Aring 703a26c5fd7SAlexander Aring if (nla_put_le16(msg, NL802154_DEV_ADDR_ATTR_PAN_ID, 704a26c5fd7SAlexander Aring desc->device_addr.pan_id) || 705a26c5fd7SAlexander Aring nla_put_u32(msg, NL802154_DEV_ADDR_ATTR_MODE, 706a26c5fd7SAlexander Aring desc->device_addr.mode)) 707a26c5fd7SAlexander Aring return -ENOBUFS; 708a26c5fd7SAlexander Aring 709a26c5fd7SAlexander Aring switch (desc->device_addr.mode) { 710a26c5fd7SAlexander Aring case NL802154_DEV_ADDR_SHORT: 711a26c5fd7SAlexander Aring if (nla_put_le16(msg, NL802154_DEV_ADDR_ATTR_SHORT, 712a26c5fd7SAlexander Aring desc->device_addr.short_addr)) 713a26c5fd7SAlexander Aring return -ENOBUFS; 714a26c5fd7SAlexander Aring break; 715a26c5fd7SAlexander Aring case NL802154_DEV_ADDR_EXTENDED: 716a26c5fd7SAlexander Aring if (nla_put_le64(msg, NL802154_DEV_ADDR_ATTR_EXTENDED, 717e7479122SNicolas Dichtel desc->device_addr.extended_addr, 718e7479122SNicolas Dichtel NL802154_DEV_ADDR_ATTR_PAD)) 719a26c5fd7SAlexander Aring return -ENOBUFS; 720a26c5fd7SAlexander Aring break; 721a26c5fd7SAlexander Aring default: 722a26c5fd7SAlexander Aring /* userspace should handle unknown */ 723a26c5fd7SAlexander Aring break; 724a26c5fd7SAlexander Aring } 725a26c5fd7SAlexander Aring 726a26c5fd7SAlexander Aring nla_nest_end(msg, nl_dev_addr); 727a26c5fd7SAlexander Aring break; 728a26c5fd7SAlexander Aring case NL802154_KEY_ID_MODE_INDEX: 729a26c5fd7SAlexander Aring break; 730a26c5fd7SAlexander Aring case NL802154_KEY_ID_MODE_INDEX_SHORT: 731a26c5fd7SAlexander Aring /* TODO renmae short_source? */ 732a26c5fd7SAlexander Aring if (nla_put_le32(msg, NL802154_KEY_ID_ATTR_SOURCE_SHORT, 733a26c5fd7SAlexander Aring desc->short_source)) 734a26c5fd7SAlexander Aring return -ENOBUFS; 735a26c5fd7SAlexander Aring break; 736a26c5fd7SAlexander Aring case NL802154_KEY_ID_MODE_INDEX_EXTENDED: 737a26c5fd7SAlexander Aring if (nla_put_le64(msg, NL802154_KEY_ID_ATTR_SOURCE_EXTENDED, 738e7479122SNicolas Dichtel desc->extended_source, 739e7479122SNicolas Dichtel NL802154_KEY_ID_ATTR_PAD)) 740a26c5fd7SAlexander Aring return -ENOBUFS; 741a26c5fd7SAlexander Aring break; 742a26c5fd7SAlexander Aring default: 743a26c5fd7SAlexander Aring /* userspace should handle unknown */ 744a26c5fd7SAlexander Aring break; 745a26c5fd7SAlexander Aring } 746a26c5fd7SAlexander Aring 747a26c5fd7SAlexander Aring /* TODO key_id to key_idx ? Check naming */ 748a26c5fd7SAlexander Aring if (desc->mode != NL802154_KEY_ID_MODE_IMPLICIT) { 749a26c5fd7SAlexander Aring if (nla_put_u8(msg, NL802154_KEY_ID_ATTR_INDEX, desc->id)) 750a26c5fd7SAlexander Aring return -ENOBUFS; 751a26c5fd7SAlexander Aring } 752a26c5fd7SAlexander Aring 753a26c5fd7SAlexander Aring return 0; 754a26c5fd7SAlexander Aring } 755a26c5fd7SAlexander Aring 756a26c5fd7SAlexander Aring static int nl802154_get_llsec_params(struct sk_buff *msg, 757a26c5fd7SAlexander Aring struct cfg802154_registered_device *rdev, 758a26c5fd7SAlexander Aring struct wpan_dev *wpan_dev) 759a26c5fd7SAlexander Aring { 760a26c5fd7SAlexander Aring struct nlattr *nl_key_id; 761a26c5fd7SAlexander Aring struct ieee802154_llsec_params params; 762a26c5fd7SAlexander Aring int ret; 763a26c5fd7SAlexander Aring 764a26c5fd7SAlexander Aring ret = rdev_get_llsec_params(rdev, wpan_dev, ¶ms); 765a26c5fd7SAlexander Aring if (ret < 0) 766a26c5fd7SAlexander Aring return ret; 767a26c5fd7SAlexander Aring 768a26c5fd7SAlexander Aring if (nla_put_u8(msg, NL802154_ATTR_SEC_ENABLED, params.enabled) || 769a26c5fd7SAlexander Aring nla_put_u32(msg, NL802154_ATTR_SEC_OUT_LEVEL, params.out_level) || 770a26c5fd7SAlexander Aring nla_put_be32(msg, NL802154_ATTR_SEC_FRAME_COUNTER, 771a26c5fd7SAlexander Aring params.frame_counter)) 772a26c5fd7SAlexander Aring return -ENOBUFS; 773a26c5fd7SAlexander Aring 774ae0be8deSMichal Kubecek nl_key_id = nla_nest_start_noflag(msg, NL802154_ATTR_SEC_OUT_KEY_ID); 775a26c5fd7SAlexander Aring if (!nl_key_id) 776a26c5fd7SAlexander Aring return -ENOBUFS; 777a26c5fd7SAlexander Aring 778a26c5fd7SAlexander Aring ret = ieee802154_llsec_send_key_id(msg, ¶ms.out_key); 779a26c5fd7SAlexander Aring if (ret < 0) 780a26c5fd7SAlexander Aring return ret; 781a26c5fd7SAlexander Aring 782a26c5fd7SAlexander Aring nla_nest_end(msg, nl_key_id); 783a26c5fd7SAlexander Aring 784a26c5fd7SAlexander Aring return 0; 785a26c5fd7SAlexander Aring } 786a26c5fd7SAlexander Aring #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ 787a26c5fd7SAlexander Aring 7884b96aea0SAlexander Aring static int 7894b96aea0SAlexander Aring nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags, 7904b96aea0SAlexander Aring struct cfg802154_registered_device *rdev, 7914b96aea0SAlexander Aring struct wpan_dev *wpan_dev) 7924b96aea0SAlexander Aring { 7934b96aea0SAlexander Aring struct net_device *dev = wpan_dev->netdev; 7944b96aea0SAlexander Aring void *hdr; 7954b96aea0SAlexander Aring 7964b96aea0SAlexander Aring hdr = nl802154hdr_put(msg, portid, seq, flags, 7974b96aea0SAlexander Aring NL802154_CMD_NEW_INTERFACE); 7984b96aea0SAlexander Aring if (!hdr) 7994b96aea0SAlexander Aring return -1; 8004b96aea0SAlexander Aring 8014b96aea0SAlexander Aring if (dev && 8024b96aea0SAlexander Aring (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex) || 8034b96aea0SAlexander Aring nla_put_string(msg, NL802154_ATTR_IFNAME, dev->name))) 8044b96aea0SAlexander Aring goto nla_put_failure; 8054b96aea0SAlexander Aring 8064b96aea0SAlexander Aring if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx) || 8074b96aea0SAlexander Aring nla_put_u32(msg, NL802154_ATTR_IFTYPE, wpan_dev->iftype) || 808a558da09SNicolas Dichtel nla_put_u64_64bit(msg, NL802154_ATTR_WPAN_DEV, 809a558da09SNicolas Dichtel wpan_dev_id(wpan_dev), NL802154_ATTR_PAD) || 8104b96aea0SAlexander Aring nla_put_u32(msg, NL802154_ATTR_GENERATION, 8114b96aea0SAlexander Aring rdev->devlist_generation ^ 8124b96aea0SAlexander Aring (cfg802154_rdev_list_generation << 2))) 8134b96aea0SAlexander Aring goto nla_put_failure; 8144b96aea0SAlexander Aring 8154b96aea0SAlexander Aring /* address settings */ 8164b96aea0SAlexander Aring if (nla_put_le64(msg, NL802154_ATTR_EXTENDED_ADDR, 817e7479122SNicolas Dichtel wpan_dev->extended_addr, 818e7479122SNicolas Dichtel NL802154_ATTR_PAD) || 8194b96aea0SAlexander Aring nla_put_le16(msg, NL802154_ATTR_SHORT_ADDR, 8204b96aea0SAlexander Aring wpan_dev->short_addr) || 8214b96aea0SAlexander Aring nla_put_le16(msg, NL802154_ATTR_PAN_ID, wpan_dev->pan_id)) 8224b96aea0SAlexander Aring goto nla_put_failure; 8234b96aea0SAlexander Aring 8244b96aea0SAlexander Aring /* ARET handling */ 8254b96aea0SAlexander Aring if (nla_put_s8(msg, NL802154_ATTR_MAX_FRAME_RETRIES, 8264b96aea0SAlexander Aring wpan_dev->frame_retries) || 8274b96aea0SAlexander Aring nla_put_u8(msg, NL802154_ATTR_MAX_BE, wpan_dev->max_be) || 8284b96aea0SAlexander Aring nla_put_u8(msg, NL802154_ATTR_MAX_CSMA_BACKOFFS, 8294b96aea0SAlexander Aring wpan_dev->csma_retries) || 8304b96aea0SAlexander Aring nla_put_u8(msg, NL802154_ATTR_MIN_BE, wpan_dev->min_be)) 8314b96aea0SAlexander Aring goto nla_put_failure; 8324b96aea0SAlexander Aring 8334b96aea0SAlexander Aring /* listen before transmit */ 8344b96aea0SAlexander Aring if (nla_put_u8(msg, NL802154_ATTR_LBT_MODE, wpan_dev->lbt)) 8354b96aea0SAlexander Aring goto nla_put_failure; 8364b96aea0SAlexander Aring 837c91208d8SAlexander Aring /* ackreq default behaviour */ 838c91208d8SAlexander Aring if (nla_put_u8(msg, NL802154_ATTR_ACKREQ_DEFAULT, wpan_dev->ackreq)) 839c91208d8SAlexander Aring goto nla_put_failure; 840c91208d8SAlexander Aring 841a26c5fd7SAlexander Aring #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL 8421534efc7SAlexander Aring if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) 8431534efc7SAlexander Aring goto out; 8441534efc7SAlexander Aring 845a26c5fd7SAlexander Aring if (nl802154_get_llsec_params(msg, rdev, wpan_dev) < 0) 846a26c5fd7SAlexander Aring goto nla_put_failure; 8471534efc7SAlexander Aring 8481534efc7SAlexander Aring out: 849a26c5fd7SAlexander Aring #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ 850a26c5fd7SAlexander Aring 851053c095aSJohannes Berg genlmsg_end(msg, hdr); 852053c095aSJohannes Berg return 0; 8534b96aea0SAlexander Aring 8544b96aea0SAlexander Aring nla_put_failure: 8554b96aea0SAlexander Aring genlmsg_cancel(msg, hdr); 8564b96aea0SAlexander Aring return -EMSGSIZE; 8574b96aea0SAlexander Aring } 8584b96aea0SAlexander Aring 8594b96aea0SAlexander Aring static int 8604b96aea0SAlexander Aring nl802154_dump_interface(struct sk_buff *skb, struct netlink_callback *cb) 8614b96aea0SAlexander Aring { 8624b96aea0SAlexander Aring int wp_idx = 0; 8634b96aea0SAlexander Aring int if_idx = 0; 8644b96aea0SAlexander Aring int wp_start = cb->args[0]; 8654b96aea0SAlexander Aring int if_start = cb->args[1]; 8664b96aea0SAlexander Aring struct cfg802154_registered_device *rdev; 8674b96aea0SAlexander Aring struct wpan_dev *wpan_dev; 8684b96aea0SAlexander Aring 8694b96aea0SAlexander Aring rtnl_lock(); 8704b96aea0SAlexander Aring list_for_each_entry(rdev, &cfg802154_rdev_list, list) { 87166e5c267SAlexander Aring if (!net_eq(wpan_phy_net(&rdev->wpan_phy), sock_net(skb->sk))) 87266e5c267SAlexander Aring continue; 8734b96aea0SAlexander Aring if (wp_idx < wp_start) { 8744b96aea0SAlexander Aring wp_idx++; 8754b96aea0SAlexander Aring continue; 8764b96aea0SAlexander Aring } 8774b96aea0SAlexander Aring if_idx = 0; 8784b96aea0SAlexander Aring 8794b96aea0SAlexander Aring list_for_each_entry(wpan_dev, &rdev->wpan_dev_list, list) { 8804b96aea0SAlexander Aring if (if_idx < if_start) { 8814b96aea0SAlexander Aring if_idx++; 8824b96aea0SAlexander Aring continue; 8834b96aea0SAlexander Aring } 8844b96aea0SAlexander Aring if (nl802154_send_iface(skb, NETLINK_CB(cb->skb).portid, 8854b96aea0SAlexander Aring cb->nlh->nlmsg_seq, NLM_F_MULTI, 8864b96aea0SAlexander Aring rdev, wpan_dev) < 0) { 8874b96aea0SAlexander Aring goto out; 8884b96aea0SAlexander Aring } 8894b96aea0SAlexander Aring if_idx++; 8904b96aea0SAlexander Aring } 8914b96aea0SAlexander Aring 8924b96aea0SAlexander Aring wp_idx++; 8934b96aea0SAlexander Aring } 8944b96aea0SAlexander Aring out: 8954b96aea0SAlexander Aring rtnl_unlock(); 8964b96aea0SAlexander Aring 8974b96aea0SAlexander Aring cb->args[0] = wp_idx; 8984b96aea0SAlexander Aring cb->args[1] = if_idx; 8994b96aea0SAlexander Aring 9004b96aea0SAlexander Aring return skb->len; 9014b96aea0SAlexander Aring } 9024b96aea0SAlexander Aring 9034b96aea0SAlexander Aring static int nl802154_get_interface(struct sk_buff *skb, struct genl_info *info) 9044b96aea0SAlexander Aring { 9054b96aea0SAlexander Aring struct sk_buff *msg; 9064b96aea0SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 9074b96aea0SAlexander Aring struct wpan_dev *wdev = info->user_ptr[1]; 9084b96aea0SAlexander Aring 9094b96aea0SAlexander Aring msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 9104b96aea0SAlexander Aring if (!msg) 9114b96aea0SAlexander Aring return -ENOMEM; 9124b96aea0SAlexander Aring 9134b96aea0SAlexander Aring if (nl802154_send_iface(msg, info->snd_portid, info->snd_seq, 0, 9144b96aea0SAlexander Aring rdev, wdev) < 0) { 9154b96aea0SAlexander Aring nlmsg_free(msg); 9164b96aea0SAlexander Aring return -ENOBUFS; 9174b96aea0SAlexander Aring } 9184b96aea0SAlexander Aring 9194b96aea0SAlexander Aring return genlmsg_reply(msg, info); 9204b96aea0SAlexander Aring } 9214b96aea0SAlexander Aring 922f3ea5e44SAlexander Aring static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info) 923f3ea5e44SAlexander Aring { 924f3ea5e44SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 925f3ea5e44SAlexander Aring enum nl802154_iftype type = NL802154_IFTYPE_UNSPEC; 9260e57547eSAlexander Aring __le64 extended_addr = cpu_to_le64(0x0000000000000000ULL); 927f3ea5e44SAlexander Aring 928f3ea5e44SAlexander Aring /* TODO avoid failing a new interface 929f3ea5e44SAlexander Aring * creation due to pending removal? 930f3ea5e44SAlexander Aring */ 931f3ea5e44SAlexander Aring 932f3ea5e44SAlexander Aring if (!info->attrs[NL802154_ATTR_IFNAME]) 933f3ea5e44SAlexander Aring return -EINVAL; 934f3ea5e44SAlexander Aring 935f3ea5e44SAlexander Aring if (info->attrs[NL802154_ATTR_IFTYPE]) { 936f3ea5e44SAlexander Aring type = nla_get_u32(info->attrs[NL802154_ATTR_IFTYPE]); 93765318680SAlexander Aring if (type > NL802154_IFTYPE_MAX || 93865318680SAlexander Aring !(rdev->wpan_phy.supported.iftypes & BIT(type))) 939f3ea5e44SAlexander Aring return -EINVAL; 940f3ea5e44SAlexander Aring } 941f3ea5e44SAlexander Aring 9420e57547eSAlexander Aring if (info->attrs[NL802154_ATTR_EXTENDED_ADDR]) 9431ee06ef1SAlexander Aring extended_addr = nla_get_le64(info->attrs[NL802154_ATTR_EXTENDED_ADDR]); 9440e57547eSAlexander Aring 945f3ea5e44SAlexander Aring if (!rdev->ops->add_virtual_intf) 946f3ea5e44SAlexander Aring return -EOPNOTSUPP; 947f3ea5e44SAlexander Aring 948f3ea5e44SAlexander Aring return rdev_add_virtual_intf(rdev, 949f3ea5e44SAlexander Aring nla_data(info->attrs[NL802154_ATTR_IFNAME]), 9505b4a1039SVarka Bhadram NET_NAME_USER, type, extended_addr); 951f3ea5e44SAlexander Aring } 952f3ea5e44SAlexander Aring 953b821ecd4SAlexander Aring static int nl802154_del_interface(struct sk_buff *skb, struct genl_info *info) 954b821ecd4SAlexander Aring { 955b821ecd4SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 956b821ecd4SAlexander Aring struct wpan_dev *wpan_dev = info->user_ptr[1]; 957b821ecd4SAlexander Aring 958b821ecd4SAlexander Aring if (!rdev->ops->del_virtual_intf) 959b821ecd4SAlexander Aring return -EOPNOTSUPP; 960b821ecd4SAlexander Aring 961b821ecd4SAlexander Aring /* If we remove a wpan device without a netdev then clear 962b821ecd4SAlexander Aring * user_ptr[1] so that nl802154_post_doit won't dereference it 963b821ecd4SAlexander Aring * to check if it needs to do dev_put(). Otherwise it crashes 964b821ecd4SAlexander Aring * since the wpan_dev has been freed, unlike with a netdev where 965b821ecd4SAlexander Aring * we need the dev_put() for the netdev to really be freed. 966b821ecd4SAlexander Aring */ 967b821ecd4SAlexander Aring if (!wpan_dev->netdev) 968b821ecd4SAlexander Aring info->user_ptr[1] = NULL; 969b821ecd4SAlexander Aring 970b821ecd4SAlexander Aring return rdev_del_virtual_intf(rdev, wpan_dev); 971b821ecd4SAlexander Aring } 972b821ecd4SAlexander Aring 973ab0bd561SAlexander Aring static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info) 974ab0bd561SAlexander Aring { 975ab0bd561SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 976ab0bd561SAlexander Aring u8 channel, page; 977ab0bd561SAlexander Aring 978ab0bd561SAlexander Aring if (!info->attrs[NL802154_ATTR_PAGE] || 979ab0bd561SAlexander Aring !info->attrs[NL802154_ATTR_CHANNEL]) 980ab0bd561SAlexander Aring return -EINVAL; 981ab0bd561SAlexander Aring 982ab0bd561SAlexander Aring page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]); 983ab0bd561SAlexander Aring channel = nla_get_u8(info->attrs[NL802154_ATTR_CHANNEL]); 984ab0bd561SAlexander Aring 985ab0bd561SAlexander Aring /* check 802.15.4 constraints */ 986d2aaf2a0SMiquel Raynal if (!ieee802154_chan_is_valid(&rdev->wpan_phy, page, channel)) 987ab0bd561SAlexander Aring return -EINVAL; 988ab0bd561SAlexander Aring 989ab0bd561SAlexander Aring return rdev_set_channel(rdev, page, channel); 990ab0bd561SAlexander Aring } 991ab0bd561SAlexander Aring 992ba2a9506SAlexander Aring static int nl802154_set_cca_mode(struct sk_buff *skb, struct genl_info *info) 993ba2a9506SAlexander Aring { 994ba2a9506SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 995ba2a9506SAlexander Aring struct wpan_phy_cca cca; 996ba2a9506SAlexander Aring 997fc4f8052SAlexander Aring if (!(rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE)) 998edea8f7cSAlexander Aring return -EOPNOTSUPP; 999edea8f7cSAlexander Aring 1000ba2a9506SAlexander Aring if (!info->attrs[NL802154_ATTR_CCA_MODE]) 1001ba2a9506SAlexander Aring return -EINVAL; 1002ba2a9506SAlexander Aring 1003ba2a9506SAlexander Aring cca.mode = nla_get_u32(info->attrs[NL802154_ATTR_CCA_MODE]); 1004ba2a9506SAlexander Aring /* checking 802.15.4 constraints */ 1005fea3318dSAlexander Aring if (cca.mode < NL802154_CCA_ENERGY || 1006fea3318dSAlexander Aring cca.mode > NL802154_CCA_ATTR_MAX || 1007fea3318dSAlexander Aring !(rdev->wpan_phy.supported.cca_modes & BIT(cca.mode))) 1008ba2a9506SAlexander Aring return -EINVAL; 1009ba2a9506SAlexander Aring 1010ba2a9506SAlexander Aring if (cca.mode == NL802154_CCA_ENERGY_CARRIER) { 1011ba2a9506SAlexander Aring if (!info->attrs[NL802154_ATTR_CCA_OPT]) 1012ba2a9506SAlexander Aring return -EINVAL; 1013ba2a9506SAlexander Aring 1014ba2a9506SAlexander Aring cca.opt = nla_get_u32(info->attrs[NL802154_ATTR_CCA_OPT]); 1015fea3318dSAlexander Aring if (cca.opt > NL802154_CCA_OPT_ATTR_MAX || 1016fea3318dSAlexander Aring !(rdev->wpan_phy.supported.cca_opts & BIT(cca.opt))) 1017ba2a9506SAlexander Aring return -EINVAL; 1018ba2a9506SAlexander Aring } 1019ba2a9506SAlexander Aring 1020ba2a9506SAlexander Aring return rdev_set_cca_mode(rdev, &cca); 1021ba2a9506SAlexander Aring } 1022ba2a9506SAlexander Aring 1023b69644c1SAlexander Aring static int nl802154_set_cca_ed_level(struct sk_buff *skb, struct genl_info *info) 1024b69644c1SAlexander Aring { 1025b69644c1SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 1026b69644c1SAlexander Aring s32 ed_level; 1027b69644c1SAlexander Aring int i; 1028b69644c1SAlexander Aring 1029b69644c1SAlexander Aring if (!(rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_ED_LEVEL)) 1030b69644c1SAlexander Aring return -EOPNOTSUPP; 1031b69644c1SAlexander Aring 1032b69644c1SAlexander Aring if (!info->attrs[NL802154_ATTR_CCA_ED_LEVEL]) 1033b69644c1SAlexander Aring return -EINVAL; 1034b69644c1SAlexander Aring 1035b69644c1SAlexander Aring ed_level = nla_get_s32(info->attrs[NL802154_ATTR_CCA_ED_LEVEL]); 1036b69644c1SAlexander Aring 1037b69644c1SAlexander Aring for (i = 0; i < rdev->wpan_phy.supported.cca_ed_levels_size; i++) { 1038b69644c1SAlexander Aring if (ed_level == rdev->wpan_phy.supported.cca_ed_levels[i]) 1039b69644c1SAlexander Aring return rdev_set_cca_ed_level(rdev, ed_level); 1040b69644c1SAlexander Aring } 1041b69644c1SAlexander Aring 1042b69644c1SAlexander Aring return -EINVAL; 1043b69644c1SAlexander Aring } 1044b69644c1SAlexander Aring 10450f999b09SVarka Bhadram static int nl802154_set_tx_power(struct sk_buff *skb, struct genl_info *info) 10460f999b09SVarka Bhadram { 10470f999b09SVarka Bhadram struct cfg802154_registered_device *rdev = info->user_ptr[0]; 10480f999b09SVarka Bhadram s32 power; 10490f999b09SVarka Bhadram int i; 10500f999b09SVarka Bhadram 10510f999b09SVarka Bhadram if (!(rdev->wpan_phy.flags & WPAN_PHY_FLAG_TXPOWER)) 10520f999b09SVarka Bhadram return -EOPNOTSUPP; 10530f999b09SVarka Bhadram 10540f999b09SVarka Bhadram if (!info->attrs[NL802154_ATTR_TX_POWER]) 10550f999b09SVarka Bhadram return -EINVAL; 10560f999b09SVarka Bhadram 10570f999b09SVarka Bhadram power = nla_get_s32(info->attrs[NL802154_ATTR_TX_POWER]); 10580f999b09SVarka Bhadram 10590f999b09SVarka Bhadram for (i = 0; i < rdev->wpan_phy.supported.tx_powers_size; i++) { 10600f999b09SVarka Bhadram if (power == rdev->wpan_phy.supported.tx_powers[i]) 10610f999b09SVarka Bhadram return rdev_set_tx_power(rdev, power); 10620f999b09SVarka Bhadram } 10630f999b09SVarka Bhadram 10640f999b09SVarka Bhadram return -EINVAL; 10650f999b09SVarka Bhadram } 10660f999b09SVarka Bhadram 1067702bf371SAlexander Aring static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info) 1068702bf371SAlexander Aring { 1069702bf371SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 1070702bf371SAlexander Aring struct net_device *dev = info->user_ptr[1]; 1071702bf371SAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 1072ee7b9053SAlexander Aring __le16 pan_id; 1073702bf371SAlexander Aring 1074702bf371SAlexander Aring /* conflict here while tx/rx calls */ 1075702bf371SAlexander Aring if (netif_running(dev)) 1076702bf371SAlexander Aring return -EBUSY; 1077702bf371SAlexander Aring 10789e3b71f3SAlexander Aring if (wpan_dev->lowpan_dev) { 10799e3b71f3SAlexander Aring if (netif_running(wpan_dev->lowpan_dev)) 10809e3b71f3SAlexander Aring return -EBUSY; 10819e3b71f3SAlexander Aring } 10829e3b71f3SAlexander Aring 1083702bf371SAlexander Aring /* don't change address fields on monitor */ 10840cf0879aSAlexander Aring if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR || 10850cf0879aSAlexander Aring !info->attrs[NL802154_ATTR_PAN_ID]) 1086702bf371SAlexander Aring return -EINVAL; 1087702bf371SAlexander Aring 1088ee7b9053SAlexander Aring pan_id = nla_get_le16(info->attrs[NL802154_ATTR_PAN_ID]); 1089702bf371SAlexander Aring 1090673692faSAlexander Aring /* TODO 1091673692faSAlexander Aring * I am not sure about to check here on broadcast pan_id. 1092673692faSAlexander Aring * Broadcast is a valid setting, comment from 802.15.4: 1093673692faSAlexander Aring * If this value is 0xffff, the device is not associated. 1094673692faSAlexander Aring * 1095673692faSAlexander Aring * This could useful to simple deassociate an device. 1096673692faSAlexander Aring */ 1097673692faSAlexander Aring if (pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST)) 1098673692faSAlexander Aring return -EINVAL; 1099673692faSAlexander Aring 1100702bf371SAlexander Aring return rdev_set_pan_id(rdev, wpan_dev, pan_id); 1101702bf371SAlexander Aring } 1102702bf371SAlexander Aring 11039830c62aSAlexander Aring static int nl802154_set_short_addr(struct sk_buff *skb, struct genl_info *info) 11049830c62aSAlexander Aring { 11059830c62aSAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 11069830c62aSAlexander Aring struct net_device *dev = info->user_ptr[1]; 11079830c62aSAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 1108ee7b9053SAlexander Aring __le16 short_addr; 11099830c62aSAlexander Aring 11109830c62aSAlexander Aring /* conflict here while tx/rx calls */ 11119830c62aSAlexander Aring if (netif_running(dev)) 11129830c62aSAlexander Aring return -EBUSY; 11139830c62aSAlexander Aring 11149e3b71f3SAlexander Aring if (wpan_dev->lowpan_dev) { 11159e3b71f3SAlexander Aring if (netif_running(wpan_dev->lowpan_dev)) 11169e3b71f3SAlexander Aring return -EBUSY; 11179e3b71f3SAlexander Aring } 11189e3b71f3SAlexander Aring 11199830c62aSAlexander Aring /* don't change address fields on monitor */ 11200cf0879aSAlexander Aring if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR || 11210cf0879aSAlexander Aring !info->attrs[NL802154_ATTR_SHORT_ADDR]) 11229830c62aSAlexander Aring return -EINVAL; 11239830c62aSAlexander Aring 1124ee7b9053SAlexander Aring short_addr = nla_get_le16(info->attrs[NL802154_ATTR_SHORT_ADDR]); 11259830c62aSAlexander Aring 1126673692faSAlexander Aring /* TODO 1127673692faSAlexander Aring * I am not sure about to check here on broadcast short_addr. 1128673692faSAlexander Aring * Broadcast is a valid setting, comment from 802.15.4: 1129673692faSAlexander Aring * A value of 0xfffe indicates that the device has 1130673692faSAlexander Aring * associated but has not been allocated an address. A 1131673692faSAlexander Aring * value of 0xffff indicates that the device does not 1132673692faSAlexander Aring * have a short address. 1133673692faSAlexander Aring * 1134673692faSAlexander Aring * I think we should allow to set these settings but 1135673692faSAlexander Aring * don't allow to allow socket communication with it. 1136673692faSAlexander Aring */ 1137673692faSAlexander Aring if (short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC) || 1138673692faSAlexander Aring short_addr == cpu_to_le16(IEEE802154_ADDR_SHORT_BROADCAST)) 1139673692faSAlexander Aring return -EINVAL; 1140673692faSAlexander Aring 11419830c62aSAlexander Aring return rdev_set_short_addr(rdev, wpan_dev, short_addr); 11429830c62aSAlexander Aring } 11439830c62aSAlexander Aring 1144656a999eSAlexander Aring static int 1145656a999eSAlexander Aring nl802154_set_backoff_exponent(struct sk_buff *skb, struct genl_info *info) 1146656a999eSAlexander Aring { 1147656a999eSAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 1148656a999eSAlexander Aring struct net_device *dev = info->user_ptr[1]; 1149656a999eSAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 1150656a999eSAlexander Aring u8 min_be, max_be; 1151656a999eSAlexander Aring 1152656a999eSAlexander Aring /* should be set on netif open inside phy settings */ 1153656a999eSAlexander Aring if (netif_running(dev)) 1154656a999eSAlexander Aring return -EBUSY; 1155656a999eSAlexander Aring 1156656a999eSAlexander Aring if (!info->attrs[NL802154_ATTR_MIN_BE] || 1157656a999eSAlexander Aring !info->attrs[NL802154_ATTR_MAX_BE]) 1158656a999eSAlexander Aring return -EINVAL; 1159656a999eSAlexander Aring 1160656a999eSAlexander Aring min_be = nla_get_u8(info->attrs[NL802154_ATTR_MIN_BE]); 1161656a999eSAlexander Aring max_be = nla_get_u8(info->attrs[NL802154_ATTR_MAX_BE]); 1162656a999eSAlexander Aring 1163656a999eSAlexander Aring /* check 802.15.4 constraints */ 1164fea3318dSAlexander Aring if (min_be < rdev->wpan_phy.supported.min_minbe || 1165fea3318dSAlexander Aring min_be > rdev->wpan_phy.supported.max_minbe || 1166fea3318dSAlexander Aring max_be < rdev->wpan_phy.supported.min_maxbe || 1167fea3318dSAlexander Aring max_be > rdev->wpan_phy.supported.max_maxbe || 1168fea3318dSAlexander Aring min_be > max_be) 1169656a999eSAlexander Aring return -EINVAL; 1170656a999eSAlexander Aring 1171656a999eSAlexander Aring return rdev_set_backoff_exponent(rdev, wpan_dev, min_be, max_be); 1172656a999eSAlexander Aring } 1173656a999eSAlexander Aring 1174a01ba765SAlexander Aring static int 1175a01ba765SAlexander Aring nl802154_set_max_csma_backoffs(struct sk_buff *skb, struct genl_info *info) 1176a01ba765SAlexander Aring { 1177a01ba765SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 1178a01ba765SAlexander Aring struct net_device *dev = info->user_ptr[1]; 1179a01ba765SAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 1180a01ba765SAlexander Aring u8 max_csma_backoffs; 1181a01ba765SAlexander Aring 1182a01ba765SAlexander Aring /* conflict here while other running iface settings */ 1183a01ba765SAlexander Aring if (netif_running(dev)) 1184a01ba765SAlexander Aring return -EBUSY; 1185a01ba765SAlexander Aring 1186a01ba765SAlexander Aring if (!info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]) 1187a01ba765SAlexander Aring return -EINVAL; 1188a01ba765SAlexander Aring 1189a01ba765SAlexander Aring max_csma_backoffs = nla_get_u8( 1190a01ba765SAlexander Aring info->attrs[NL802154_ATTR_MAX_CSMA_BACKOFFS]); 1191a01ba765SAlexander Aring 1192a01ba765SAlexander Aring /* check 802.15.4 constraints */ 1193fea3318dSAlexander Aring if (max_csma_backoffs < rdev->wpan_phy.supported.min_csma_backoffs || 1194fea3318dSAlexander Aring max_csma_backoffs > rdev->wpan_phy.supported.max_csma_backoffs) 1195a01ba765SAlexander Aring return -EINVAL; 1196a01ba765SAlexander Aring 1197a01ba765SAlexander Aring return rdev_set_max_csma_backoffs(rdev, wpan_dev, max_csma_backoffs); 1198a01ba765SAlexander Aring } 1199a01ba765SAlexander Aring 120017a3a46bSAlexander Aring static int 120117a3a46bSAlexander Aring nl802154_set_max_frame_retries(struct sk_buff *skb, struct genl_info *info) 120217a3a46bSAlexander Aring { 120317a3a46bSAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 120417a3a46bSAlexander Aring struct net_device *dev = info->user_ptr[1]; 120517a3a46bSAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 120617a3a46bSAlexander Aring s8 max_frame_retries; 120717a3a46bSAlexander Aring 120817a3a46bSAlexander Aring if (netif_running(dev)) 120917a3a46bSAlexander Aring return -EBUSY; 121017a3a46bSAlexander Aring 121117a3a46bSAlexander Aring if (!info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]) 121217a3a46bSAlexander Aring return -EINVAL; 121317a3a46bSAlexander Aring 121417a3a46bSAlexander Aring max_frame_retries = nla_get_s8( 121517a3a46bSAlexander Aring info->attrs[NL802154_ATTR_MAX_FRAME_RETRIES]); 121617a3a46bSAlexander Aring 121717a3a46bSAlexander Aring /* check 802.15.4 constraints */ 1218fea3318dSAlexander Aring if (max_frame_retries < rdev->wpan_phy.supported.min_frame_retries || 1219fea3318dSAlexander Aring max_frame_retries > rdev->wpan_phy.supported.max_frame_retries) 122017a3a46bSAlexander Aring return -EINVAL; 122117a3a46bSAlexander Aring 122217a3a46bSAlexander Aring return rdev_set_max_frame_retries(rdev, wpan_dev, max_frame_retries); 122317a3a46bSAlexander Aring } 122417a3a46bSAlexander Aring 1225c8937a1dSAlexander Aring static int nl802154_set_lbt_mode(struct sk_buff *skb, struct genl_info *info) 1226c8937a1dSAlexander Aring { 1227c8937a1dSAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 1228c8937a1dSAlexander Aring struct net_device *dev = info->user_ptr[1]; 1229c8937a1dSAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 12304e1795deSStefan Schmidt int mode; 1231c8937a1dSAlexander Aring 1232c8937a1dSAlexander Aring if (netif_running(dev)) 1233c8937a1dSAlexander Aring return -EBUSY; 1234c8937a1dSAlexander Aring 1235c8937a1dSAlexander Aring if (!info->attrs[NL802154_ATTR_LBT_MODE]) 1236c8937a1dSAlexander Aring return -EINVAL; 1237c8937a1dSAlexander Aring 12384e1795deSStefan Schmidt mode = nla_get_u8(info->attrs[NL802154_ATTR_LBT_MODE]); 12394e1795deSStefan Schmidt 12404e1795deSStefan Schmidt if (mode != 0 && mode != 1) 12414e1795deSStefan Schmidt return -EINVAL; 12424e1795deSStefan Schmidt 1243fea3318dSAlexander Aring if (!wpan_phy_supported_bool(mode, rdev->wpan_phy.supported.lbt)) 1244fea3318dSAlexander Aring return -EINVAL; 1245fea3318dSAlexander Aring 1246c8937a1dSAlexander Aring return rdev_set_lbt_mode(rdev, wpan_dev, mode); 1247c8937a1dSAlexander Aring } 1248c8937a1dSAlexander Aring 1249c91208d8SAlexander Aring static int 1250c91208d8SAlexander Aring nl802154_set_ackreq_default(struct sk_buff *skb, struct genl_info *info) 1251c91208d8SAlexander Aring { 1252c91208d8SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 1253c91208d8SAlexander Aring struct net_device *dev = info->user_ptr[1]; 1254c91208d8SAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 12554e1795deSStefan Schmidt int ackreq; 1256c91208d8SAlexander Aring 1257c91208d8SAlexander Aring if (netif_running(dev)) 1258c91208d8SAlexander Aring return -EBUSY; 1259c91208d8SAlexander Aring 1260c91208d8SAlexander Aring if (!info->attrs[NL802154_ATTR_ACKREQ_DEFAULT]) 1261c91208d8SAlexander Aring return -EINVAL; 1262c91208d8SAlexander Aring 12634e1795deSStefan Schmidt ackreq = nla_get_u8(info->attrs[NL802154_ATTR_ACKREQ_DEFAULT]); 12644e1795deSStefan Schmidt 12654e1795deSStefan Schmidt if (ackreq != 0 && ackreq != 1) 12664e1795deSStefan Schmidt return -EINVAL; 12674e1795deSStefan Schmidt 1268c91208d8SAlexander Aring return rdev_set_ackreq_default(rdev, wpan_dev, ackreq); 1269c91208d8SAlexander Aring } 1270c91208d8SAlexander Aring 127166e5c267SAlexander Aring static int nl802154_wpan_phy_netns(struct sk_buff *skb, struct genl_info *info) 127266e5c267SAlexander Aring { 127366e5c267SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 127466e5c267SAlexander Aring struct net *net; 127566e5c267SAlexander Aring int err; 127666e5c267SAlexander Aring 127766e5c267SAlexander Aring if (info->attrs[NL802154_ATTR_PID]) { 127866e5c267SAlexander Aring u32 pid = nla_get_u32(info->attrs[NL802154_ATTR_PID]); 127966e5c267SAlexander Aring 128066e5c267SAlexander Aring net = get_net_ns_by_pid(pid); 128166e5c267SAlexander Aring } else if (info->attrs[NL802154_ATTR_NETNS_FD]) { 128266e5c267SAlexander Aring u32 fd = nla_get_u32(info->attrs[NL802154_ATTR_NETNS_FD]); 128366e5c267SAlexander Aring 128466e5c267SAlexander Aring net = get_net_ns_by_fd(fd); 128566e5c267SAlexander Aring } else { 128666e5c267SAlexander Aring return -EINVAL; 128766e5c267SAlexander Aring } 128866e5c267SAlexander Aring 128966e5c267SAlexander Aring if (IS_ERR(net)) 129066e5c267SAlexander Aring return PTR_ERR(net); 129166e5c267SAlexander Aring 129266e5c267SAlexander Aring err = 0; 129366e5c267SAlexander Aring 129466e5c267SAlexander Aring /* check if anything to do */ 129566e5c267SAlexander Aring if (!net_eq(wpan_phy_net(&rdev->wpan_phy), net)) 129666e5c267SAlexander Aring err = cfg802154_switch_netns(rdev, net); 129766e5c267SAlexander Aring 129866e5c267SAlexander Aring put_net(net); 129966e5c267SAlexander Aring return err; 130066e5c267SAlexander Aring } 130166e5c267SAlexander Aring 130251147284SMiquel Raynal static int nl802154_prep_scan_event_msg(struct sk_buff *msg, 130351147284SMiquel Raynal struct cfg802154_registered_device *rdev, 130451147284SMiquel Raynal struct wpan_dev *wpan_dev, 130551147284SMiquel Raynal u32 portid, u32 seq, int flags, u8 cmd, 130651147284SMiquel Raynal struct ieee802154_coord_desc *desc) 130751147284SMiquel Raynal { 130851147284SMiquel Raynal struct nlattr *nla; 130951147284SMiquel Raynal void *hdr; 131051147284SMiquel Raynal 131151147284SMiquel Raynal hdr = nl802154hdr_put(msg, portid, seq, flags, cmd); 131251147284SMiquel Raynal if (!hdr) 131351147284SMiquel Raynal return -ENOBUFS; 131451147284SMiquel Raynal 131551147284SMiquel Raynal if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx)) 131651147284SMiquel Raynal goto nla_put_failure; 131751147284SMiquel Raynal 131851147284SMiquel Raynal if (wpan_dev->netdev && 131951147284SMiquel Raynal nla_put_u32(msg, NL802154_ATTR_IFINDEX, wpan_dev->netdev->ifindex)) 132051147284SMiquel Raynal goto nla_put_failure; 132151147284SMiquel Raynal 132251147284SMiquel Raynal if (nla_put_u64_64bit(msg, NL802154_ATTR_WPAN_DEV, 132351147284SMiquel Raynal wpan_dev_id(wpan_dev), NL802154_ATTR_PAD)) 132451147284SMiquel Raynal goto nla_put_failure; 132551147284SMiquel Raynal 132651147284SMiquel Raynal nla = nla_nest_start_noflag(msg, NL802154_ATTR_COORDINATOR); 132751147284SMiquel Raynal if (!nla) 132851147284SMiquel Raynal goto nla_put_failure; 132951147284SMiquel Raynal 133051147284SMiquel Raynal if (nla_put(msg, NL802154_COORD_PANID, IEEE802154_PAN_ID_LEN, 133151147284SMiquel Raynal &desc->addr.pan_id)) 133251147284SMiquel Raynal goto nla_put_failure; 133351147284SMiquel Raynal 133451147284SMiquel Raynal if (desc->addr.mode == IEEE802154_ADDR_SHORT) { 133551147284SMiquel Raynal if (nla_put(msg, NL802154_COORD_ADDR, 133651147284SMiquel Raynal IEEE802154_SHORT_ADDR_LEN, 133751147284SMiquel Raynal &desc->addr.short_addr)) 133851147284SMiquel Raynal goto nla_put_failure; 133951147284SMiquel Raynal } else { 134051147284SMiquel Raynal if (nla_put(msg, NL802154_COORD_ADDR, 134151147284SMiquel Raynal IEEE802154_EXTENDED_ADDR_LEN, 134251147284SMiquel Raynal &desc->addr.extended_addr)) 134351147284SMiquel Raynal goto nla_put_failure; 134451147284SMiquel Raynal } 134551147284SMiquel Raynal 134651147284SMiquel Raynal if (nla_put_u8(msg, NL802154_COORD_CHANNEL, desc->channel)) 134751147284SMiquel Raynal goto nla_put_failure; 134851147284SMiquel Raynal 134951147284SMiquel Raynal if (nla_put_u8(msg, NL802154_COORD_PAGE, desc->page)) 135051147284SMiquel Raynal goto nla_put_failure; 135151147284SMiquel Raynal 135251147284SMiquel Raynal if (nla_put_u16(msg, NL802154_COORD_SUPERFRAME_SPEC, 135351147284SMiquel Raynal desc->superframe_spec)) 135451147284SMiquel Raynal goto nla_put_failure; 135551147284SMiquel Raynal 135651147284SMiquel Raynal if (nla_put_u8(msg, NL802154_COORD_LINK_QUALITY, desc->link_quality)) 135751147284SMiquel Raynal goto nla_put_failure; 135851147284SMiquel Raynal 135951147284SMiquel Raynal if (desc->gts_permit && nla_put_flag(msg, NL802154_COORD_GTS_PERMIT)) 136051147284SMiquel Raynal goto nla_put_failure; 136151147284SMiquel Raynal 136251147284SMiquel Raynal /* TODO: NL802154_COORD_PAYLOAD_DATA if any */ 136351147284SMiquel Raynal 136451147284SMiquel Raynal nla_nest_end(msg, nla); 136551147284SMiquel Raynal 136651147284SMiquel Raynal genlmsg_end(msg, hdr); 136751147284SMiquel Raynal 136851147284SMiquel Raynal return 0; 136951147284SMiquel Raynal 137051147284SMiquel Raynal nla_put_failure: 137151147284SMiquel Raynal genlmsg_cancel(msg, hdr); 137251147284SMiquel Raynal 137351147284SMiquel Raynal return -EMSGSIZE; 137451147284SMiquel Raynal } 137551147284SMiquel Raynal 137651147284SMiquel Raynal int nl802154_scan_event(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, 137751147284SMiquel Raynal struct ieee802154_coord_desc *desc) 137851147284SMiquel Raynal { 137951147284SMiquel Raynal struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(wpan_phy); 138051147284SMiquel Raynal struct sk_buff *msg; 138151147284SMiquel Raynal int ret; 138251147284SMiquel Raynal 138351147284SMiquel Raynal msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 138451147284SMiquel Raynal if (!msg) 138551147284SMiquel Raynal return -ENOMEM; 138651147284SMiquel Raynal 138751147284SMiquel Raynal ret = nl802154_prep_scan_event_msg(msg, rdev, wpan_dev, 0, 0, 0, 138851147284SMiquel Raynal NL802154_CMD_SCAN_EVENT, 138951147284SMiquel Raynal desc); 139051147284SMiquel Raynal if (ret < 0) { 139151147284SMiquel Raynal nlmsg_free(msg); 139251147284SMiquel Raynal return ret; 139351147284SMiquel Raynal } 139451147284SMiquel Raynal 139551147284SMiquel Raynal return genlmsg_multicast_netns(&nl802154_fam, wpan_phy_net(wpan_phy), 139651147284SMiquel Raynal msg, 0, NL802154_MCGRP_SCAN, GFP_ATOMIC); 139751147284SMiquel Raynal } 139851147284SMiquel Raynal EXPORT_SYMBOL_GPL(nl802154_scan_event); 139951147284SMiquel Raynal 1400ed3557c9SMiquel Raynal static int nl802154_trigger_scan(struct sk_buff *skb, struct genl_info *info) 1401ed3557c9SMiquel Raynal { 1402ed3557c9SMiquel Raynal struct cfg802154_registered_device *rdev = info->user_ptr[0]; 1403ed3557c9SMiquel Raynal struct net_device *dev = info->user_ptr[1]; 1404ed3557c9SMiquel Raynal struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 1405ed3557c9SMiquel Raynal struct wpan_phy *wpan_phy = &rdev->wpan_phy; 1406ed3557c9SMiquel Raynal struct cfg802154_scan_request *request; 1407ed3557c9SMiquel Raynal u8 type; 1408ed3557c9SMiquel Raynal int err; 1409ed3557c9SMiquel Raynal 1410a0b61066SMiquel Raynal if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) { 1411a0b61066SMiquel Raynal NL_SET_ERR_MSG(info->extack, "Monitors are not allowed to perform scans"); 1412*1edecbd0SMiquel Raynal return -EOPNOTSUPP; 1413a0b61066SMiquel Raynal } 1414a0b61066SMiquel Raynal 1415a0b61066SMiquel Raynal if (!nla_get_u8(info->attrs[NL802154_ATTR_SCAN_TYPE])) { 1416a0b61066SMiquel Raynal NL_SET_ERR_MSG(info->extack, "Malformed request, missing scan type"); 1417a0b61066SMiquel Raynal return -EINVAL; 1418a0b61066SMiquel Raynal } 1419ed3557c9SMiquel Raynal 1420ed3557c9SMiquel Raynal request = kzalloc(sizeof(*request), GFP_KERNEL); 1421ed3557c9SMiquel Raynal if (!request) 1422ed3557c9SMiquel Raynal return -ENOMEM; 1423ed3557c9SMiquel Raynal 1424ed3557c9SMiquel Raynal request->wpan_dev = wpan_dev; 1425ed3557c9SMiquel Raynal request->wpan_phy = wpan_phy; 1426ed3557c9SMiquel Raynal 1427ed3557c9SMiquel Raynal type = nla_get_u8(info->attrs[NL802154_ATTR_SCAN_TYPE]); 1428ed3557c9SMiquel Raynal switch (type) { 1429ed3557c9SMiquel Raynal case NL802154_SCAN_PASSIVE: 1430ed3557c9SMiquel Raynal request->type = type; 1431ed3557c9SMiquel Raynal break; 1432ed3557c9SMiquel Raynal default: 1433a0b61066SMiquel Raynal NL_SET_ERR_MSG_FMT(info->extack, "Unsupported scan type: %d", type); 1434ed3557c9SMiquel Raynal err = -EINVAL; 1435ed3557c9SMiquel Raynal goto free_request; 1436ed3557c9SMiquel Raynal } 1437ed3557c9SMiquel Raynal 1438ed3557c9SMiquel Raynal /* Use current page by default */ 1439648324c9SMiquel Raynal if (info->attrs[NL802154_ATTR_PAGE]) 1440648324c9SMiquel Raynal request->page = nla_get_u8(info->attrs[NL802154_ATTR_PAGE]); 1441648324c9SMiquel Raynal else 1442ed3557c9SMiquel Raynal request->page = wpan_phy->current_page; 1443ed3557c9SMiquel Raynal 1444ed3557c9SMiquel Raynal /* Scan all supported channels by default */ 1445648324c9SMiquel Raynal if (info->attrs[NL802154_ATTR_SCAN_CHANNELS]) 1446648324c9SMiquel Raynal request->channels = nla_get_u32(info->attrs[NL802154_ATTR_SCAN_CHANNELS]); 1447648324c9SMiquel Raynal else 1448ed3557c9SMiquel Raynal request->channels = wpan_phy->supported.channels[request->page]; 1449ed3557c9SMiquel Raynal 1450ed3557c9SMiquel Raynal /* Use maximum duration order by default */ 1451648324c9SMiquel Raynal if (info->attrs[NL802154_ATTR_SCAN_DURATION]) 1452648324c9SMiquel Raynal request->duration = nla_get_u8(info->attrs[NL802154_ATTR_SCAN_DURATION]); 1453648324c9SMiquel Raynal else 1454ed3557c9SMiquel Raynal request->duration = IEEE802154_MAX_SCAN_DURATION; 1455ed3557c9SMiquel Raynal 1456ed3557c9SMiquel Raynal if (wpan_dev->netdev) 1457ed3557c9SMiquel Raynal dev_hold(wpan_dev->netdev); 1458ed3557c9SMiquel Raynal 1459ed3557c9SMiquel Raynal err = rdev_trigger_scan(rdev, request); 1460ed3557c9SMiquel Raynal if (err) { 1461ed3557c9SMiquel Raynal pr_err("Failure starting scanning (%d)\n", err); 1462ed3557c9SMiquel Raynal goto free_device; 1463ed3557c9SMiquel Raynal } 1464ed3557c9SMiquel Raynal 1465ed3557c9SMiquel Raynal return 0; 1466ed3557c9SMiquel Raynal 1467ed3557c9SMiquel Raynal free_device: 1468ed3557c9SMiquel Raynal if (wpan_dev->netdev) 1469ed3557c9SMiquel Raynal dev_put(wpan_dev->netdev); 1470ed3557c9SMiquel Raynal free_request: 1471ed3557c9SMiquel Raynal kfree(request); 1472ed3557c9SMiquel Raynal 1473ed3557c9SMiquel Raynal return err; 1474ed3557c9SMiquel Raynal } 1475ed3557c9SMiquel Raynal 1476ed3557c9SMiquel Raynal static int nl802154_prep_scan_msg(struct sk_buff *msg, 1477ed3557c9SMiquel Raynal struct cfg802154_registered_device *rdev, 1478ed3557c9SMiquel Raynal struct wpan_dev *wpan_dev, u32 portid, 1479ed3557c9SMiquel Raynal u32 seq, int flags, u8 cmd, u8 arg) 1480ed3557c9SMiquel Raynal { 1481ed3557c9SMiquel Raynal void *hdr; 1482ed3557c9SMiquel Raynal 1483ed3557c9SMiquel Raynal hdr = nl802154hdr_put(msg, portid, seq, flags, cmd); 1484ed3557c9SMiquel Raynal if (!hdr) 1485ed3557c9SMiquel Raynal return -ENOBUFS; 1486ed3557c9SMiquel Raynal 1487ed3557c9SMiquel Raynal if (nla_put_u32(msg, NL802154_ATTR_WPAN_PHY, rdev->wpan_phy_idx)) 1488ed3557c9SMiquel Raynal goto nla_put_failure; 1489ed3557c9SMiquel Raynal 1490ed3557c9SMiquel Raynal if (wpan_dev->netdev && 1491ed3557c9SMiquel Raynal nla_put_u32(msg, NL802154_ATTR_IFINDEX, wpan_dev->netdev->ifindex)) 1492ed3557c9SMiquel Raynal goto nla_put_failure; 1493ed3557c9SMiquel Raynal 1494ed3557c9SMiquel Raynal if (nla_put_u64_64bit(msg, NL802154_ATTR_WPAN_DEV, 1495ed3557c9SMiquel Raynal wpan_dev_id(wpan_dev), NL802154_ATTR_PAD)) 1496ed3557c9SMiquel Raynal goto nla_put_failure; 1497ed3557c9SMiquel Raynal 1498ed3557c9SMiquel Raynal if (cmd == NL802154_CMD_SCAN_DONE && 1499ed3557c9SMiquel Raynal nla_put_u8(msg, NL802154_ATTR_SCAN_DONE_REASON, arg)) 1500ed3557c9SMiquel Raynal goto nla_put_failure; 1501ed3557c9SMiquel Raynal 1502ed3557c9SMiquel Raynal genlmsg_end(msg, hdr); 1503ed3557c9SMiquel Raynal 1504ed3557c9SMiquel Raynal return 0; 1505ed3557c9SMiquel Raynal 1506ed3557c9SMiquel Raynal nla_put_failure: 1507ed3557c9SMiquel Raynal genlmsg_cancel(msg, hdr); 1508ed3557c9SMiquel Raynal 1509ed3557c9SMiquel Raynal return -EMSGSIZE; 1510ed3557c9SMiquel Raynal } 1511ed3557c9SMiquel Raynal 1512ed3557c9SMiquel Raynal static int nl802154_send_scan_msg(struct cfg802154_registered_device *rdev, 1513ed3557c9SMiquel Raynal struct wpan_dev *wpan_dev, u8 cmd, u8 arg) 1514ed3557c9SMiquel Raynal { 1515ed3557c9SMiquel Raynal struct sk_buff *msg; 1516ed3557c9SMiquel Raynal int ret; 1517ed3557c9SMiquel Raynal 1518ed3557c9SMiquel Raynal msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 1519ed3557c9SMiquel Raynal if (!msg) 1520ed3557c9SMiquel Raynal return -ENOMEM; 1521ed3557c9SMiquel Raynal 1522ed3557c9SMiquel Raynal ret = nl802154_prep_scan_msg(msg, rdev, wpan_dev, 0, 0, 0, cmd, arg); 1523ed3557c9SMiquel Raynal if (ret < 0) { 1524ed3557c9SMiquel Raynal nlmsg_free(msg); 1525ed3557c9SMiquel Raynal return ret; 1526ed3557c9SMiquel Raynal } 1527ed3557c9SMiquel Raynal 1528ed3557c9SMiquel Raynal return genlmsg_multicast_netns(&nl802154_fam, 1529ed3557c9SMiquel Raynal wpan_phy_net(&rdev->wpan_phy), msg, 0, 1530ed3557c9SMiquel Raynal NL802154_MCGRP_SCAN, GFP_KERNEL); 1531ed3557c9SMiquel Raynal } 1532ed3557c9SMiquel Raynal 1533ed3557c9SMiquel Raynal int nl802154_scan_started(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev) 1534ed3557c9SMiquel Raynal { 1535ed3557c9SMiquel Raynal struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(wpan_phy); 1536ed3557c9SMiquel Raynal int err; 1537ed3557c9SMiquel Raynal 1538ed3557c9SMiquel Raynal /* Ignore errors when there are no listeners */ 1539ed3557c9SMiquel Raynal err = nl802154_send_scan_msg(rdev, wpan_dev, NL802154_CMD_TRIGGER_SCAN, 0); 1540ed3557c9SMiquel Raynal if (err == -ESRCH) 1541ed3557c9SMiquel Raynal err = 0; 1542ed3557c9SMiquel Raynal 1543ed3557c9SMiquel Raynal return err; 1544ed3557c9SMiquel Raynal } 1545ed3557c9SMiquel Raynal EXPORT_SYMBOL_GPL(nl802154_scan_started); 1546ed3557c9SMiquel Raynal 1547ed3557c9SMiquel Raynal int nl802154_scan_done(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev, 1548ed3557c9SMiquel Raynal enum nl802154_scan_done_reasons reason) 1549ed3557c9SMiquel Raynal { 1550ed3557c9SMiquel Raynal struct cfg802154_registered_device *rdev = wpan_phy_to_rdev(wpan_phy); 1551ed3557c9SMiquel Raynal int err; 1552ed3557c9SMiquel Raynal 1553ed3557c9SMiquel Raynal /* Ignore errors when there are no listeners */ 1554ed3557c9SMiquel Raynal err = nl802154_send_scan_msg(rdev, wpan_dev, NL802154_CMD_SCAN_DONE, reason); 1555ed3557c9SMiquel Raynal if (err == -ESRCH) 1556ed3557c9SMiquel Raynal err = 0; 1557ed3557c9SMiquel Raynal 1558ed3557c9SMiquel Raynal if (wpan_dev->netdev) 1559ed3557c9SMiquel Raynal dev_put(wpan_dev->netdev); 1560ed3557c9SMiquel Raynal 1561ed3557c9SMiquel Raynal return err; 1562ed3557c9SMiquel Raynal } 1563ed3557c9SMiquel Raynal EXPORT_SYMBOL_GPL(nl802154_scan_done); 1564ed3557c9SMiquel Raynal 1565ed3557c9SMiquel Raynal static int nl802154_abort_scan(struct sk_buff *skb, struct genl_info *info) 1566ed3557c9SMiquel Raynal { 1567ed3557c9SMiquel Raynal struct cfg802154_registered_device *rdev = info->user_ptr[0]; 1568ed3557c9SMiquel Raynal struct net_device *dev = info->user_ptr[1]; 1569ed3557c9SMiquel Raynal struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 1570ed3557c9SMiquel Raynal 1571ed3557c9SMiquel Raynal /* Resources are released in the notification helper above */ 1572ed3557c9SMiquel Raynal return rdev_abort_scan(rdev, wpan_dev); 1573ed3557c9SMiquel Raynal } 1574ed3557c9SMiquel Raynal 15759bc11450SMiquel Raynal static int 15769bc11450SMiquel Raynal nl802154_send_beacons(struct sk_buff *skb, struct genl_info *info) 15779bc11450SMiquel Raynal { 15789bc11450SMiquel Raynal struct cfg802154_registered_device *rdev = info->user_ptr[0]; 15799bc11450SMiquel Raynal struct net_device *dev = info->user_ptr[1]; 15809bc11450SMiquel Raynal struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 15819bc11450SMiquel Raynal struct wpan_phy *wpan_phy = &rdev->wpan_phy; 15829bc11450SMiquel Raynal struct cfg802154_beacon_request *request; 15839bc11450SMiquel Raynal int err; 15849bc11450SMiquel Raynal 1585a0b61066SMiquel Raynal if (wpan_dev->iftype != NL802154_IFTYPE_COORD) { 1586a0b61066SMiquel Raynal NL_SET_ERR_MSG(info->extack, "Only coordinators can send beacons"); 15879bc11450SMiquel Raynal return -EOPNOTSUPP; 1588a0b61066SMiquel Raynal } 15899bc11450SMiquel Raynal 15909bc11450SMiquel Raynal if (wpan_dev->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) { 1591a0b61066SMiquel Raynal NL_SET_ERR_MSG(info->extack, "Device is not part of any PAN"); 15929bc11450SMiquel Raynal return -EPERM; 15939bc11450SMiquel Raynal } 15949bc11450SMiquel Raynal 15959bc11450SMiquel Raynal request = kzalloc(sizeof(*request), GFP_KERNEL); 15969bc11450SMiquel Raynal if (!request) 15979bc11450SMiquel Raynal return -ENOMEM; 15989bc11450SMiquel Raynal 15999bc11450SMiquel Raynal request->wpan_dev = wpan_dev; 16009bc11450SMiquel Raynal request->wpan_phy = wpan_phy; 16019bc11450SMiquel Raynal 16029bc11450SMiquel Raynal /* Use maximum duration order by default */ 1603648324c9SMiquel Raynal if (info->attrs[NL802154_ATTR_BEACON_INTERVAL]) 1604648324c9SMiquel Raynal request->interval = nla_get_u8(info->attrs[NL802154_ATTR_BEACON_INTERVAL]); 1605648324c9SMiquel Raynal else 16069bc11450SMiquel Raynal request->interval = IEEE802154_MAX_SCAN_DURATION; 16079bc11450SMiquel Raynal 16089bc11450SMiquel Raynal if (wpan_dev->netdev) 16099bc11450SMiquel Raynal dev_hold(wpan_dev->netdev); 16109bc11450SMiquel Raynal 16119bc11450SMiquel Raynal err = rdev_send_beacons(rdev, request); 16129bc11450SMiquel Raynal if (err) { 16139bc11450SMiquel Raynal pr_err("Failure starting sending beacons (%d)\n", err); 16149bc11450SMiquel Raynal goto free_device; 16159bc11450SMiquel Raynal } 16169bc11450SMiquel Raynal 16179bc11450SMiquel Raynal return 0; 16189bc11450SMiquel Raynal 16199bc11450SMiquel Raynal free_device: 16209bc11450SMiquel Raynal if (wpan_dev->netdev) 16219bc11450SMiquel Raynal dev_put(wpan_dev->netdev); 1622648324c9SMiquel Raynal 16239bc11450SMiquel Raynal kfree(request); 16249bc11450SMiquel Raynal 16259bc11450SMiquel Raynal return err; 16269bc11450SMiquel Raynal } 16279bc11450SMiquel Raynal 16289bc11450SMiquel Raynal void nl802154_beaconing_done(struct wpan_dev *wpan_dev) 16299bc11450SMiquel Raynal { 16309bc11450SMiquel Raynal if (wpan_dev->netdev) 16319bc11450SMiquel Raynal dev_put(wpan_dev->netdev); 16329bc11450SMiquel Raynal } 16339bc11450SMiquel Raynal EXPORT_SYMBOL_GPL(nl802154_beaconing_done); 16349bc11450SMiquel Raynal 16359bc11450SMiquel Raynal static int 16369bc11450SMiquel Raynal nl802154_stop_beacons(struct sk_buff *skb, struct genl_info *info) 16379bc11450SMiquel Raynal { 16389bc11450SMiquel Raynal struct cfg802154_registered_device *rdev = info->user_ptr[0]; 16399bc11450SMiquel Raynal struct net_device *dev = info->user_ptr[1]; 16409bc11450SMiquel Raynal struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 16419bc11450SMiquel Raynal 16429bc11450SMiquel Raynal /* Resources are released in the notification helper above */ 16439bc11450SMiquel Raynal return rdev_stop_beacons(rdev, wpan_dev); 16449bc11450SMiquel Raynal } 16459bc11450SMiquel Raynal 1646a26c5fd7SAlexander Aring #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL 1647a26c5fd7SAlexander Aring static const struct nla_policy nl802154_dev_addr_policy[NL802154_DEV_ADDR_ATTR_MAX + 1] = { 1648a26c5fd7SAlexander Aring [NL802154_DEV_ADDR_ATTR_PAN_ID] = { .type = NLA_U16 }, 1649a26c5fd7SAlexander Aring [NL802154_DEV_ADDR_ATTR_MODE] = { .type = NLA_U32 }, 1650a26c5fd7SAlexander Aring [NL802154_DEV_ADDR_ATTR_SHORT] = { .type = NLA_U16 }, 1651a26c5fd7SAlexander Aring [NL802154_DEV_ADDR_ATTR_EXTENDED] = { .type = NLA_U64 }, 1652a26c5fd7SAlexander Aring }; 1653a26c5fd7SAlexander Aring 1654a26c5fd7SAlexander Aring static int 1655a26c5fd7SAlexander Aring ieee802154_llsec_parse_dev_addr(struct nlattr *nla, 1656a26c5fd7SAlexander Aring struct ieee802154_addr *addr) 1657a26c5fd7SAlexander Aring { 1658a26c5fd7SAlexander Aring struct nlattr *attrs[NL802154_DEV_ADDR_ATTR_MAX + 1]; 1659a26c5fd7SAlexander Aring 16608cb08174SJohannes Berg if (!nla || nla_parse_nested_deprecated(attrs, NL802154_DEV_ADDR_ATTR_MAX, nla, nl802154_dev_addr_policy, NULL)) 1661a26c5fd7SAlexander Aring return -EINVAL; 1662a26c5fd7SAlexander Aring 16639fdd0491SDan Robertson if (!attrs[NL802154_DEV_ADDR_ATTR_PAN_ID] || !attrs[NL802154_DEV_ADDR_ATTR_MODE]) 1664a26c5fd7SAlexander Aring return -EINVAL; 1665a26c5fd7SAlexander Aring 1666a26c5fd7SAlexander Aring addr->pan_id = nla_get_le16(attrs[NL802154_DEV_ADDR_ATTR_PAN_ID]); 1667a26c5fd7SAlexander Aring addr->mode = nla_get_u32(attrs[NL802154_DEV_ADDR_ATTR_MODE]); 1668a26c5fd7SAlexander Aring switch (addr->mode) { 1669a26c5fd7SAlexander Aring case NL802154_DEV_ADDR_SHORT: 16709fdd0491SDan Robertson if (!attrs[NL802154_DEV_ADDR_ATTR_SHORT]) 16719fdd0491SDan Robertson return -EINVAL; 1672a26c5fd7SAlexander Aring addr->short_addr = nla_get_le16(attrs[NL802154_DEV_ADDR_ATTR_SHORT]); 1673a26c5fd7SAlexander Aring break; 1674a26c5fd7SAlexander Aring case NL802154_DEV_ADDR_EXTENDED: 16759fdd0491SDan Robertson if (!attrs[NL802154_DEV_ADDR_ATTR_EXTENDED]) 16769fdd0491SDan Robertson return -EINVAL; 1677a26c5fd7SAlexander Aring addr->extended_addr = nla_get_le64(attrs[NL802154_DEV_ADDR_ATTR_EXTENDED]); 1678a26c5fd7SAlexander Aring break; 1679a26c5fd7SAlexander Aring default: 1680a26c5fd7SAlexander Aring return -EINVAL; 1681a26c5fd7SAlexander Aring } 1682a26c5fd7SAlexander Aring 1683a26c5fd7SAlexander Aring return 0; 1684a26c5fd7SAlexander Aring } 1685a26c5fd7SAlexander Aring 1686a26c5fd7SAlexander Aring static const struct nla_policy nl802154_key_id_policy[NL802154_KEY_ID_ATTR_MAX + 1] = { 1687a26c5fd7SAlexander Aring [NL802154_KEY_ID_ATTR_MODE] = { .type = NLA_U32 }, 1688a26c5fd7SAlexander Aring [NL802154_KEY_ID_ATTR_INDEX] = { .type = NLA_U8 }, 1689a26c5fd7SAlexander Aring [NL802154_KEY_ID_ATTR_IMPLICIT] = { .type = NLA_NESTED }, 1690a26c5fd7SAlexander Aring [NL802154_KEY_ID_ATTR_SOURCE_SHORT] = { .type = NLA_U32 }, 1691a26c5fd7SAlexander Aring [NL802154_KEY_ID_ATTR_SOURCE_EXTENDED] = { .type = NLA_U64 }, 1692a26c5fd7SAlexander Aring }; 1693a26c5fd7SAlexander Aring 1694a26c5fd7SAlexander Aring static int 1695a26c5fd7SAlexander Aring ieee802154_llsec_parse_key_id(struct nlattr *nla, 1696a26c5fd7SAlexander Aring struct ieee802154_llsec_key_id *desc) 1697a26c5fd7SAlexander Aring { 1698a26c5fd7SAlexander Aring struct nlattr *attrs[NL802154_KEY_ID_ATTR_MAX + 1]; 1699a26c5fd7SAlexander Aring 17008cb08174SJohannes Berg if (!nla || nla_parse_nested_deprecated(attrs, NL802154_KEY_ID_ATTR_MAX, nla, nl802154_key_id_policy, NULL)) 1701a26c5fd7SAlexander Aring return -EINVAL; 1702a26c5fd7SAlexander Aring 1703a26c5fd7SAlexander Aring if (!attrs[NL802154_KEY_ID_ATTR_MODE]) 1704a26c5fd7SAlexander Aring return -EINVAL; 1705a26c5fd7SAlexander Aring 1706a26c5fd7SAlexander Aring desc->mode = nla_get_u32(attrs[NL802154_KEY_ID_ATTR_MODE]); 1707a26c5fd7SAlexander Aring switch (desc->mode) { 1708a26c5fd7SAlexander Aring case NL802154_KEY_ID_MODE_IMPLICIT: 1709a26c5fd7SAlexander Aring if (!attrs[NL802154_KEY_ID_ATTR_IMPLICIT]) 1710a26c5fd7SAlexander Aring return -EINVAL; 1711a26c5fd7SAlexander Aring 1712a26c5fd7SAlexander Aring if (ieee802154_llsec_parse_dev_addr(attrs[NL802154_KEY_ID_ATTR_IMPLICIT], 1713a26c5fd7SAlexander Aring &desc->device_addr) < 0) 1714a26c5fd7SAlexander Aring return -EINVAL; 1715a26c5fd7SAlexander Aring break; 1716a26c5fd7SAlexander Aring case NL802154_KEY_ID_MODE_INDEX: 1717a26c5fd7SAlexander Aring break; 1718a26c5fd7SAlexander Aring case NL802154_KEY_ID_MODE_INDEX_SHORT: 1719a26c5fd7SAlexander Aring if (!attrs[NL802154_KEY_ID_ATTR_SOURCE_SHORT]) 1720a26c5fd7SAlexander Aring return -EINVAL; 1721a26c5fd7SAlexander Aring 1722a26c5fd7SAlexander Aring desc->short_source = nla_get_le32(attrs[NL802154_KEY_ID_ATTR_SOURCE_SHORT]); 1723a26c5fd7SAlexander Aring break; 1724a26c5fd7SAlexander Aring case NL802154_KEY_ID_MODE_INDEX_EXTENDED: 1725a26c5fd7SAlexander Aring if (!attrs[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED]) 1726a26c5fd7SAlexander Aring return -EINVAL; 1727a26c5fd7SAlexander Aring 1728a26c5fd7SAlexander Aring desc->extended_source = nla_get_le64(attrs[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED]); 1729a26c5fd7SAlexander Aring break; 1730a26c5fd7SAlexander Aring default: 1731a26c5fd7SAlexander Aring return -EINVAL; 1732a26c5fd7SAlexander Aring } 1733a26c5fd7SAlexander Aring 1734a26c5fd7SAlexander Aring if (desc->mode != NL802154_KEY_ID_MODE_IMPLICIT) { 1735a26c5fd7SAlexander Aring if (!attrs[NL802154_KEY_ID_ATTR_INDEX]) 1736a26c5fd7SAlexander Aring return -EINVAL; 1737a26c5fd7SAlexander Aring 1738a26c5fd7SAlexander Aring /* TODO change id to idx */ 1739a26c5fd7SAlexander Aring desc->id = nla_get_u8(attrs[NL802154_KEY_ID_ATTR_INDEX]); 1740a26c5fd7SAlexander Aring } 1741a26c5fd7SAlexander Aring 1742a26c5fd7SAlexander Aring return 0; 1743a26c5fd7SAlexander Aring } 1744a26c5fd7SAlexander Aring 1745a26c5fd7SAlexander Aring static int nl802154_set_llsec_params(struct sk_buff *skb, 1746a26c5fd7SAlexander Aring struct genl_info *info) 1747a26c5fd7SAlexander Aring { 1748a26c5fd7SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 1749a26c5fd7SAlexander Aring struct net_device *dev = info->user_ptr[1]; 1750a26c5fd7SAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 1751a26c5fd7SAlexander Aring struct ieee802154_llsec_params params; 1752a26c5fd7SAlexander Aring u32 changed = 0; 1753a26c5fd7SAlexander Aring int ret; 1754a26c5fd7SAlexander Aring 175588c17855SAlexander Aring if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) 175688c17855SAlexander Aring return -EOPNOTSUPP; 175788c17855SAlexander Aring 1758a26c5fd7SAlexander Aring if (info->attrs[NL802154_ATTR_SEC_ENABLED]) { 1759a26c5fd7SAlexander Aring u8 enabled; 1760a26c5fd7SAlexander Aring 1761a26c5fd7SAlexander Aring enabled = nla_get_u8(info->attrs[NL802154_ATTR_SEC_ENABLED]); 1762a26c5fd7SAlexander Aring if (enabled != 0 && enabled != 1) 1763a26c5fd7SAlexander Aring return -EINVAL; 1764a26c5fd7SAlexander Aring 1765a26c5fd7SAlexander Aring params.enabled = nla_get_u8(info->attrs[NL802154_ATTR_SEC_ENABLED]); 1766a26c5fd7SAlexander Aring changed |= IEEE802154_LLSEC_PARAM_ENABLED; 1767a26c5fd7SAlexander Aring } 1768a26c5fd7SAlexander Aring 1769a26c5fd7SAlexander Aring if (info->attrs[NL802154_ATTR_SEC_OUT_KEY_ID]) { 1770a26c5fd7SAlexander Aring ret = ieee802154_llsec_parse_key_id(info->attrs[NL802154_ATTR_SEC_OUT_KEY_ID], 1771a26c5fd7SAlexander Aring ¶ms.out_key); 1772a26c5fd7SAlexander Aring if (ret < 0) 1773a26c5fd7SAlexander Aring return ret; 1774a26c5fd7SAlexander Aring 1775a26c5fd7SAlexander Aring changed |= IEEE802154_LLSEC_PARAM_OUT_KEY; 1776a26c5fd7SAlexander Aring } 1777a26c5fd7SAlexander Aring 1778a26c5fd7SAlexander Aring if (info->attrs[NL802154_ATTR_SEC_OUT_LEVEL]) { 1779a26c5fd7SAlexander Aring params.out_level = nla_get_u32(info->attrs[NL802154_ATTR_SEC_OUT_LEVEL]); 1780a26c5fd7SAlexander Aring if (params.out_level > NL802154_SECLEVEL_MAX) 1781a26c5fd7SAlexander Aring return -EINVAL; 1782a26c5fd7SAlexander Aring 1783a26c5fd7SAlexander Aring changed |= IEEE802154_LLSEC_PARAM_OUT_LEVEL; 1784a26c5fd7SAlexander Aring } 1785a26c5fd7SAlexander Aring 1786a26c5fd7SAlexander Aring if (info->attrs[NL802154_ATTR_SEC_FRAME_COUNTER]) { 1787a26c5fd7SAlexander Aring params.frame_counter = nla_get_be32(info->attrs[NL802154_ATTR_SEC_FRAME_COUNTER]); 1788a26c5fd7SAlexander Aring changed |= IEEE802154_LLSEC_PARAM_FRAME_COUNTER; 1789a26c5fd7SAlexander Aring } 1790a26c5fd7SAlexander Aring 1791a26c5fd7SAlexander Aring return rdev_set_llsec_params(rdev, wpan_dev, ¶ms, changed); 1792a26c5fd7SAlexander Aring } 1793a26c5fd7SAlexander Aring 1794a26c5fd7SAlexander Aring static int nl802154_send_key(struct sk_buff *msg, u32 cmd, u32 portid, 1795a26c5fd7SAlexander Aring u32 seq, int flags, 1796a26c5fd7SAlexander Aring struct cfg802154_registered_device *rdev, 1797a26c5fd7SAlexander Aring struct net_device *dev, 1798a26c5fd7SAlexander Aring const struct ieee802154_llsec_key_entry *key) 1799a26c5fd7SAlexander Aring { 1800a26c5fd7SAlexander Aring void *hdr; 1801a26c5fd7SAlexander Aring u32 commands[NL802154_CMD_FRAME_NR_IDS / 32]; 1802a26c5fd7SAlexander Aring struct nlattr *nl_key, *nl_key_id; 1803a26c5fd7SAlexander Aring 1804a26c5fd7SAlexander Aring hdr = nl802154hdr_put(msg, portid, seq, flags, cmd); 1805a26c5fd7SAlexander Aring if (!hdr) 180679c37ca7SMiquel Raynal return -ENOBUFS; 1807a26c5fd7SAlexander Aring 1808a26c5fd7SAlexander Aring if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex)) 1809a26c5fd7SAlexander Aring goto nla_put_failure; 1810a26c5fd7SAlexander Aring 1811ae0be8deSMichal Kubecek nl_key = nla_nest_start_noflag(msg, NL802154_ATTR_SEC_KEY); 1812a26c5fd7SAlexander Aring if (!nl_key) 1813a26c5fd7SAlexander Aring goto nla_put_failure; 1814a26c5fd7SAlexander Aring 1815ae0be8deSMichal Kubecek nl_key_id = nla_nest_start_noflag(msg, NL802154_KEY_ATTR_ID); 1816a26c5fd7SAlexander Aring if (!nl_key_id) 1817a26c5fd7SAlexander Aring goto nla_put_failure; 1818a26c5fd7SAlexander Aring 1819a26c5fd7SAlexander Aring if (ieee802154_llsec_send_key_id(msg, &key->id) < 0) 1820a26c5fd7SAlexander Aring goto nla_put_failure; 1821a26c5fd7SAlexander Aring 1822a26c5fd7SAlexander Aring nla_nest_end(msg, nl_key_id); 1823a26c5fd7SAlexander Aring 1824a26c5fd7SAlexander Aring if (nla_put_u8(msg, NL802154_KEY_ATTR_USAGE_FRAMES, 1825a26c5fd7SAlexander Aring key->key->frame_types)) 1826a26c5fd7SAlexander Aring goto nla_put_failure; 1827a26c5fd7SAlexander Aring 1828a26c5fd7SAlexander Aring if (key->key->frame_types & BIT(NL802154_FRAME_CMD)) { 1829a26c5fd7SAlexander Aring /* TODO for each nested */ 1830a26c5fd7SAlexander Aring memset(commands, 0, sizeof(commands)); 1831a26c5fd7SAlexander Aring commands[7] = key->key->cmd_frame_ids; 1832a26c5fd7SAlexander Aring if (nla_put(msg, NL802154_KEY_ATTR_USAGE_CMDS, 1833a26c5fd7SAlexander Aring sizeof(commands), commands)) 1834a26c5fd7SAlexander Aring goto nla_put_failure; 1835a26c5fd7SAlexander Aring } 1836a26c5fd7SAlexander Aring 1837a26c5fd7SAlexander Aring if (nla_put(msg, NL802154_KEY_ATTR_BYTES, NL802154_KEY_SIZE, 1838a26c5fd7SAlexander Aring key->key->key)) 1839a26c5fd7SAlexander Aring goto nla_put_failure; 1840a26c5fd7SAlexander Aring 1841a26c5fd7SAlexander Aring nla_nest_end(msg, nl_key); 1842a26c5fd7SAlexander Aring genlmsg_end(msg, hdr); 1843a26c5fd7SAlexander Aring 1844a26c5fd7SAlexander Aring return 0; 1845a26c5fd7SAlexander Aring 1846a26c5fd7SAlexander Aring nla_put_failure: 1847a26c5fd7SAlexander Aring genlmsg_cancel(msg, hdr); 1848a26c5fd7SAlexander Aring return -EMSGSIZE; 1849a26c5fd7SAlexander Aring } 1850a26c5fd7SAlexander Aring 1851a26c5fd7SAlexander Aring static int 1852a26c5fd7SAlexander Aring nl802154_dump_llsec_key(struct sk_buff *skb, struct netlink_callback *cb) 1853a26c5fd7SAlexander Aring { 1854a26c5fd7SAlexander Aring struct cfg802154_registered_device *rdev = NULL; 1855a26c5fd7SAlexander Aring struct ieee802154_llsec_key_entry *key; 1856a26c5fd7SAlexander Aring struct ieee802154_llsec_table *table; 1857a26c5fd7SAlexander Aring struct wpan_dev *wpan_dev; 1858a26c5fd7SAlexander Aring int err; 1859a26c5fd7SAlexander Aring 1860a26c5fd7SAlexander Aring err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev); 1861a26c5fd7SAlexander Aring if (err) 1862a26c5fd7SAlexander Aring return err; 1863a26c5fd7SAlexander Aring 1864fb3c5cdfSAlexander Aring if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) { 1865fb3c5cdfSAlexander Aring err = skb->len; 1866fb3c5cdfSAlexander Aring goto out_err; 1867fb3c5cdfSAlexander Aring } 1868fb3c5cdfSAlexander Aring 1869a26c5fd7SAlexander Aring if (!wpan_dev->netdev) { 1870a26c5fd7SAlexander Aring err = -EINVAL; 1871a26c5fd7SAlexander Aring goto out_err; 1872a26c5fd7SAlexander Aring } 1873a26c5fd7SAlexander Aring 1874a26c5fd7SAlexander Aring rdev_lock_llsec_table(rdev, wpan_dev); 1875a26c5fd7SAlexander Aring rdev_get_llsec_table(rdev, wpan_dev, &table); 1876a26c5fd7SAlexander Aring 1877a26c5fd7SAlexander Aring /* TODO make it like station dump */ 1878a26c5fd7SAlexander Aring if (cb->args[2]) 1879a26c5fd7SAlexander Aring goto out; 1880a26c5fd7SAlexander Aring 1881a26c5fd7SAlexander Aring list_for_each_entry(key, &table->keys, list) { 1882a26c5fd7SAlexander Aring if (nl802154_send_key(skb, NL802154_CMD_NEW_SEC_KEY, 1883a26c5fd7SAlexander Aring NETLINK_CB(cb->skb).portid, 1884a26c5fd7SAlexander Aring cb->nlh->nlmsg_seq, NLM_F_MULTI, 1885a26c5fd7SAlexander Aring rdev, wpan_dev->netdev, key) < 0) { 1886a26c5fd7SAlexander Aring /* TODO */ 1887a26c5fd7SAlexander Aring err = -EIO; 1888a26c5fd7SAlexander Aring rdev_unlock_llsec_table(rdev, wpan_dev); 1889a26c5fd7SAlexander Aring goto out_err; 1890a26c5fd7SAlexander Aring } 1891a26c5fd7SAlexander Aring } 1892a26c5fd7SAlexander Aring 1893a26c5fd7SAlexander Aring cb->args[2] = 1; 1894a26c5fd7SAlexander Aring 1895a26c5fd7SAlexander Aring out: 1896a26c5fd7SAlexander Aring rdev_unlock_llsec_table(rdev, wpan_dev); 1897a26c5fd7SAlexander Aring err = skb->len; 1898a26c5fd7SAlexander Aring out_err: 1899a26c5fd7SAlexander Aring nl802154_finish_wpan_dev_dump(rdev); 1900a26c5fd7SAlexander Aring 1901a26c5fd7SAlexander Aring return err; 1902a26c5fd7SAlexander Aring } 1903a26c5fd7SAlexander Aring 1904a26c5fd7SAlexander Aring static const struct nla_policy nl802154_key_policy[NL802154_KEY_ATTR_MAX + 1] = { 1905a26c5fd7SAlexander Aring [NL802154_KEY_ATTR_ID] = { NLA_NESTED }, 1906a26c5fd7SAlexander Aring /* TODO handle it as for_each_nested and NLA_FLAG? */ 1907a26c5fd7SAlexander Aring [NL802154_KEY_ATTR_USAGE_FRAMES] = { NLA_U8 }, 1908a26c5fd7SAlexander Aring /* TODO handle it as for_each_nested, not static array? */ 1909a26c5fd7SAlexander Aring [NL802154_KEY_ATTR_USAGE_CMDS] = { .len = NL802154_CMD_FRAME_NR_IDS / 8 }, 1910a26c5fd7SAlexander Aring [NL802154_KEY_ATTR_BYTES] = { .len = NL802154_KEY_SIZE }, 1911a26c5fd7SAlexander Aring }; 1912a26c5fd7SAlexander Aring 1913a26c5fd7SAlexander Aring static int nl802154_add_llsec_key(struct sk_buff *skb, struct genl_info *info) 1914a26c5fd7SAlexander Aring { 1915a26c5fd7SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 1916a26c5fd7SAlexander Aring struct net_device *dev = info->user_ptr[1]; 1917a26c5fd7SAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 1918a26c5fd7SAlexander Aring struct nlattr *attrs[NL802154_KEY_ATTR_MAX + 1]; 1919a26c5fd7SAlexander Aring struct ieee802154_llsec_key key = { }; 1920a26c5fd7SAlexander Aring struct ieee802154_llsec_key_id id = { }; 1921a26c5fd7SAlexander Aring u32 commands[NL802154_CMD_FRAME_NR_IDS / 32] = { }; 1922a26c5fd7SAlexander Aring 192308470c54SAlexander Aring if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) 192408470c54SAlexander Aring return -EOPNOTSUPP; 192508470c54SAlexander Aring 192620d5fe2dSAlexander Aring if (!info->attrs[NL802154_ATTR_SEC_KEY] || 192720d5fe2dSAlexander Aring nla_parse_nested_deprecated(attrs, NL802154_KEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_KEY], nl802154_key_policy, info->extack)) 1928a26c5fd7SAlexander Aring return -EINVAL; 1929a26c5fd7SAlexander Aring 1930a26c5fd7SAlexander Aring if (!attrs[NL802154_KEY_ATTR_USAGE_FRAMES] || 1931a26c5fd7SAlexander Aring !attrs[NL802154_KEY_ATTR_BYTES]) 1932aa655562SDan Carpenter return -EINVAL; 1933a26c5fd7SAlexander Aring 1934a26c5fd7SAlexander Aring if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0) 1935a26c5fd7SAlexander Aring return -ENOBUFS; 1936a26c5fd7SAlexander Aring 1937a26c5fd7SAlexander Aring key.frame_types = nla_get_u8(attrs[NL802154_KEY_ATTR_USAGE_FRAMES]); 1938a26c5fd7SAlexander Aring if (key.frame_types > BIT(NL802154_FRAME_MAX) || 1939a26c5fd7SAlexander Aring ((key.frame_types & BIT(NL802154_FRAME_CMD)) && 1940a26c5fd7SAlexander Aring !attrs[NL802154_KEY_ATTR_USAGE_CMDS])) 1941a26c5fd7SAlexander Aring return -EINVAL; 1942a26c5fd7SAlexander Aring 1943a26c5fd7SAlexander Aring if (attrs[NL802154_KEY_ATTR_USAGE_CMDS]) { 1944a26c5fd7SAlexander Aring /* TODO for each nested */ 1945a26c5fd7SAlexander Aring nla_memcpy(commands, attrs[NL802154_KEY_ATTR_USAGE_CMDS], 1946a26c5fd7SAlexander Aring NL802154_CMD_FRAME_NR_IDS / 8); 1947a26c5fd7SAlexander Aring 1948a26c5fd7SAlexander Aring /* TODO understand the -EINVAL logic here? last condition */ 1949a26c5fd7SAlexander Aring if (commands[0] || commands[1] || commands[2] || commands[3] || 1950a26c5fd7SAlexander Aring commands[4] || commands[5] || commands[6] || 1951a26c5fd7SAlexander Aring commands[7] > BIT(NL802154_CMD_FRAME_MAX)) 1952a26c5fd7SAlexander Aring return -EINVAL; 1953a26c5fd7SAlexander Aring 1954a26c5fd7SAlexander Aring key.cmd_frame_ids = commands[7]; 1955a26c5fd7SAlexander Aring } else { 1956a26c5fd7SAlexander Aring key.cmd_frame_ids = 0; 1957a26c5fd7SAlexander Aring } 1958a26c5fd7SAlexander Aring 1959a26c5fd7SAlexander Aring nla_memcpy(key.key, attrs[NL802154_KEY_ATTR_BYTES], NL802154_KEY_SIZE); 1960a26c5fd7SAlexander Aring 1961a26c5fd7SAlexander Aring if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0) 1962a26c5fd7SAlexander Aring return -ENOBUFS; 1963a26c5fd7SAlexander Aring 1964a26c5fd7SAlexander Aring return rdev_add_llsec_key(rdev, wpan_dev, &id, &key); 1965a26c5fd7SAlexander Aring } 1966a26c5fd7SAlexander Aring 1967a26c5fd7SAlexander Aring static int nl802154_del_llsec_key(struct sk_buff *skb, struct genl_info *info) 1968a26c5fd7SAlexander Aring { 1969a26c5fd7SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 1970a26c5fd7SAlexander Aring struct net_device *dev = info->user_ptr[1]; 1971a26c5fd7SAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 1972a26c5fd7SAlexander Aring struct nlattr *attrs[NL802154_KEY_ATTR_MAX + 1]; 1973a26c5fd7SAlexander Aring struct ieee802154_llsec_key_id id; 1974a26c5fd7SAlexander Aring 1975b6e29495SAlexander Aring if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) 1976b6e29495SAlexander Aring return -EOPNOTSUPP; 1977b6e29495SAlexander Aring 197837feaaf5SAlexander Aring if (!info->attrs[NL802154_ATTR_SEC_KEY] || 197937feaaf5SAlexander Aring nla_parse_nested_deprecated(attrs, NL802154_KEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_KEY], nl802154_key_policy, info->extack)) 1980a26c5fd7SAlexander Aring return -EINVAL; 1981a26c5fd7SAlexander Aring 1982a26c5fd7SAlexander Aring if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0) 1983a26c5fd7SAlexander Aring return -ENOBUFS; 1984a26c5fd7SAlexander Aring 1985a26c5fd7SAlexander Aring return rdev_del_llsec_key(rdev, wpan_dev, &id); 1986a26c5fd7SAlexander Aring } 1987a26c5fd7SAlexander Aring 1988a26c5fd7SAlexander Aring static int nl802154_send_device(struct sk_buff *msg, u32 cmd, u32 portid, 1989a26c5fd7SAlexander Aring u32 seq, int flags, 1990a26c5fd7SAlexander Aring struct cfg802154_registered_device *rdev, 1991a26c5fd7SAlexander Aring struct net_device *dev, 1992a26c5fd7SAlexander Aring const struct ieee802154_llsec_device *dev_desc) 1993a26c5fd7SAlexander Aring { 1994a26c5fd7SAlexander Aring void *hdr; 1995a26c5fd7SAlexander Aring struct nlattr *nl_device; 1996a26c5fd7SAlexander Aring 1997a26c5fd7SAlexander Aring hdr = nl802154hdr_put(msg, portid, seq, flags, cmd); 1998a26c5fd7SAlexander Aring if (!hdr) 199979c37ca7SMiquel Raynal return -ENOBUFS; 2000a26c5fd7SAlexander Aring 2001a26c5fd7SAlexander Aring if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex)) 2002a26c5fd7SAlexander Aring goto nla_put_failure; 2003a26c5fd7SAlexander Aring 2004ae0be8deSMichal Kubecek nl_device = nla_nest_start_noflag(msg, NL802154_ATTR_SEC_DEVICE); 2005a26c5fd7SAlexander Aring if (!nl_device) 2006a26c5fd7SAlexander Aring goto nla_put_failure; 2007a26c5fd7SAlexander Aring 2008a26c5fd7SAlexander Aring if (nla_put_u32(msg, NL802154_DEV_ATTR_FRAME_COUNTER, 2009a26c5fd7SAlexander Aring dev_desc->frame_counter) || 2010a26c5fd7SAlexander Aring nla_put_le16(msg, NL802154_DEV_ATTR_PAN_ID, dev_desc->pan_id) || 2011a26c5fd7SAlexander Aring nla_put_le16(msg, NL802154_DEV_ATTR_SHORT_ADDR, 2012a26c5fd7SAlexander Aring dev_desc->short_addr) || 2013a26c5fd7SAlexander Aring nla_put_le64(msg, NL802154_DEV_ATTR_EXTENDED_ADDR, 2014e7479122SNicolas Dichtel dev_desc->hwaddr, NL802154_DEV_ATTR_PAD) || 2015a26c5fd7SAlexander Aring nla_put_u8(msg, NL802154_DEV_ATTR_SECLEVEL_EXEMPT, 2016a26c5fd7SAlexander Aring dev_desc->seclevel_exempt) || 2017a26c5fd7SAlexander Aring nla_put_u32(msg, NL802154_DEV_ATTR_KEY_MODE, dev_desc->key_mode)) 2018a26c5fd7SAlexander Aring goto nla_put_failure; 2019a26c5fd7SAlexander Aring 2020a26c5fd7SAlexander Aring nla_nest_end(msg, nl_device); 2021a26c5fd7SAlexander Aring genlmsg_end(msg, hdr); 2022a26c5fd7SAlexander Aring 2023a26c5fd7SAlexander Aring return 0; 2024a26c5fd7SAlexander Aring 2025a26c5fd7SAlexander Aring nla_put_failure: 2026a26c5fd7SAlexander Aring genlmsg_cancel(msg, hdr); 2027a26c5fd7SAlexander Aring return -EMSGSIZE; 2028a26c5fd7SAlexander Aring } 2029a26c5fd7SAlexander Aring 2030a26c5fd7SAlexander Aring static int 2031a26c5fd7SAlexander Aring nl802154_dump_llsec_dev(struct sk_buff *skb, struct netlink_callback *cb) 2032a26c5fd7SAlexander Aring { 2033a26c5fd7SAlexander Aring struct cfg802154_registered_device *rdev = NULL; 2034a26c5fd7SAlexander Aring struct ieee802154_llsec_device *dev; 2035a26c5fd7SAlexander Aring struct ieee802154_llsec_table *table; 2036a26c5fd7SAlexander Aring struct wpan_dev *wpan_dev; 2037a26c5fd7SAlexander Aring int err; 2038a26c5fd7SAlexander Aring 2039a26c5fd7SAlexander Aring err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev); 2040a26c5fd7SAlexander Aring if (err) 2041a26c5fd7SAlexander Aring return err; 2042a26c5fd7SAlexander Aring 20435582d641SAlexander Aring if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) { 20445582d641SAlexander Aring err = skb->len; 20455582d641SAlexander Aring goto out_err; 20465582d641SAlexander Aring } 20475582d641SAlexander Aring 2048a26c5fd7SAlexander Aring if (!wpan_dev->netdev) { 2049a26c5fd7SAlexander Aring err = -EINVAL; 2050a26c5fd7SAlexander Aring goto out_err; 2051a26c5fd7SAlexander Aring } 2052a26c5fd7SAlexander Aring 2053a26c5fd7SAlexander Aring rdev_lock_llsec_table(rdev, wpan_dev); 2054a26c5fd7SAlexander Aring rdev_get_llsec_table(rdev, wpan_dev, &table); 2055a26c5fd7SAlexander Aring 2056a26c5fd7SAlexander Aring /* TODO make it like station dump */ 2057a26c5fd7SAlexander Aring if (cb->args[2]) 2058a26c5fd7SAlexander Aring goto out; 2059a26c5fd7SAlexander Aring 2060a26c5fd7SAlexander Aring list_for_each_entry(dev, &table->devices, list) { 2061a26c5fd7SAlexander Aring if (nl802154_send_device(skb, NL802154_CMD_NEW_SEC_LEVEL, 2062a26c5fd7SAlexander Aring NETLINK_CB(cb->skb).portid, 2063a26c5fd7SAlexander Aring cb->nlh->nlmsg_seq, NLM_F_MULTI, 2064a26c5fd7SAlexander Aring rdev, wpan_dev->netdev, dev) < 0) { 2065a26c5fd7SAlexander Aring /* TODO */ 2066a26c5fd7SAlexander Aring err = -EIO; 2067a26c5fd7SAlexander Aring rdev_unlock_llsec_table(rdev, wpan_dev); 2068a26c5fd7SAlexander Aring goto out_err; 2069a26c5fd7SAlexander Aring } 2070a26c5fd7SAlexander Aring } 2071a26c5fd7SAlexander Aring 2072a26c5fd7SAlexander Aring cb->args[2] = 1; 2073a26c5fd7SAlexander Aring 2074a26c5fd7SAlexander Aring out: 2075a26c5fd7SAlexander Aring rdev_unlock_llsec_table(rdev, wpan_dev); 2076a26c5fd7SAlexander Aring err = skb->len; 2077a26c5fd7SAlexander Aring out_err: 2078a26c5fd7SAlexander Aring nl802154_finish_wpan_dev_dump(rdev); 2079a26c5fd7SAlexander Aring 2080a26c5fd7SAlexander Aring return err; 2081a26c5fd7SAlexander Aring } 2082a26c5fd7SAlexander Aring 2083a26c5fd7SAlexander Aring static const struct nla_policy nl802154_dev_policy[NL802154_DEV_ATTR_MAX + 1] = { 2084a26c5fd7SAlexander Aring [NL802154_DEV_ATTR_FRAME_COUNTER] = { NLA_U32 }, 2085a26c5fd7SAlexander Aring [NL802154_DEV_ATTR_PAN_ID] = { .type = NLA_U16 }, 2086a26c5fd7SAlexander Aring [NL802154_DEV_ATTR_SHORT_ADDR] = { .type = NLA_U16 }, 2087a26c5fd7SAlexander Aring [NL802154_DEV_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 }, 2088a26c5fd7SAlexander Aring [NL802154_DEV_ATTR_SECLEVEL_EXEMPT] = { NLA_U8 }, 2089a26c5fd7SAlexander Aring [NL802154_DEV_ATTR_KEY_MODE] = { NLA_U32 }, 2090a26c5fd7SAlexander Aring }; 2091a26c5fd7SAlexander Aring 2092a26c5fd7SAlexander Aring static int 2093a26c5fd7SAlexander Aring ieee802154_llsec_parse_device(struct nlattr *nla, 2094a26c5fd7SAlexander Aring struct ieee802154_llsec_device *dev) 2095a26c5fd7SAlexander Aring { 2096a26c5fd7SAlexander Aring struct nlattr *attrs[NL802154_DEV_ATTR_MAX + 1]; 2097a26c5fd7SAlexander Aring 20988cb08174SJohannes Berg if (!nla || nla_parse_nested_deprecated(attrs, NL802154_DEV_ATTR_MAX, nla, nl802154_dev_policy, NULL)) 2099a26c5fd7SAlexander Aring return -EINVAL; 2100a26c5fd7SAlexander Aring 2101a26c5fd7SAlexander Aring memset(dev, 0, sizeof(*dev)); 2102a26c5fd7SAlexander Aring 2103a26c5fd7SAlexander Aring if (!attrs[NL802154_DEV_ATTR_FRAME_COUNTER] || 2104a26c5fd7SAlexander Aring !attrs[NL802154_DEV_ATTR_PAN_ID] || 2105a26c5fd7SAlexander Aring !attrs[NL802154_DEV_ATTR_SHORT_ADDR] || 2106a26c5fd7SAlexander Aring !attrs[NL802154_DEV_ATTR_EXTENDED_ADDR] || 2107a26c5fd7SAlexander Aring !attrs[NL802154_DEV_ATTR_SECLEVEL_EXEMPT] || 2108a26c5fd7SAlexander Aring !attrs[NL802154_DEV_ATTR_KEY_MODE]) 2109a26c5fd7SAlexander Aring return -EINVAL; 2110a26c5fd7SAlexander Aring 2111a26c5fd7SAlexander Aring /* TODO be32 */ 2112a26c5fd7SAlexander Aring dev->frame_counter = nla_get_u32(attrs[NL802154_DEV_ATTR_FRAME_COUNTER]); 2113a26c5fd7SAlexander Aring dev->pan_id = nla_get_le16(attrs[NL802154_DEV_ATTR_PAN_ID]); 2114a26c5fd7SAlexander Aring dev->short_addr = nla_get_le16(attrs[NL802154_DEV_ATTR_SHORT_ADDR]); 2115a26c5fd7SAlexander Aring /* TODO rename hwaddr to extended_addr */ 2116a26c5fd7SAlexander Aring dev->hwaddr = nla_get_le64(attrs[NL802154_DEV_ATTR_EXTENDED_ADDR]); 2117a26c5fd7SAlexander Aring dev->seclevel_exempt = nla_get_u8(attrs[NL802154_DEV_ATTR_SECLEVEL_EXEMPT]); 2118a26c5fd7SAlexander Aring dev->key_mode = nla_get_u32(attrs[NL802154_DEV_ATTR_KEY_MODE]); 2119a26c5fd7SAlexander Aring 2120a26c5fd7SAlexander Aring if (dev->key_mode > NL802154_DEVKEY_MAX || 2121a26c5fd7SAlexander Aring (dev->seclevel_exempt != 0 && dev->seclevel_exempt != 1)) 2122a26c5fd7SAlexander Aring return -EINVAL; 2123a26c5fd7SAlexander Aring 2124a26c5fd7SAlexander Aring return 0; 2125a26c5fd7SAlexander Aring } 2126a26c5fd7SAlexander Aring 2127a26c5fd7SAlexander Aring static int nl802154_add_llsec_dev(struct sk_buff *skb, struct genl_info *info) 2128a26c5fd7SAlexander Aring { 2129a26c5fd7SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 2130a26c5fd7SAlexander Aring struct net_device *dev = info->user_ptr[1]; 2131a26c5fd7SAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 2132a26c5fd7SAlexander Aring struct ieee802154_llsec_device dev_desc; 2133a26c5fd7SAlexander Aring 21345303f956SAlexander Aring if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) 21355303f956SAlexander Aring return -EOPNOTSUPP; 21365303f956SAlexander Aring 2137a26c5fd7SAlexander Aring if (ieee802154_llsec_parse_device(info->attrs[NL802154_ATTR_SEC_DEVICE], 2138a26c5fd7SAlexander Aring &dev_desc) < 0) 2139a26c5fd7SAlexander Aring return -EINVAL; 2140a26c5fd7SAlexander Aring 2141a26c5fd7SAlexander Aring return rdev_add_device(rdev, wpan_dev, &dev_desc); 2142a26c5fd7SAlexander Aring } 2143a26c5fd7SAlexander Aring 2144a26c5fd7SAlexander Aring static int nl802154_del_llsec_dev(struct sk_buff *skb, struct genl_info *info) 2145a26c5fd7SAlexander Aring { 2146a26c5fd7SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 2147a26c5fd7SAlexander Aring struct net_device *dev = info->user_ptr[1]; 2148a26c5fd7SAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 2149a26c5fd7SAlexander Aring struct nlattr *attrs[NL802154_DEV_ATTR_MAX + 1]; 2150a26c5fd7SAlexander Aring __le64 extended_addr; 2151a26c5fd7SAlexander Aring 2152ad8f9de1SAlexander Aring if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) 2153ad8f9de1SAlexander Aring return -EOPNOTSUPP; 2154ad8f9de1SAlexander Aring 21553d1eac2fSAlexander Aring if (!info->attrs[NL802154_ATTR_SEC_DEVICE] || 21563d1eac2fSAlexander Aring nla_parse_nested_deprecated(attrs, NL802154_DEV_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_DEVICE], nl802154_dev_policy, info->extack)) 2157a26c5fd7SAlexander Aring return -EINVAL; 2158a26c5fd7SAlexander Aring 2159a26c5fd7SAlexander Aring if (!attrs[NL802154_DEV_ATTR_EXTENDED_ADDR]) 2160a26c5fd7SAlexander Aring return -EINVAL; 2161a26c5fd7SAlexander Aring 2162a26c5fd7SAlexander Aring extended_addr = nla_get_le64(attrs[NL802154_DEV_ATTR_EXTENDED_ADDR]); 2163a26c5fd7SAlexander Aring return rdev_del_device(rdev, wpan_dev, extended_addr); 2164a26c5fd7SAlexander Aring } 2165a26c5fd7SAlexander Aring 2166a26c5fd7SAlexander Aring static int nl802154_send_devkey(struct sk_buff *msg, u32 cmd, u32 portid, 2167a26c5fd7SAlexander Aring u32 seq, int flags, 2168a26c5fd7SAlexander Aring struct cfg802154_registered_device *rdev, 2169a26c5fd7SAlexander Aring struct net_device *dev, __le64 extended_addr, 2170a26c5fd7SAlexander Aring const struct ieee802154_llsec_device_key *devkey) 2171a26c5fd7SAlexander Aring { 2172a26c5fd7SAlexander Aring void *hdr; 2173a26c5fd7SAlexander Aring struct nlattr *nl_devkey, *nl_key_id; 2174a26c5fd7SAlexander Aring 2175a26c5fd7SAlexander Aring hdr = nl802154hdr_put(msg, portid, seq, flags, cmd); 2176a26c5fd7SAlexander Aring if (!hdr) 217779c37ca7SMiquel Raynal return -ENOBUFS; 2178a26c5fd7SAlexander Aring 2179a26c5fd7SAlexander Aring if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex)) 2180a26c5fd7SAlexander Aring goto nla_put_failure; 2181a26c5fd7SAlexander Aring 2182ae0be8deSMichal Kubecek nl_devkey = nla_nest_start_noflag(msg, NL802154_ATTR_SEC_DEVKEY); 2183a26c5fd7SAlexander Aring if (!nl_devkey) 2184a26c5fd7SAlexander Aring goto nla_put_failure; 2185a26c5fd7SAlexander Aring 2186a26c5fd7SAlexander Aring if (nla_put_le64(msg, NL802154_DEVKEY_ATTR_EXTENDED_ADDR, 2187e7479122SNicolas Dichtel extended_addr, NL802154_DEVKEY_ATTR_PAD) || 2188a26c5fd7SAlexander Aring nla_put_u32(msg, NL802154_DEVKEY_ATTR_FRAME_COUNTER, 2189a26c5fd7SAlexander Aring devkey->frame_counter)) 2190a26c5fd7SAlexander Aring goto nla_put_failure; 2191a26c5fd7SAlexander Aring 2192ae0be8deSMichal Kubecek nl_key_id = nla_nest_start_noflag(msg, NL802154_DEVKEY_ATTR_ID); 2193a26c5fd7SAlexander Aring if (!nl_key_id) 2194a26c5fd7SAlexander Aring goto nla_put_failure; 2195a26c5fd7SAlexander Aring 2196a26c5fd7SAlexander Aring if (ieee802154_llsec_send_key_id(msg, &devkey->key_id) < 0) 2197a26c5fd7SAlexander Aring goto nla_put_failure; 2198a26c5fd7SAlexander Aring 2199a26c5fd7SAlexander Aring nla_nest_end(msg, nl_key_id); 2200a26c5fd7SAlexander Aring nla_nest_end(msg, nl_devkey); 2201a26c5fd7SAlexander Aring genlmsg_end(msg, hdr); 2202a26c5fd7SAlexander Aring 2203a26c5fd7SAlexander Aring return 0; 2204a26c5fd7SAlexander Aring 2205a26c5fd7SAlexander Aring nla_put_failure: 2206a26c5fd7SAlexander Aring genlmsg_cancel(msg, hdr); 2207a26c5fd7SAlexander Aring return -EMSGSIZE; 2208a26c5fd7SAlexander Aring } 2209a26c5fd7SAlexander Aring 2210a26c5fd7SAlexander Aring static int 2211a26c5fd7SAlexander Aring nl802154_dump_llsec_devkey(struct sk_buff *skb, struct netlink_callback *cb) 2212a26c5fd7SAlexander Aring { 2213a26c5fd7SAlexander Aring struct cfg802154_registered_device *rdev = NULL; 2214a26c5fd7SAlexander Aring struct ieee802154_llsec_device_key *kpos; 2215a26c5fd7SAlexander Aring struct ieee802154_llsec_device *dpos; 2216a26c5fd7SAlexander Aring struct ieee802154_llsec_table *table; 2217a26c5fd7SAlexander Aring struct wpan_dev *wpan_dev; 2218a26c5fd7SAlexander Aring int err; 2219a26c5fd7SAlexander Aring 2220a26c5fd7SAlexander Aring err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev); 2221a26c5fd7SAlexander Aring if (err) 2222a26c5fd7SAlexander Aring return err; 2223a26c5fd7SAlexander Aring 2224080d1a57SAlexander Aring if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) { 2225080d1a57SAlexander Aring err = skb->len; 2226080d1a57SAlexander Aring goto out_err; 2227080d1a57SAlexander Aring } 2228080d1a57SAlexander Aring 2229a26c5fd7SAlexander Aring if (!wpan_dev->netdev) { 2230a26c5fd7SAlexander Aring err = -EINVAL; 2231a26c5fd7SAlexander Aring goto out_err; 2232a26c5fd7SAlexander Aring } 2233a26c5fd7SAlexander Aring 2234a26c5fd7SAlexander Aring rdev_lock_llsec_table(rdev, wpan_dev); 2235a26c5fd7SAlexander Aring rdev_get_llsec_table(rdev, wpan_dev, &table); 2236a26c5fd7SAlexander Aring 2237a26c5fd7SAlexander Aring /* TODO make it like station dump */ 2238a26c5fd7SAlexander Aring if (cb->args[2]) 2239a26c5fd7SAlexander Aring goto out; 2240a26c5fd7SAlexander Aring 2241a26c5fd7SAlexander Aring /* TODO look if remove devkey and do some nested attribute */ 2242a26c5fd7SAlexander Aring list_for_each_entry(dpos, &table->devices, list) { 2243a26c5fd7SAlexander Aring list_for_each_entry(kpos, &dpos->keys, list) { 2244a26c5fd7SAlexander Aring if (nl802154_send_devkey(skb, 2245a26c5fd7SAlexander Aring NL802154_CMD_NEW_SEC_LEVEL, 2246a26c5fd7SAlexander Aring NETLINK_CB(cb->skb).portid, 2247a26c5fd7SAlexander Aring cb->nlh->nlmsg_seq, 2248a26c5fd7SAlexander Aring NLM_F_MULTI, rdev, 2249a26c5fd7SAlexander Aring wpan_dev->netdev, 2250a26c5fd7SAlexander Aring dpos->hwaddr, 2251a26c5fd7SAlexander Aring kpos) < 0) { 2252a26c5fd7SAlexander Aring /* TODO */ 2253a26c5fd7SAlexander Aring err = -EIO; 2254a26c5fd7SAlexander Aring rdev_unlock_llsec_table(rdev, wpan_dev); 2255a26c5fd7SAlexander Aring goto out_err; 2256a26c5fd7SAlexander Aring } 2257a26c5fd7SAlexander Aring } 2258a26c5fd7SAlexander Aring } 2259a26c5fd7SAlexander Aring 2260a26c5fd7SAlexander Aring cb->args[2] = 1; 2261a26c5fd7SAlexander Aring 2262a26c5fd7SAlexander Aring out: 2263a26c5fd7SAlexander Aring rdev_unlock_llsec_table(rdev, wpan_dev); 2264a26c5fd7SAlexander Aring err = skb->len; 2265a26c5fd7SAlexander Aring out_err: 2266a26c5fd7SAlexander Aring nl802154_finish_wpan_dev_dump(rdev); 2267a26c5fd7SAlexander Aring 2268a26c5fd7SAlexander Aring return err; 2269a26c5fd7SAlexander Aring } 2270a26c5fd7SAlexander Aring 2271a26c5fd7SAlexander Aring static const struct nla_policy nl802154_devkey_policy[NL802154_DEVKEY_ATTR_MAX + 1] = { 2272a26c5fd7SAlexander Aring [NL802154_DEVKEY_ATTR_FRAME_COUNTER] = { NLA_U32 }, 2273a26c5fd7SAlexander Aring [NL802154_DEVKEY_ATTR_EXTENDED_ADDR] = { NLA_U64 }, 2274a26c5fd7SAlexander Aring [NL802154_DEVKEY_ATTR_ID] = { NLA_NESTED }, 2275a26c5fd7SAlexander Aring }; 2276a26c5fd7SAlexander Aring 2277a26c5fd7SAlexander Aring static int nl802154_add_llsec_devkey(struct sk_buff *skb, struct genl_info *info) 2278a26c5fd7SAlexander Aring { 2279a26c5fd7SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 2280a26c5fd7SAlexander Aring struct net_device *dev = info->user_ptr[1]; 2281a26c5fd7SAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 2282a26c5fd7SAlexander Aring struct nlattr *attrs[NL802154_DEVKEY_ATTR_MAX + 1]; 2283a26c5fd7SAlexander Aring struct ieee802154_llsec_device_key key; 2284a26c5fd7SAlexander Aring __le64 extended_addr; 2285a26c5fd7SAlexander Aring 2286a347b3b3SAlexander Aring if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) 2287a347b3b3SAlexander Aring return -EOPNOTSUPP; 2288a347b3b3SAlexander Aring 2289a26c5fd7SAlexander Aring if (!info->attrs[NL802154_ATTR_SEC_DEVKEY] || 22908cb08174SJohannes Berg nla_parse_nested_deprecated(attrs, NL802154_DEVKEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_DEVKEY], nl802154_devkey_policy, info->extack) < 0) 2291a26c5fd7SAlexander Aring return -EINVAL; 2292a26c5fd7SAlexander Aring 2293a26c5fd7SAlexander Aring if (!attrs[NL802154_DEVKEY_ATTR_FRAME_COUNTER] || 2294a26c5fd7SAlexander Aring !attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]) 2295a26c5fd7SAlexander Aring return -EINVAL; 2296a26c5fd7SAlexander Aring 2297a26c5fd7SAlexander Aring /* TODO change key.id ? */ 2298a26c5fd7SAlexander Aring if (ieee802154_llsec_parse_key_id(attrs[NL802154_DEVKEY_ATTR_ID], 2299a26c5fd7SAlexander Aring &key.key_id) < 0) 2300a26c5fd7SAlexander Aring return -ENOBUFS; 2301a26c5fd7SAlexander Aring 2302a26c5fd7SAlexander Aring /* TODO be32 */ 2303a26c5fd7SAlexander Aring key.frame_counter = nla_get_u32(attrs[NL802154_DEVKEY_ATTR_FRAME_COUNTER]); 2304a26c5fd7SAlexander Aring /* TODO change naming hwaddr -> extended_addr 2305a26c5fd7SAlexander Aring * check unique identifier short+pan OR extended_addr 2306a26c5fd7SAlexander Aring */ 2307a26c5fd7SAlexander Aring extended_addr = nla_get_le64(attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]); 2308a26c5fd7SAlexander Aring return rdev_add_devkey(rdev, wpan_dev, extended_addr, &key); 2309a26c5fd7SAlexander Aring } 2310a26c5fd7SAlexander Aring 2311a26c5fd7SAlexander Aring static int nl802154_del_llsec_devkey(struct sk_buff *skb, struct genl_info *info) 2312a26c5fd7SAlexander Aring { 2313a26c5fd7SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 2314a26c5fd7SAlexander Aring struct net_device *dev = info->user_ptr[1]; 2315a26c5fd7SAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 2316a26c5fd7SAlexander Aring struct nlattr *attrs[NL802154_DEVKEY_ATTR_MAX + 1]; 2317a26c5fd7SAlexander Aring struct ieee802154_llsec_device_key key; 2318a26c5fd7SAlexander Aring __le64 extended_addr; 2319a26c5fd7SAlexander Aring 23206fb80453SAlexander Aring if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) 23216fb80453SAlexander Aring return -EOPNOTSUPP; 23226fb80453SAlexander Aring 232327c74686SAlexander Aring if (!info->attrs[NL802154_ATTR_SEC_DEVKEY] || 232427c74686SAlexander Aring nla_parse_nested_deprecated(attrs, NL802154_DEVKEY_ATTR_MAX, info->attrs[NL802154_ATTR_SEC_DEVKEY], nl802154_devkey_policy, info->extack)) 2325a26c5fd7SAlexander Aring return -EINVAL; 2326a26c5fd7SAlexander Aring 2327a26c5fd7SAlexander Aring if (!attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]) 2328a26c5fd7SAlexander Aring return -EINVAL; 2329a26c5fd7SAlexander Aring 2330a26c5fd7SAlexander Aring /* TODO change key.id ? */ 2331a26c5fd7SAlexander Aring if (ieee802154_llsec_parse_key_id(attrs[NL802154_DEVKEY_ATTR_ID], 2332a26c5fd7SAlexander Aring &key.key_id) < 0) 2333a26c5fd7SAlexander Aring return -ENOBUFS; 2334a26c5fd7SAlexander Aring 2335a26c5fd7SAlexander Aring /* TODO change naming hwaddr -> extended_addr 2336a26c5fd7SAlexander Aring * check unique identifier short+pan OR extended_addr 2337a26c5fd7SAlexander Aring */ 2338a26c5fd7SAlexander Aring extended_addr = nla_get_le64(attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]); 2339a26c5fd7SAlexander Aring return rdev_del_devkey(rdev, wpan_dev, extended_addr, &key); 2340a26c5fd7SAlexander Aring } 2341a26c5fd7SAlexander Aring 2342a26c5fd7SAlexander Aring static int nl802154_send_seclevel(struct sk_buff *msg, u32 cmd, u32 portid, 2343a26c5fd7SAlexander Aring u32 seq, int flags, 2344a26c5fd7SAlexander Aring struct cfg802154_registered_device *rdev, 2345a26c5fd7SAlexander Aring struct net_device *dev, 2346a26c5fd7SAlexander Aring const struct ieee802154_llsec_seclevel *sl) 2347a26c5fd7SAlexander Aring { 2348a26c5fd7SAlexander Aring void *hdr; 2349a26c5fd7SAlexander Aring struct nlattr *nl_seclevel; 2350a26c5fd7SAlexander Aring 2351a26c5fd7SAlexander Aring hdr = nl802154hdr_put(msg, portid, seq, flags, cmd); 2352a26c5fd7SAlexander Aring if (!hdr) 235379c37ca7SMiquel Raynal return -ENOBUFS; 2354a26c5fd7SAlexander Aring 2355a26c5fd7SAlexander Aring if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex)) 2356a26c5fd7SAlexander Aring goto nla_put_failure; 2357a26c5fd7SAlexander Aring 2358ae0be8deSMichal Kubecek nl_seclevel = nla_nest_start_noflag(msg, NL802154_ATTR_SEC_LEVEL); 2359a26c5fd7SAlexander Aring if (!nl_seclevel) 2360a26c5fd7SAlexander Aring goto nla_put_failure; 2361a26c5fd7SAlexander Aring 2362a26c5fd7SAlexander Aring if (nla_put_u32(msg, NL802154_SECLEVEL_ATTR_FRAME, sl->frame_type) || 2363a26c5fd7SAlexander Aring nla_put_u32(msg, NL802154_SECLEVEL_ATTR_LEVELS, sl->sec_levels) || 2364a26c5fd7SAlexander Aring nla_put_u8(msg, NL802154_SECLEVEL_ATTR_DEV_OVERRIDE, 2365a26c5fd7SAlexander Aring sl->device_override)) 2366a26c5fd7SAlexander Aring goto nla_put_failure; 2367a26c5fd7SAlexander Aring 2368a26c5fd7SAlexander Aring if (sl->frame_type == NL802154_FRAME_CMD) { 2369a26c5fd7SAlexander Aring if (nla_put_u32(msg, NL802154_SECLEVEL_ATTR_CMD_FRAME, 2370a26c5fd7SAlexander Aring sl->cmd_frame_id)) 2371a26c5fd7SAlexander Aring goto nla_put_failure; 2372a26c5fd7SAlexander Aring } 2373a26c5fd7SAlexander Aring 2374a26c5fd7SAlexander Aring nla_nest_end(msg, nl_seclevel); 2375a26c5fd7SAlexander Aring genlmsg_end(msg, hdr); 2376a26c5fd7SAlexander Aring 2377a26c5fd7SAlexander Aring return 0; 2378a26c5fd7SAlexander Aring 2379a26c5fd7SAlexander Aring nla_put_failure: 2380a26c5fd7SAlexander Aring genlmsg_cancel(msg, hdr); 2381a26c5fd7SAlexander Aring return -EMSGSIZE; 2382a26c5fd7SAlexander Aring } 2383a26c5fd7SAlexander Aring 2384a26c5fd7SAlexander Aring static int 2385a26c5fd7SAlexander Aring nl802154_dump_llsec_seclevel(struct sk_buff *skb, struct netlink_callback *cb) 2386a26c5fd7SAlexander Aring { 2387a26c5fd7SAlexander Aring struct cfg802154_registered_device *rdev = NULL; 2388a26c5fd7SAlexander Aring struct ieee802154_llsec_seclevel *sl; 2389a26c5fd7SAlexander Aring struct ieee802154_llsec_table *table; 2390a26c5fd7SAlexander Aring struct wpan_dev *wpan_dev; 2391a26c5fd7SAlexander Aring int err; 2392a26c5fd7SAlexander Aring 2393a26c5fd7SAlexander Aring err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev); 2394a26c5fd7SAlexander Aring if (err) 2395a26c5fd7SAlexander Aring return err; 2396a26c5fd7SAlexander Aring 23974c9b4f55SAlexander Aring if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) { 23984c9b4f55SAlexander Aring err = skb->len; 23994c9b4f55SAlexander Aring goto out_err; 24004c9b4f55SAlexander Aring } 24014c9b4f55SAlexander Aring 2402a26c5fd7SAlexander Aring if (!wpan_dev->netdev) { 2403a26c5fd7SAlexander Aring err = -EINVAL; 2404a26c5fd7SAlexander Aring goto out_err; 2405a26c5fd7SAlexander Aring } 2406a26c5fd7SAlexander Aring 2407a26c5fd7SAlexander Aring rdev_lock_llsec_table(rdev, wpan_dev); 2408a26c5fd7SAlexander Aring rdev_get_llsec_table(rdev, wpan_dev, &table); 2409a26c5fd7SAlexander Aring 2410a26c5fd7SAlexander Aring /* TODO make it like station dump */ 2411a26c5fd7SAlexander Aring if (cb->args[2]) 2412a26c5fd7SAlexander Aring goto out; 2413a26c5fd7SAlexander Aring 2414a26c5fd7SAlexander Aring list_for_each_entry(sl, &table->security_levels, list) { 2415a26c5fd7SAlexander Aring if (nl802154_send_seclevel(skb, NL802154_CMD_NEW_SEC_LEVEL, 2416a26c5fd7SAlexander Aring NETLINK_CB(cb->skb).portid, 2417a26c5fd7SAlexander Aring cb->nlh->nlmsg_seq, NLM_F_MULTI, 2418a26c5fd7SAlexander Aring rdev, wpan_dev->netdev, sl) < 0) { 2419a26c5fd7SAlexander Aring /* TODO */ 2420a26c5fd7SAlexander Aring err = -EIO; 2421a26c5fd7SAlexander Aring rdev_unlock_llsec_table(rdev, wpan_dev); 2422a26c5fd7SAlexander Aring goto out_err; 2423a26c5fd7SAlexander Aring } 2424a26c5fd7SAlexander Aring } 2425a26c5fd7SAlexander Aring 2426a26c5fd7SAlexander Aring cb->args[2] = 1; 2427a26c5fd7SAlexander Aring 2428a26c5fd7SAlexander Aring out: 2429a26c5fd7SAlexander Aring rdev_unlock_llsec_table(rdev, wpan_dev); 2430a26c5fd7SAlexander Aring err = skb->len; 2431a26c5fd7SAlexander Aring out_err: 2432a26c5fd7SAlexander Aring nl802154_finish_wpan_dev_dump(rdev); 2433a26c5fd7SAlexander Aring 2434a26c5fd7SAlexander Aring return err; 2435a26c5fd7SAlexander Aring } 2436a26c5fd7SAlexander Aring 2437a26c5fd7SAlexander Aring static const struct nla_policy nl802154_seclevel_policy[NL802154_SECLEVEL_ATTR_MAX + 1] = { 2438a26c5fd7SAlexander Aring [NL802154_SECLEVEL_ATTR_LEVELS] = { .type = NLA_U8 }, 2439a26c5fd7SAlexander Aring [NL802154_SECLEVEL_ATTR_FRAME] = { .type = NLA_U32 }, 2440a26c5fd7SAlexander Aring [NL802154_SECLEVEL_ATTR_CMD_FRAME] = { .type = NLA_U32 }, 2441a26c5fd7SAlexander Aring [NL802154_SECLEVEL_ATTR_DEV_OVERRIDE] = { .type = NLA_U8 }, 2442a26c5fd7SAlexander Aring }; 2443a26c5fd7SAlexander Aring 2444a26c5fd7SAlexander Aring static int 2445a26c5fd7SAlexander Aring llsec_parse_seclevel(struct nlattr *nla, struct ieee802154_llsec_seclevel *sl) 2446a26c5fd7SAlexander Aring { 2447a26c5fd7SAlexander Aring struct nlattr *attrs[NL802154_SECLEVEL_ATTR_MAX + 1]; 2448a26c5fd7SAlexander Aring 24498cb08174SJohannes Berg if (!nla || nla_parse_nested_deprecated(attrs, NL802154_SECLEVEL_ATTR_MAX, nla, nl802154_seclevel_policy, NULL)) 2450a26c5fd7SAlexander Aring return -EINVAL; 2451a26c5fd7SAlexander Aring 2452a26c5fd7SAlexander Aring memset(sl, 0, sizeof(*sl)); 2453a26c5fd7SAlexander Aring 2454a26c5fd7SAlexander Aring if (!attrs[NL802154_SECLEVEL_ATTR_LEVELS] || 2455a26c5fd7SAlexander Aring !attrs[NL802154_SECLEVEL_ATTR_FRAME] || 2456a26c5fd7SAlexander Aring !attrs[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE]) 2457a26c5fd7SAlexander Aring return -EINVAL; 2458a26c5fd7SAlexander Aring 2459a26c5fd7SAlexander Aring sl->sec_levels = nla_get_u8(attrs[NL802154_SECLEVEL_ATTR_LEVELS]); 2460a26c5fd7SAlexander Aring sl->frame_type = nla_get_u32(attrs[NL802154_SECLEVEL_ATTR_FRAME]); 2461a26c5fd7SAlexander Aring sl->device_override = nla_get_u8(attrs[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE]); 2462a26c5fd7SAlexander Aring if (sl->frame_type > NL802154_FRAME_MAX || 2463a26c5fd7SAlexander Aring (sl->device_override != 0 && sl->device_override != 1)) 2464a26c5fd7SAlexander Aring return -EINVAL; 2465a26c5fd7SAlexander Aring 2466a26c5fd7SAlexander Aring if (sl->frame_type == NL802154_FRAME_CMD) { 2467a26c5fd7SAlexander Aring if (!attrs[NL802154_SECLEVEL_ATTR_CMD_FRAME]) 2468a26c5fd7SAlexander Aring return -EINVAL; 2469a26c5fd7SAlexander Aring 2470a26c5fd7SAlexander Aring sl->cmd_frame_id = nla_get_u32(attrs[NL802154_SECLEVEL_ATTR_CMD_FRAME]); 2471a26c5fd7SAlexander Aring if (sl->cmd_frame_id > NL802154_CMD_FRAME_MAX) 2472a26c5fd7SAlexander Aring return -EINVAL; 2473a26c5fd7SAlexander Aring } 2474a26c5fd7SAlexander Aring 2475a26c5fd7SAlexander Aring return 0; 2476a26c5fd7SAlexander Aring } 2477a26c5fd7SAlexander Aring 2478a26c5fd7SAlexander Aring static int nl802154_add_llsec_seclevel(struct sk_buff *skb, 2479a26c5fd7SAlexander Aring struct genl_info *info) 2480a26c5fd7SAlexander Aring { 2481a26c5fd7SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 2482a26c5fd7SAlexander Aring struct net_device *dev = info->user_ptr[1]; 2483a26c5fd7SAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 2484a26c5fd7SAlexander Aring struct ieee802154_llsec_seclevel sl; 2485a26c5fd7SAlexander Aring 24869ec87e32SAlexander Aring if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) 24879ec87e32SAlexander Aring return -EOPNOTSUPP; 24889ec87e32SAlexander Aring 2489a26c5fd7SAlexander Aring if (llsec_parse_seclevel(info->attrs[NL802154_ATTR_SEC_LEVEL], 2490a26c5fd7SAlexander Aring &sl) < 0) 2491a26c5fd7SAlexander Aring return -EINVAL; 2492a26c5fd7SAlexander Aring 2493a26c5fd7SAlexander Aring return rdev_add_seclevel(rdev, wpan_dev, &sl); 2494a26c5fd7SAlexander Aring } 2495a26c5fd7SAlexander Aring 2496a26c5fd7SAlexander Aring static int nl802154_del_llsec_seclevel(struct sk_buff *skb, 2497a26c5fd7SAlexander Aring struct genl_info *info) 2498a26c5fd7SAlexander Aring { 2499a26c5fd7SAlexander Aring struct cfg802154_registered_device *rdev = info->user_ptr[0]; 2500a26c5fd7SAlexander Aring struct net_device *dev = info->user_ptr[1]; 2501a26c5fd7SAlexander Aring struct wpan_dev *wpan_dev = dev->ieee802154_ptr; 2502a26c5fd7SAlexander Aring struct ieee802154_llsec_seclevel sl; 2503a26c5fd7SAlexander Aring 25049dde1309SAlexander Aring if (wpan_dev->iftype == NL802154_IFTYPE_MONITOR) 25059dde1309SAlexander Aring return -EOPNOTSUPP; 25069dde1309SAlexander Aring 2507a26c5fd7SAlexander Aring if (!info->attrs[NL802154_ATTR_SEC_LEVEL] || 2508a26c5fd7SAlexander Aring llsec_parse_seclevel(info->attrs[NL802154_ATTR_SEC_LEVEL], 2509a26c5fd7SAlexander Aring &sl) < 0) 2510a26c5fd7SAlexander Aring return -EINVAL; 2511a26c5fd7SAlexander Aring 2512a26c5fd7SAlexander Aring return rdev_del_seclevel(rdev, wpan_dev, &sl); 2513a26c5fd7SAlexander Aring } 2514a26c5fd7SAlexander Aring #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ 2515a26c5fd7SAlexander Aring 251679fe1a2aSAlexander Aring #define NL802154_FLAG_NEED_WPAN_PHY 0x01 251779fe1a2aSAlexander Aring #define NL802154_FLAG_NEED_NETDEV 0x02 251879fe1a2aSAlexander Aring #define NL802154_FLAG_NEED_RTNL 0x04 251979fe1a2aSAlexander Aring #define NL802154_FLAG_CHECK_NETDEV_UP 0x08 252079fe1a2aSAlexander Aring #define NL802154_FLAG_NEED_WPAN_DEV 0x10 252179fe1a2aSAlexander Aring 252220b0b53aSJakub Kicinski static int nl802154_pre_doit(const struct genl_split_ops *ops, 252320b0b53aSJakub Kicinski struct sk_buff *skb, 252479fe1a2aSAlexander Aring struct genl_info *info) 252579fe1a2aSAlexander Aring { 252679fe1a2aSAlexander Aring struct cfg802154_registered_device *rdev; 252779fe1a2aSAlexander Aring struct wpan_dev *wpan_dev; 252879fe1a2aSAlexander Aring struct net_device *dev; 252979fe1a2aSAlexander Aring bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL; 253079fe1a2aSAlexander Aring 253179fe1a2aSAlexander Aring if (rtnl) 253279fe1a2aSAlexander Aring rtnl_lock(); 253379fe1a2aSAlexander Aring 253479fe1a2aSAlexander Aring if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) { 253579fe1a2aSAlexander Aring rdev = cfg802154_get_dev_from_info(genl_info_net(info), info); 253679fe1a2aSAlexander Aring if (IS_ERR(rdev)) { 253779fe1a2aSAlexander Aring if (rtnl) 253879fe1a2aSAlexander Aring rtnl_unlock(); 253979fe1a2aSAlexander Aring return PTR_ERR(rdev); 254079fe1a2aSAlexander Aring } 254179fe1a2aSAlexander Aring info->user_ptr[0] = rdev; 254279fe1a2aSAlexander Aring } else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV || 254379fe1a2aSAlexander Aring ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) { 254479fe1a2aSAlexander Aring ASSERT_RTNL(); 254579fe1a2aSAlexander Aring wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info), 254679fe1a2aSAlexander Aring info->attrs); 254779fe1a2aSAlexander Aring if (IS_ERR(wpan_dev)) { 254879fe1a2aSAlexander Aring if (rtnl) 254979fe1a2aSAlexander Aring rtnl_unlock(); 255079fe1a2aSAlexander Aring return PTR_ERR(wpan_dev); 255179fe1a2aSAlexander Aring } 255279fe1a2aSAlexander Aring 255379fe1a2aSAlexander Aring dev = wpan_dev->netdev; 255479fe1a2aSAlexander Aring rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy); 255579fe1a2aSAlexander Aring 255679fe1a2aSAlexander Aring if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) { 255779fe1a2aSAlexander Aring if (!dev) { 255879fe1a2aSAlexander Aring if (rtnl) 255979fe1a2aSAlexander Aring rtnl_unlock(); 256079fe1a2aSAlexander Aring return -EINVAL; 256179fe1a2aSAlexander Aring } 256279fe1a2aSAlexander Aring 256379fe1a2aSAlexander Aring info->user_ptr[1] = dev; 256479fe1a2aSAlexander Aring } else { 256579fe1a2aSAlexander Aring info->user_ptr[1] = wpan_dev; 256679fe1a2aSAlexander Aring } 256779fe1a2aSAlexander Aring 256879fe1a2aSAlexander Aring if (dev) { 256979fe1a2aSAlexander Aring if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP && 257079fe1a2aSAlexander Aring !netif_running(dev)) { 257179fe1a2aSAlexander Aring if (rtnl) 257279fe1a2aSAlexander Aring rtnl_unlock(); 257379fe1a2aSAlexander Aring return -ENETDOWN; 257479fe1a2aSAlexander Aring } 257579fe1a2aSAlexander Aring 257679fe1a2aSAlexander Aring dev_hold(dev); 257779fe1a2aSAlexander Aring } 257879fe1a2aSAlexander Aring 257979fe1a2aSAlexander Aring info->user_ptr[0] = rdev; 258079fe1a2aSAlexander Aring } 258179fe1a2aSAlexander Aring 258279fe1a2aSAlexander Aring return 0; 258379fe1a2aSAlexander Aring } 258479fe1a2aSAlexander Aring 258520b0b53aSJakub Kicinski static void nl802154_post_doit(const struct genl_split_ops *ops, 258620b0b53aSJakub Kicinski struct sk_buff *skb, 258779fe1a2aSAlexander Aring struct genl_info *info) 258879fe1a2aSAlexander Aring { 258979fe1a2aSAlexander Aring if (info->user_ptr[1]) { 259079fe1a2aSAlexander Aring if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) { 259179fe1a2aSAlexander Aring struct wpan_dev *wpan_dev = info->user_ptr[1]; 259279fe1a2aSAlexander Aring 259379fe1a2aSAlexander Aring dev_put(wpan_dev->netdev); 259479fe1a2aSAlexander Aring } else { 259579fe1a2aSAlexander Aring dev_put(info->user_ptr[1]); 259679fe1a2aSAlexander Aring } 259779fe1a2aSAlexander Aring } 259879fe1a2aSAlexander Aring 259979fe1a2aSAlexander Aring if (ops->internal_flags & NL802154_FLAG_NEED_RTNL) 260079fe1a2aSAlexander Aring rtnl_unlock(); 260179fe1a2aSAlexander Aring } 260279fe1a2aSAlexander Aring 260379fe1a2aSAlexander Aring static const struct genl_ops nl802154_ops[] = { 2604ca20ce20SAlexander Aring { 2605ca20ce20SAlexander Aring .cmd = NL802154_CMD_GET_WPAN_PHY, 260675cdbdd0SJiri Pirko .validate = GENL_DONT_VALIDATE_STRICT | 260775cdbdd0SJiri Pirko GENL_DONT_VALIDATE_DUMP_STRICT, 2608ca20ce20SAlexander Aring .doit = nl802154_get_wpan_phy, 2609ca20ce20SAlexander Aring .dumpit = nl802154_dump_wpan_phy, 2610ca20ce20SAlexander Aring .done = nl802154_dump_wpan_phy_done, 2611ca20ce20SAlexander Aring /* can be retrieved by unprivileged users */ 2612ca20ce20SAlexander Aring .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | 2613ca20ce20SAlexander Aring NL802154_FLAG_NEED_RTNL, 2614ca20ce20SAlexander Aring }, 26154b96aea0SAlexander Aring { 26164b96aea0SAlexander Aring .cmd = NL802154_CMD_GET_INTERFACE, 2617ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 26184b96aea0SAlexander Aring .doit = nl802154_get_interface, 26194b96aea0SAlexander Aring .dumpit = nl802154_dump_interface, 26204b96aea0SAlexander Aring /* can be retrieved by unprivileged users */ 26214b96aea0SAlexander Aring .internal_flags = NL802154_FLAG_NEED_WPAN_DEV | 26224b96aea0SAlexander Aring NL802154_FLAG_NEED_RTNL, 26234b96aea0SAlexander Aring }, 2624ab0bd561SAlexander Aring { 2625f3ea5e44SAlexander Aring .cmd = NL802154_CMD_NEW_INTERFACE, 2626ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 2627f3ea5e44SAlexander Aring .doit = nl802154_new_interface, 2628f3ea5e44SAlexander Aring .flags = GENL_ADMIN_PERM, 2629f3ea5e44SAlexander Aring .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | 2630f3ea5e44SAlexander Aring NL802154_FLAG_NEED_RTNL, 2631f3ea5e44SAlexander Aring }, 2632f3ea5e44SAlexander Aring { 2633b821ecd4SAlexander Aring .cmd = NL802154_CMD_DEL_INTERFACE, 2634ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 2635b821ecd4SAlexander Aring .doit = nl802154_del_interface, 2636b821ecd4SAlexander Aring .flags = GENL_ADMIN_PERM, 2637b821ecd4SAlexander Aring .internal_flags = NL802154_FLAG_NEED_WPAN_DEV | 2638b821ecd4SAlexander Aring NL802154_FLAG_NEED_RTNL, 2639b821ecd4SAlexander Aring }, 2640b821ecd4SAlexander Aring { 2641ab0bd561SAlexander Aring .cmd = NL802154_CMD_SET_CHANNEL, 2642ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 2643ab0bd561SAlexander Aring .doit = nl802154_set_channel, 2644ab0bd561SAlexander Aring .flags = GENL_ADMIN_PERM, 2645ab0bd561SAlexander Aring .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | 2646ab0bd561SAlexander Aring NL802154_FLAG_NEED_RTNL, 2647ab0bd561SAlexander Aring }, 2648702bf371SAlexander Aring { 2649ba2a9506SAlexander Aring .cmd = NL802154_CMD_SET_CCA_MODE, 2650ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 2651ba2a9506SAlexander Aring .doit = nl802154_set_cca_mode, 2652ba2a9506SAlexander Aring .flags = GENL_ADMIN_PERM, 2653ba2a9506SAlexander Aring .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | 2654ba2a9506SAlexander Aring NL802154_FLAG_NEED_RTNL, 2655ba2a9506SAlexander Aring }, 2656ba2a9506SAlexander Aring { 2657b69644c1SAlexander Aring .cmd = NL802154_CMD_SET_CCA_ED_LEVEL, 2658ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 2659b69644c1SAlexander Aring .doit = nl802154_set_cca_ed_level, 2660b69644c1SAlexander Aring .flags = GENL_ADMIN_PERM, 2661b69644c1SAlexander Aring .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | 2662b69644c1SAlexander Aring NL802154_FLAG_NEED_RTNL, 2663b69644c1SAlexander Aring }, 2664b69644c1SAlexander Aring { 26650f999b09SVarka Bhadram .cmd = NL802154_CMD_SET_TX_POWER, 2666ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 26670f999b09SVarka Bhadram .doit = nl802154_set_tx_power, 26680f999b09SVarka Bhadram .flags = GENL_ADMIN_PERM, 26690f999b09SVarka Bhadram .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | 26700f999b09SVarka Bhadram NL802154_FLAG_NEED_RTNL, 26710f999b09SVarka Bhadram }, 26720f999b09SVarka Bhadram { 267366e5c267SAlexander Aring .cmd = NL802154_CMD_SET_WPAN_PHY_NETNS, 2674ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 267566e5c267SAlexander Aring .doit = nl802154_wpan_phy_netns, 267666e5c267SAlexander Aring .flags = GENL_ADMIN_PERM, 267766e5c267SAlexander Aring .internal_flags = NL802154_FLAG_NEED_WPAN_PHY | 267866e5c267SAlexander Aring NL802154_FLAG_NEED_RTNL, 267966e5c267SAlexander Aring }, 268066e5c267SAlexander Aring { 2681702bf371SAlexander Aring .cmd = NL802154_CMD_SET_PAN_ID, 2682ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 2683702bf371SAlexander Aring .doit = nl802154_set_pan_id, 2684702bf371SAlexander Aring .flags = GENL_ADMIN_PERM, 2685702bf371SAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 2686702bf371SAlexander Aring NL802154_FLAG_NEED_RTNL, 2687702bf371SAlexander Aring }, 26889830c62aSAlexander Aring { 26899830c62aSAlexander Aring .cmd = NL802154_CMD_SET_SHORT_ADDR, 2690ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 26919830c62aSAlexander Aring .doit = nl802154_set_short_addr, 26929830c62aSAlexander Aring .flags = GENL_ADMIN_PERM, 26939830c62aSAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 26949830c62aSAlexander Aring NL802154_FLAG_NEED_RTNL, 26959830c62aSAlexander Aring }, 2696656a999eSAlexander Aring { 2697656a999eSAlexander Aring .cmd = NL802154_CMD_SET_BACKOFF_EXPONENT, 2698ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 2699656a999eSAlexander Aring .doit = nl802154_set_backoff_exponent, 2700656a999eSAlexander Aring .flags = GENL_ADMIN_PERM, 2701656a999eSAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 2702656a999eSAlexander Aring NL802154_FLAG_NEED_RTNL, 2703656a999eSAlexander Aring }, 2704a01ba765SAlexander Aring { 2705a01ba765SAlexander Aring .cmd = NL802154_CMD_SET_MAX_CSMA_BACKOFFS, 2706ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 2707a01ba765SAlexander Aring .doit = nl802154_set_max_csma_backoffs, 2708a01ba765SAlexander Aring .flags = GENL_ADMIN_PERM, 2709a01ba765SAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 2710a01ba765SAlexander Aring NL802154_FLAG_NEED_RTNL, 2711a01ba765SAlexander Aring }, 271217a3a46bSAlexander Aring { 271317a3a46bSAlexander Aring .cmd = NL802154_CMD_SET_MAX_FRAME_RETRIES, 2714ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 271517a3a46bSAlexander Aring .doit = nl802154_set_max_frame_retries, 271617a3a46bSAlexander Aring .flags = GENL_ADMIN_PERM, 271717a3a46bSAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 271817a3a46bSAlexander Aring NL802154_FLAG_NEED_RTNL, 271917a3a46bSAlexander Aring }, 2720c8937a1dSAlexander Aring { 2721c8937a1dSAlexander Aring .cmd = NL802154_CMD_SET_LBT_MODE, 2722ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 2723c8937a1dSAlexander Aring .doit = nl802154_set_lbt_mode, 2724c8937a1dSAlexander Aring .flags = GENL_ADMIN_PERM, 2725c8937a1dSAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 2726c8937a1dSAlexander Aring NL802154_FLAG_NEED_RTNL, 2727c8937a1dSAlexander Aring }, 2728c91208d8SAlexander Aring { 2729c91208d8SAlexander Aring .cmd = NL802154_CMD_SET_ACKREQ_DEFAULT, 2730ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 2731c91208d8SAlexander Aring .doit = nl802154_set_ackreq_default, 2732c91208d8SAlexander Aring .flags = GENL_ADMIN_PERM, 2733c91208d8SAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 2734c91208d8SAlexander Aring NL802154_FLAG_NEED_RTNL, 2735c91208d8SAlexander Aring }, 2736ed3557c9SMiquel Raynal { 2737ed3557c9SMiquel Raynal .cmd = NL802154_CMD_TRIGGER_SCAN, 2738ed3557c9SMiquel Raynal .doit = nl802154_trigger_scan, 2739ed3557c9SMiquel Raynal .flags = GENL_ADMIN_PERM, 2740ed3557c9SMiquel Raynal .internal_flags = NL802154_FLAG_NEED_NETDEV | 2741ed3557c9SMiquel Raynal NL802154_FLAG_CHECK_NETDEV_UP | 2742ed3557c9SMiquel Raynal NL802154_FLAG_NEED_RTNL, 2743ed3557c9SMiquel Raynal }, 2744ed3557c9SMiquel Raynal { 2745ed3557c9SMiquel Raynal .cmd = NL802154_CMD_ABORT_SCAN, 2746ed3557c9SMiquel Raynal .doit = nl802154_abort_scan, 2747ed3557c9SMiquel Raynal .flags = GENL_ADMIN_PERM, 2748ed3557c9SMiquel Raynal .internal_flags = NL802154_FLAG_NEED_NETDEV | 2749ed3557c9SMiquel Raynal NL802154_FLAG_CHECK_NETDEV_UP | 2750ed3557c9SMiquel Raynal NL802154_FLAG_NEED_RTNL, 2751ed3557c9SMiquel Raynal }, 27529bc11450SMiquel Raynal { 27539bc11450SMiquel Raynal .cmd = NL802154_CMD_SEND_BEACONS, 27549bc11450SMiquel Raynal .doit = nl802154_send_beacons, 27559bc11450SMiquel Raynal .flags = GENL_ADMIN_PERM, 27569bc11450SMiquel Raynal .internal_flags = NL802154_FLAG_NEED_NETDEV | 27579bc11450SMiquel Raynal NL802154_FLAG_CHECK_NETDEV_UP | 27589bc11450SMiquel Raynal NL802154_FLAG_NEED_RTNL, 27599bc11450SMiquel Raynal }, 27609bc11450SMiquel Raynal { 27619bc11450SMiquel Raynal .cmd = NL802154_CMD_STOP_BEACONS, 27629bc11450SMiquel Raynal .doit = nl802154_stop_beacons, 27639bc11450SMiquel Raynal .flags = GENL_ADMIN_PERM, 27649bc11450SMiquel Raynal .internal_flags = NL802154_FLAG_NEED_NETDEV | 27659bc11450SMiquel Raynal NL802154_FLAG_CHECK_NETDEV_UP | 27669bc11450SMiquel Raynal NL802154_FLAG_NEED_RTNL, 27679bc11450SMiquel Raynal }, 2768a26c5fd7SAlexander Aring #ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL 2769a26c5fd7SAlexander Aring { 2770a26c5fd7SAlexander Aring .cmd = NL802154_CMD_SET_SEC_PARAMS, 2771ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 2772a26c5fd7SAlexander Aring .doit = nl802154_set_llsec_params, 2773a26c5fd7SAlexander Aring .flags = GENL_ADMIN_PERM, 2774a26c5fd7SAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 2775a26c5fd7SAlexander Aring NL802154_FLAG_NEED_RTNL, 2776a26c5fd7SAlexander Aring }, 2777a26c5fd7SAlexander Aring { 2778a26c5fd7SAlexander Aring .cmd = NL802154_CMD_GET_SEC_KEY, 277975cdbdd0SJiri Pirko .validate = GENL_DONT_VALIDATE_STRICT | 278075cdbdd0SJiri Pirko GENL_DONT_VALIDATE_DUMP_STRICT, 2781a26c5fd7SAlexander Aring /* TODO .doit by matching key id? */ 2782a26c5fd7SAlexander Aring .dumpit = nl802154_dump_llsec_key, 2783a26c5fd7SAlexander Aring .flags = GENL_ADMIN_PERM, 2784a26c5fd7SAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 2785a26c5fd7SAlexander Aring NL802154_FLAG_NEED_RTNL, 2786a26c5fd7SAlexander Aring }, 2787a26c5fd7SAlexander Aring { 2788a26c5fd7SAlexander Aring .cmd = NL802154_CMD_NEW_SEC_KEY, 2789ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 2790a26c5fd7SAlexander Aring .doit = nl802154_add_llsec_key, 2791a26c5fd7SAlexander Aring .flags = GENL_ADMIN_PERM, 2792a26c5fd7SAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 2793a26c5fd7SAlexander Aring NL802154_FLAG_NEED_RTNL, 2794a26c5fd7SAlexander Aring }, 2795a26c5fd7SAlexander Aring { 2796a26c5fd7SAlexander Aring .cmd = NL802154_CMD_DEL_SEC_KEY, 2797ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 2798a26c5fd7SAlexander Aring .doit = nl802154_del_llsec_key, 2799a26c5fd7SAlexander Aring .flags = GENL_ADMIN_PERM, 2800a26c5fd7SAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 2801a26c5fd7SAlexander Aring NL802154_FLAG_NEED_RTNL, 2802a26c5fd7SAlexander Aring }, 2803a26c5fd7SAlexander Aring /* TODO unique identifier must short+pan OR extended_addr */ 2804a26c5fd7SAlexander Aring { 2805a26c5fd7SAlexander Aring .cmd = NL802154_CMD_GET_SEC_DEV, 280675cdbdd0SJiri Pirko .validate = GENL_DONT_VALIDATE_STRICT | 280775cdbdd0SJiri Pirko GENL_DONT_VALIDATE_DUMP_STRICT, 2808a26c5fd7SAlexander Aring /* TODO .doit by matching extended_addr? */ 2809a26c5fd7SAlexander Aring .dumpit = nl802154_dump_llsec_dev, 2810a26c5fd7SAlexander Aring .flags = GENL_ADMIN_PERM, 2811a26c5fd7SAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 2812a26c5fd7SAlexander Aring NL802154_FLAG_NEED_RTNL, 2813a26c5fd7SAlexander Aring }, 2814a26c5fd7SAlexander Aring { 2815a26c5fd7SAlexander Aring .cmd = NL802154_CMD_NEW_SEC_DEV, 2816ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 2817a26c5fd7SAlexander Aring .doit = nl802154_add_llsec_dev, 2818a26c5fd7SAlexander Aring .flags = GENL_ADMIN_PERM, 2819a26c5fd7SAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 2820a26c5fd7SAlexander Aring NL802154_FLAG_NEED_RTNL, 2821a26c5fd7SAlexander Aring }, 2822a26c5fd7SAlexander Aring { 2823a26c5fd7SAlexander Aring .cmd = NL802154_CMD_DEL_SEC_DEV, 2824ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 2825a26c5fd7SAlexander Aring .doit = nl802154_del_llsec_dev, 2826a26c5fd7SAlexander Aring .flags = GENL_ADMIN_PERM, 2827a26c5fd7SAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 2828a26c5fd7SAlexander Aring NL802154_FLAG_NEED_RTNL, 2829a26c5fd7SAlexander Aring }, 2830a26c5fd7SAlexander Aring /* TODO remove complete devkey, put it as nested? */ 2831a26c5fd7SAlexander Aring { 2832a26c5fd7SAlexander Aring .cmd = NL802154_CMD_GET_SEC_DEVKEY, 283375cdbdd0SJiri Pirko .validate = GENL_DONT_VALIDATE_STRICT | 283475cdbdd0SJiri Pirko GENL_DONT_VALIDATE_DUMP_STRICT, 2835a26c5fd7SAlexander Aring /* TODO doit by matching ??? */ 2836a26c5fd7SAlexander Aring .dumpit = nl802154_dump_llsec_devkey, 2837a26c5fd7SAlexander Aring .flags = GENL_ADMIN_PERM, 2838a26c5fd7SAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 2839a26c5fd7SAlexander Aring NL802154_FLAG_NEED_RTNL, 2840a26c5fd7SAlexander Aring }, 2841a26c5fd7SAlexander Aring { 2842a26c5fd7SAlexander Aring .cmd = NL802154_CMD_NEW_SEC_DEVKEY, 2843ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 2844a26c5fd7SAlexander Aring .doit = nl802154_add_llsec_devkey, 2845a26c5fd7SAlexander Aring .flags = GENL_ADMIN_PERM, 2846a26c5fd7SAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 2847a26c5fd7SAlexander Aring NL802154_FLAG_NEED_RTNL, 2848a26c5fd7SAlexander Aring }, 2849a26c5fd7SAlexander Aring { 2850a26c5fd7SAlexander Aring .cmd = NL802154_CMD_DEL_SEC_DEVKEY, 2851ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 2852a26c5fd7SAlexander Aring .doit = nl802154_del_llsec_devkey, 2853a26c5fd7SAlexander Aring .flags = GENL_ADMIN_PERM, 2854a26c5fd7SAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 2855a26c5fd7SAlexander Aring NL802154_FLAG_NEED_RTNL, 2856a26c5fd7SAlexander Aring }, 2857a26c5fd7SAlexander Aring { 2858a26c5fd7SAlexander Aring .cmd = NL802154_CMD_GET_SEC_LEVEL, 285975cdbdd0SJiri Pirko .validate = GENL_DONT_VALIDATE_STRICT | 286075cdbdd0SJiri Pirko GENL_DONT_VALIDATE_DUMP_STRICT, 2861a26c5fd7SAlexander Aring /* TODO .doit by matching frame_type? */ 2862a26c5fd7SAlexander Aring .dumpit = nl802154_dump_llsec_seclevel, 2863a26c5fd7SAlexander Aring .flags = GENL_ADMIN_PERM, 2864a26c5fd7SAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 2865a26c5fd7SAlexander Aring NL802154_FLAG_NEED_RTNL, 2866a26c5fd7SAlexander Aring }, 2867a26c5fd7SAlexander Aring { 2868a26c5fd7SAlexander Aring .cmd = NL802154_CMD_NEW_SEC_LEVEL, 2869ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 2870a26c5fd7SAlexander Aring .doit = nl802154_add_llsec_seclevel, 2871a26c5fd7SAlexander Aring .flags = GENL_ADMIN_PERM, 2872a26c5fd7SAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 2873a26c5fd7SAlexander Aring NL802154_FLAG_NEED_RTNL, 2874a26c5fd7SAlexander Aring }, 2875a26c5fd7SAlexander Aring { 2876a26c5fd7SAlexander Aring .cmd = NL802154_CMD_DEL_SEC_LEVEL, 2877ef6243acSJohannes Berg .validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP, 2878a26c5fd7SAlexander Aring /* TODO match frame_type only? */ 2879a26c5fd7SAlexander Aring .doit = nl802154_del_llsec_seclevel, 2880a26c5fd7SAlexander Aring .flags = GENL_ADMIN_PERM, 2881a26c5fd7SAlexander Aring .internal_flags = NL802154_FLAG_NEED_NETDEV | 2882a26c5fd7SAlexander Aring NL802154_FLAG_NEED_RTNL, 2883a26c5fd7SAlexander Aring }, 2884a26c5fd7SAlexander Aring #endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */ 288579fe1a2aSAlexander Aring }; 288679fe1a2aSAlexander Aring 288756989f6dSJohannes Berg static struct genl_family nl802154_fam __ro_after_init = { 2888489111e5SJohannes Berg .name = NL802154_GENL_NAME, /* have users key off the name instead */ 2889489111e5SJohannes Berg .hdrsize = 0, /* no private header */ 2890489111e5SJohannes Berg .version = 1, /* no particular meaning now */ 2891489111e5SJohannes Berg .maxattr = NL802154_ATTR_MAX, 28923b0f31f2SJohannes Berg .policy = nl802154_policy, 2893489111e5SJohannes Berg .netnsok = true, 2894489111e5SJohannes Berg .pre_doit = nl802154_pre_doit, 2895489111e5SJohannes Berg .post_doit = nl802154_post_doit, 2896489111e5SJohannes Berg .module = THIS_MODULE, 2897489111e5SJohannes Berg .ops = nl802154_ops, 2898489111e5SJohannes Berg .n_ops = ARRAY_SIZE(nl802154_ops), 28999c5d03d3SJakub Kicinski .resv_start_op = NL802154_CMD_DEL_SEC_LEVEL + 1, 2900489111e5SJohannes Berg .mcgrps = nl802154_mcgrps, 2901489111e5SJohannes Berg .n_mcgrps = ARRAY_SIZE(nl802154_mcgrps), 2902489111e5SJohannes Berg }; 2903489111e5SJohannes Berg 290479fe1a2aSAlexander Aring /* initialisation/exit functions */ 290556989f6dSJohannes Berg int __init nl802154_init(void) 290679fe1a2aSAlexander Aring { 2907489111e5SJohannes Berg return genl_register_family(&nl802154_fam); 290879fe1a2aSAlexander Aring } 290979fe1a2aSAlexander Aring 291079fe1a2aSAlexander Aring void nl802154_exit(void) 291179fe1a2aSAlexander Aring { 291279fe1a2aSAlexander Aring genl_unregister_family(&nl802154_fam); 291379fe1a2aSAlexander Aring } 2914