Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Netlink interface for IEEE 802.15.4 stack
0004  *
0005  * Copyright 2007, 2008 Siemens AG
0006  *
0007  * Written by:
0008  * Sergey Lapin <slapin@ossfans.org>
0009  * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
0010  * Maxim Osipov <maxim.osipov@siemens.com>
0011  */
0012 
0013 #include <linux/kernel.h>
0014 #include <linux/slab.h>
0015 #include <linux/if_arp.h>
0016 #include <net/netlink.h>
0017 #include <net/genetlink.h>
0018 #include <net/cfg802154.h>
0019 #include <net/af_ieee802154.h>
0020 #include <net/ieee802154_netdev.h>
0021 #include <net/rtnetlink.h> /* for rtnl_{un,}lock */
0022 #include <linux/nl802154.h>
0023 
0024 #include "ieee802154.h"
0025 #include "rdev-ops.h"
0026 #include "core.h"
0027 
0028 static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid,
0029                   u32 seq, int flags, struct wpan_phy *phy)
0030 {
0031     void *hdr;
0032     int i, pages = 0;
0033     u32 *buf = kcalloc(IEEE802154_MAX_PAGE + 1, sizeof(u32), GFP_KERNEL);
0034 
0035     pr_debug("%s\n", __func__);
0036 
0037     if (!buf)
0038         return -EMSGSIZE;
0039 
0040     hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags,
0041               IEEE802154_LIST_PHY);
0042     if (!hdr)
0043         goto out;
0044 
0045     rtnl_lock();
0046     if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
0047         nla_put_u8(msg, IEEE802154_ATTR_PAGE, phy->current_page) ||
0048         nla_put_u8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel))
0049         goto nla_put_failure;
0050     for (i = 0; i <= IEEE802154_MAX_PAGE; i++) {
0051         if (phy->supported.channels[i])
0052             buf[pages++] = phy->supported.channels[i] | (i << 27);
0053     }
0054     if (pages &&
0055         nla_put(msg, IEEE802154_ATTR_CHANNEL_PAGE_LIST,
0056             pages * sizeof(uint32_t), buf))
0057         goto nla_put_failure;
0058     rtnl_unlock();
0059     kfree(buf);
0060     genlmsg_end(msg, hdr);
0061     return 0;
0062 
0063 nla_put_failure:
0064     rtnl_unlock();
0065     genlmsg_cancel(msg, hdr);
0066 out:
0067     kfree(buf);
0068     return -EMSGSIZE;
0069 }
0070 
0071 int ieee802154_list_phy(struct sk_buff *skb, struct genl_info *info)
0072 {
0073     /* Request for interface name, index, type, IEEE address,
0074      * PAN Id, short address
0075      */
0076     struct sk_buff *msg;
0077     struct wpan_phy *phy;
0078     const char *name;
0079     int rc = -ENOBUFS;
0080 
0081     pr_debug("%s\n", __func__);
0082 
0083     if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
0084         return -EINVAL;
0085 
0086     name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
0087     if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
0088         return -EINVAL; /* phy name should be null-terminated */
0089 
0090     phy = wpan_phy_find(name);
0091     if (!phy)
0092         return -ENODEV;
0093 
0094     msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
0095     if (!msg)
0096         goto out_dev;
0097 
0098     rc = ieee802154_nl_fill_phy(msg, info->snd_portid, info->snd_seq,
0099                     0, phy);
0100     if (rc < 0)
0101         goto out_free;
0102 
0103     wpan_phy_put(phy);
0104 
0105     return genlmsg_reply(msg, info);
0106 out_free:
0107     nlmsg_free(msg);
0108 out_dev:
0109     wpan_phy_put(phy);
0110     return rc;
0111 }
0112 
0113 struct dump_phy_data {
0114     struct sk_buff *skb;
0115     struct netlink_callback *cb;
0116     int idx, s_idx;
0117 };
0118 
0119 static int ieee802154_dump_phy_iter(struct wpan_phy *phy, void *_data)
0120 {
0121     int rc;
0122     struct dump_phy_data *data = _data;
0123 
0124     pr_debug("%s\n", __func__);
0125 
0126     if (data->idx++ < data->s_idx)
0127         return 0;
0128 
0129     rc = ieee802154_nl_fill_phy(data->skb,
0130                     NETLINK_CB(data->cb->skb).portid,
0131                     data->cb->nlh->nlmsg_seq,
0132                     NLM_F_MULTI,
0133                     phy);
0134 
0135     if (rc < 0) {
0136         data->idx--;
0137         return rc;
0138     }
0139 
0140     return 0;
0141 }
0142 
0143 int ieee802154_dump_phy(struct sk_buff *skb, struct netlink_callback *cb)
0144 {
0145     struct dump_phy_data data = {
0146         .cb = cb,
0147         .skb = skb,
0148         .s_idx = cb->args[0],
0149         .idx = 0,
0150     };
0151 
0152     pr_debug("%s\n", __func__);
0153 
0154     wpan_phy_for_each(ieee802154_dump_phy_iter, &data);
0155 
0156     cb->args[0] = data.idx;
0157 
0158     return skb->len;
0159 }
0160 
0161 int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info)
0162 {
0163     struct sk_buff *msg;
0164     struct wpan_phy *phy;
0165     const char *name;
0166     const char *devname;
0167     int rc = -ENOBUFS;
0168     struct net_device *dev;
0169     int type = __IEEE802154_DEV_INVALID;
0170     unsigned char name_assign_type;
0171 
0172     pr_debug("%s\n", __func__);
0173 
0174     if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
0175         return -EINVAL;
0176 
0177     name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
0178     if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
0179         return -EINVAL; /* phy name should be null-terminated */
0180 
0181     if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
0182         devname = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
0183         if (devname[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1]
0184                 != '\0')
0185             return -EINVAL; /* phy name should be null-terminated */
0186         name_assign_type = NET_NAME_USER;
0187     } else  {
0188         devname = "wpan%d";
0189         name_assign_type = NET_NAME_ENUM;
0190     }
0191 
0192     if (strlen(devname) >= IFNAMSIZ)
0193         return -ENAMETOOLONG;
0194 
0195     phy = wpan_phy_find(name);
0196     if (!phy)
0197         return -ENODEV;
0198 
0199     msg = ieee802154_nl_new_reply(info, 0, IEEE802154_ADD_IFACE);
0200     if (!msg)
0201         goto out_dev;
0202 
0203     if (info->attrs[IEEE802154_ATTR_HW_ADDR] &&
0204         nla_len(info->attrs[IEEE802154_ATTR_HW_ADDR]) !=
0205             IEEE802154_ADDR_LEN) {
0206         rc = -EINVAL;
0207         goto nla_put_failure;
0208     }
0209 
0210     if (info->attrs[IEEE802154_ATTR_DEV_TYPE]) {
0211         type = nla_get_u8(info->attrs[IEEE802154_ATTR_DEV_TYPE]);
0212         if (type >= __IEEE802154_DEV_MAX) {
0213             rc = -EINVAL;
0214             goto nla_put_failure;
0215         }
0216     }
0217 
0218     dev = rdev_add_virtual_intf_deprecated(wpan_phy_to_rdev(phy), devname,
0219                            name_assign_type, type);
0220     if (IS_ERR(dev)) {
0221         rc = PTR_ERR(dev);
0222         goto nla_put_failure;
0223     }
0224     dev_hold(dev);
0225 
0226     if (info->attrs[IEEE802154_ATTR_HW_ADDR]) {
0227         struct sockaddr addr;
0228 
0229         addr.sa_family = ARPHRD_IEEE802154;
0230         nla_memcpy(&addr.sa_data, info->attrs[IEEE802154_ATTR_HW_ADDR],
0231                IEEE802154_ADDR_LEN);
0232 
0233         /* strangely enough, some callbacks (inetdev_event) from
0234          * dev_set_mac_address require RTNL_LOCK
0235          */
0236         rtnl_lock();
0237         rc = dev_set_mac_address(dev, &addr, NULL);
0238         rtnl_unlock();
0239         if (rc)
0240             goto dev_unregister;
0241     }
0242 
0243     if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
0244         nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name)) {
0245         rc = -EMSGSIZE;
0246         goto nla_put_failure;
0247     }
0248     dev_put(dev);
0249 
0250     wpan_phy_put(phy);
0251 
0252     return ieee802154_nl_reply(msg, info);
0253 
0254 dev_unregister:
0255     rtnl_lock(); /* del_iface must be called with RTNL lock */
0256     rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev);
0257     dev_put(dev);
0258     rtnl_unlock();
0259 nla_put_failure:
0260     nlmsg_free(msg);
0261 out_dev:
0262     wpan_phy_put(phy);
0263     return rc;
0264 }
0265 
0266 int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info)
0267 {
0268     struct sk_buff *msg;
0269     struct wpan_phy *phy;
0270     const char *name;
0271     int rc;
0272     struct net_device *dev;
0273 
0274     pr_debug("%s\n", __func__);
0275 
0276     if (!info->attrs[IEEE802154_ATTR_DEV_NAME])
0277         return -EINVAL;
0278 
0279     name = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
0280     if (name[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1] != '\0')
0281         return -EINVAL; /* name should be null-terminated */
0282 
0283     rc = -ENODEV;
0284     dev = dev_get_by_name(genl_info_net(info), name);
0285     if (!dev)
0286         return rc;
0287     if (dev->type != ARPHRD_IEEE802154)
0288         goto out;
0289 
0290     phy = dev->ieee802154_ptr->wpan_phy;
0291     BUG_ON(!phy);
0292     get_device(&phy->dev);
0293 
0294     rc = -EINVAL;
0295     /* phy name is optional, but should be checked if it's given */
0296     if (info->attrs[IEEE802154_ATTR_PHY_NAME]) {
0297         struct wpan_phy *phy2;
0298 
0299         const char *pname =
0300             nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
0301         if (pname[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1]
0302                 != '\0')
0303             /* name should be null-terminated */
0304             goto out_dev;
0305 
0306         phy2 = wpan_phy_find(pname);
0307         if (!phy2)
0308             goto out_dev;
0309 
0310         if (phy != phy2) {
0311             wpan_phy_put(phy2);
0312             goto out_dev;
0313         }
0314     }
0315 
0316     rc = -ENOBUFS;
0317 
0318     msg = ieee802154_nl_new_reply(info, 0, IEEE802154_DEL_IFACE);
0319     if (!msg)
0320         goto out_dev;
0321 
0322     rtnl_lock();
0323     rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev);
0324 
0325     /* We don't have device anymore */
0326     dev_put(dev);
0327     dev = NULL;
0328 
0329     rtnl_unlock();
0330 
0331     if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
0332         nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, name))
0333         goto nla_put_failure;
0334     wpan_phy_put(phy);
0335 
0336     return ieee802154_nl_reply(msg, info);
0337 
0338 nla_put_failure:
0339     nlmsg_free(msg);
0340 out_dev:
0341     wpan_phy_put(phy);
0342 out:
0343     dev_put(dev);
0344 
0345     return rc;
0346 }