162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Netlink interface for IEEE 802.15.4 stack 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2007, 2008 Siemens AG 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Written by: 862306a36Sopenharmony_ci * Sergey Lapin <slapin@ossfans.org> 962306a36Sopenharmony_ci * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com> 1062306a36Sopenharmony_ci * Maxim Osipov <maxim.osipov@siemens.com> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/if_arp.h> 1662306a36Sopenharmony_ci#include <net/netlink.h> 1762306a36Sopenharmony_ci#include <net/genetlink.h> 1862306a36Sopenharmony_ci#include <net/cfg802154.h> 1962306a36Sopenharmony_ci#include <net/af_ieee802154.h> 2062306a36Sopenharmony_ci#include <net/ieee802154_netdev.h> 2162306a36Sopenharmony_ci#include <net/rtnetlink.h> /* for rtnl_{un,}lock */ 2262306a36Sopenharmony_ci#include <linux/nl802154.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "ieee802154.h" 2562306a36Sopenharmony_ci#include "rdev-ops.h" 2662306a36Sopenharmony_ci#include "core.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid, 2962306a36Sopenharmony_ci u32 seq, int flags, struct wpan_phy *phy) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci void *hdr; 3262306a36Sopenharmony_ci int i, pages = 0; 3362306a36Sopenharmony_ci u32 *buf = kcalloc(IEEE802154_MAX_PAGE + 1, sizeof(u32), GFP_KERNEL); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci pr_debug("%s\n", __func__); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (!buf) 3862306a36Sopenharmony_ci return -EMSGSIZE; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags, 4162306a36Sopenharmony_ci IEEE802154_LIST_PHY); 4262306a36Sopenharmony_ci if (!hdr) 4362306a36Sopenharmony_ci goto out; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci rtnl_lock(); 4662306a36Sopenharmony_ci if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) || 4762306a36Sopenharmony_ci nla_put_u8(msg, IEEE802154_ATTR_PAGE, phy->current_page) || 4862306a36Sopenharmony_ci nla_put_u8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel)) 4962306a36Sopenharmony_ci goto nla_put_failure; 5062306a36Sopenharmony_ci for (i = 0; i <= IEEE802154_MAX_PAGE; i++) { 5162306a36Sopenharmony_ci if (phy->supported.channels[i]) 5262306a36Sopenharmony_ci buf[pages++] = phy->supported.channels[i] | (i << 27); 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci if (pages && 5562306a36Sopenharmony_ci nla_put(msg, IEEE802154_ATTR_CHANNEL_PAGE_LIST, 5662306a36Sopenharmony_ci pages * sizeof(uint32_t), buf)) 5762306a36Sopenharmony_ci goto nla_put_failure; 5862306a36Sopenharmony_ci rtnl_unlock(); 5962306a36Sopenharmony_ci kfree(buf); 6062306a36Sopenharmony_ci genlmsg_end(msg, hdr); 6162306a36Sopenharmony_ci return 0; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cinla_put_failure: 6462306a36Sopenharmony_ci rtnl_unlock(); 6562306a36Sopenharmony_ci genlmsg_cancel(msg, hdr); 6662306a36Sopenharmony_ciout: 6762306a36Sopenharmony_ci kfree(buf); 6862306a36Sopenharmony_ci return -EMSGSIZE; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ciint ieee802154_list_phy(struct sk_buff *skb, struct genl_info *info) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci /* Request for interface name, index, type, IEEE address, 7462306a36Sopenharmony_ci * PAN Id, short address 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_ci struct sk_buff *msg; 7762306a36Sopenharmony_ci struct wpan_phy *phy; 7862306a36Sopenharmony_ci const char *name; 7962306a36Sopenharmony_ci int rc = -ENOBUFS; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci pr_debug("%s\n", __func__); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (!info->attrs[IEEE802154_ATTR_PHY_NAME]) 8462306a36Sopenharmony_ci return -EINVAL; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]); 8762306a36Sopenharmony_ci if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0') 8862306a36Sopenharmony_ci return -EINVAL; /* phy name should be null-terminated */ 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci phy = wpan_phy_find(name); 9162306a36Sopenharmony_ci if (!phy) 9262306a36Sopenharmony_ci return -ENODEV; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 9562306a36Sopenharmony_ci if (!msg) 9662306a36Sopenharmony_ci goto out_dev; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci rc = ieee802154_nl_fill_phy(msg, info->snd_portid, info->snd_seq, 9962306a36Sopenharmony_ci 0, phy); 10062306a36Sopenharmony_ci if (rc < 0) 10162306a36Sopenharmony_ci goto out_free; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci wpan_phy_put(phy); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return genlmsg_reply(msg, info); 10662306a36Sopenharmony_ciout_free: 10762306a36Sopenharmony_ci nlmsg_free(msg); 10862306a36Sopenharmony_ciout_dev: 10962306a36Sopenharmony_ci wpan_phy_put(phy); 11062306a36Sopenharmony_ci return rc; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistruct dump_phy_data { 11462306a36Sopenharmony_ci struct sk_buff *skb; 11562306a36Sopenharmony_ci struct netlink_callback *cb; 11662306a36Sopenharmony_ci int idx, s_idx; 11762306a36Sopenharmony_ci}; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int ieee802154_dump_phy_iter(struct wpan_phy *phy, void *_data) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci int rc; 12262306a36Sopenharmony_ci struct dump_phy_data *data = _data; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci pr_debug("%s\n", __func__); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (data->idx++ < data->s_idx) 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci rc = ieee802154_nl_fill_phy(data->skb, 13062306a36Sopenharmony_ci NETLINK_CB(data->cb->skb).portid, 13162306a36Sopenharmony_ci data->cb->nlh->nlmsg_seq, 13262306a36Sopenharmony_ci NLM_F_MULTI, 13362306a36Sopenharmony_ci phy); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (rc < 0) { 13662306a36Sopenharmony_ci data->idx--; 13762306a36Sopenharmony_ci return rc; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ciint ieee802154_dump_phy(struct sk_buff *skb, struct netlink_callback *cb) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct dump_phy_data data = { 14662306a36Sopenharmony_ci .cb = cb, 14762306a36Sopenharmony_ci .skb = skb, 14862306a36Sopenharmony_ci .s_idx = cb->args[0], 14962306a36Sopenharmony_ci .idx = 0, 15062306a36Sopenharmony_ci }; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci pr_debug("%s\n", __func__); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci wpan_phy_for_each(ieee802154_dump_phy_iter, &data); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci cb->args[0] = data.idx; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return skb->len; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ciint ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct sk_buff *msg; 16462306a36Sopenharmony_ci struct wpan_phy *phy; 16562306a36Sopenharmony_ci const char *name; 16662306a36Sopenharmony_ci const char *devname; 16762306a36Sopenharmony_ci int rc = -ENOBUFS; 16862306a36Sopenharmony_ci struct net_device *dev; 16962306a36Sopenharmony_ci int type = __IEEE802154_DEV_INVALID; 17062306a36Sopenharmony_ci unsigned char name_assign_type; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci pr_debug("%s\n", __func__); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (!info->attrs[IEEE802154_ATTR_PHY_NAME]) 17562306a36Sopenharmony_ci return -EINVAL; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]); 17862306a36Sopenharmony_ci if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0') 17962306a36Sopenharmony_ci return -EINVAL; /* phy name should be null-terminated */ 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (info->attrs[IEEE802154_ATTR_DEV_NAME]) { 18262306a36Sopenharmony_ci devname = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]); 18362306a36Sopenharmony_ci if (devname[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1] 18462306a36Sopenharmony_ci != '\0') 18562306a36Sopenharmony_ci return -EINVAL; /* phy name should be null-terminated */ 18662306a36Sopenharmony_ci name_assign_type = NET_NAME_USER; 18762306a36Sopenharmony_ci } else { 18862306a36Sopenharmony_ci devname = "wpan%d"; 18962306a36Sopenharmony_ci name_assign_type = NET_NAME_ENUM; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (strlen(devname) >= IFNAMSIZ) 19362306a36Sopenharmony_ci return -ENAMETOOLONG; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci phy = wpan_phy_find(name); 19662306a36Sopenharmony_ci if (!phy) 19762306a36Sopenharmony_ci return -ENODEV; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci msg = ieee802154_nl_new_reply(info, 0, IEEE802154_ADD_IFACE); 20062306a36Sopenharmony_ci if (!msg) 20162306a36Sopenharmony_ci goto out_dev; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (info->attrs[IEEE802154_ATTR_HW_ADDR] && 20462306a36Sopenharmony_ci nla_len(info->attrs[IEEE802154_ATTR_HW_ADDR]) != 20562306a36Sopenharmony_ci IEEE802154_ADDR_LEN) { 20662306a36Sopenharmony_ci rc = -EINVAL; 20762306a36Sopenharmony_ci goto nla_put_failure; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (info->attrs[IEEE802154_ATTR_DEV_TYPE]) { 21162306a36Sopenharmony_ci type = nla_get_u8(info->attrs[IEEE802154_ATTR_DEV_TYPE]); 21262306a36Sopenharmony_ci if (type >= __IEEE802154_DEV_MAX) { 21362306a36Sopenharmony_ci rc = -EINVAL; 21462306a36Sopenharmony_ci goto nla_put_failure; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci } 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci dev = rdev_add_virtual_intf_deprecated(wpan_phy_to_rdev(phy), devname, 21962306a36Sopenharmony_ci name_assign_type, type); 22062306a36Sopenharmony_ci if (IS_ERR(dev)) { 22162306a36Sopenharmony_ci rc = PTR_ERR(dev); 22262306a36Sopenharmony_ci goto nla_put_failure; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci dev_hold(dev); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci if (info->attrs[IEEE802154_ATTR_HW_ADDR]) { 22762306a36Sopenharmony_ci struct sockaddr addr; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci addr.sa_family = ARPHRD_IEEE802154; 23062306a36Sopenharmony_ci nla_memcpy(&addr.sa_data, info->attrs[IEEE802154_ATTR_HW_ADDR], 23162306a36Sopenharmony_ci IEEE802154_ADDR_LEN); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* strangely enough, some callbacks (inetdev_event) from 23462306a36Sopenharmony_ci * dev_set_mac_address require RTNL_LOCK 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ci rtnl_lock(); 23762306a36Sopenharmony_ci rc = dev_set_mac_address(dev, &addr, NULL); 23862306a36Sopenharmony_ci rtnl_unlock(); 23962306a36Sopenharmony_ci if (rc) 24062306a36Sopenharmony_ci goto dev_unregister; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) || 24462306a36Sopenharmony_ci nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name)) { 24562306a36Sopenharmony_ci rc = -EMSGSIZE; 24662306a36Sopenharmony_ci goto nla_put_failure; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci dev_put(dev); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci wpan_phy_put(phy); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return ieee802154_nl_reply(msg, info); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cidev_unregister: 25562306a36Sopenharmony_ci rtnl_lock(); /* del_iface must be called with RTNL lock */ 25662306a36Sopenharmony_ci rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev); 25762306a36Sopenharmony_ci dev_put(dev); 25862306a36Sopenharmony_ci rtnl_unlock(); 25962306a36Sopenharmony_cinla_put_failure: 26062306a36Sopenharmony_ci nlmsg_free(msg); 26162306a36Sopenharmony_ciout_dev: 26262306a36Sopenharmony_ci wpan_phy_put(phy); 26362306a36Sopenharmony_ci return rc; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ciint ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci struct sk_buff *msg; 26962306a36Sopenharmony_ci struct wpan_phy *phy; 27062306a36Sopenharmony_ci const char *name; 27162306a36Sopenharmony_ci int rc; 27262306a36Sopenharmony_ci struct net_device *dev; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci pr_debug("%s\n", __func__); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (!info->attrs[IEEE802154_ATTR_DEV_NAME]) 27762306a36Sopenharmony_ci return -EINVAL; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci name = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]); 28062306a36Sopenharmony_ci if (name[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1] != '\0') 28162306a36Sopenharmony_ci return -EINVAL; /* name should be null-terminated */ 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci rc = -ENODEV; 28462306a36Sopenharmony_ci dev = dev_get_by_name(genl_info_net(info), name); 28562306a36Sopenharmony_ci if (!dev) 28662306a36Sopenharmony_ci return rc; 28762306a36Sopenharmony_ci if (dev->type != ARPHRD_IEEE802154) 28862306a36Sopenharmony_ci goto out; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci phy = dev->ieee802154_ptr->wpan_phy; 29162306a36Sopenharmony_ci BUG_ON(!phy); 29262306a36Sopenharmony_ci get_device(&phy->dev); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci rc = -EINVAL; 29562306a36Sopenharmony_ci /* phy name is optional, but should be checked if it's given */ 29662306a36Sopenharmony_ci if (info->attrs[IEEE802154_ATTR_PHY_NAME]) { 29762306a36Sopenharmony_ci struct wpan_phy *phy2; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci const char *pname = 30062306a36Sopenharmony_ci nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]); 30162306a36Sopenharmony_ci if (pname[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] 30262306a36Sopenharmony_ci != '\0') 30362306a36Sopenharmony_ci /* name should be null-terminated */ 30462306a36Sopenharmony_ci goto out_dev; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci phy2 = wpan_phy_find(pname); 30762306a36Sopenharmony_ci if (!phy2) 30862306a36Sopenharmony_ci goto out_dev; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (phy != phy2) { 31162306a36Sopenharmony_ci wpan_phy_put(phy2); 31262306a36Sopenharmony_ci goto out_dev; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci rc = -ENOBUFS; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci msg = ieee802154_nl_new_reply(info, 0, IEEE802154_DEL_IFACE); 31962306a36Sopenharmony_ci if (!msg) 32062306a36Sopenharmony_ci goto out_dev; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci rtnl_lock(); 32362306a36Sopenharmony_ci rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* We don't have device anymore */ 32662306a36Sopenharmony_ci dev_put(dev); 32762306a36Sopenharmony_ci dev = NULL; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci rtnl_unlock(); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) || 33262306a36Sopenharmony_ci nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, name)) 33362306a36Sopenharmony_ci goto nla_put_failure; 33462306a36Sopenharmony_ci wpan_phy_put(phy); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci return ieee802154_nl_reply(msg, info); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cinla_put_failure: 33962306a36Sopenharmony_ci nlmsg_free(msg); 34062306a36Sopenharmony_ciout_dev: 34162306a36Sopenharmony_ci wpan_phy_put(phy); 34262306a36Sopenharmony_ciout: 34362306a36Sopenharmony_ci dev_put(dev); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci return rc; 34662306a36Sopenharmony_ci} 347