linux/net/ieee802154/nl-phy.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Netlink interface for IEEE 802.15.4 stack
   4 *
   5 * Copyright 2007, 2008 Siemens AG
   6 *
   7 * Written by:
   8 * Sergey Lapin <slapin@ossfans.org>
   9 * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
  10 * Maxim Osipov <maxim.osipov@siemens.com>
  11 */
  12
  13#include <linux/kernel.h>
  14#include <linux/slab.h>
  15#include <linux/if_arp.h>
  16#include <net/netlink.h>
  17#include <net/genetlink.h>
  18#include <net/cfg802154.h>
  19#include <net/af_ieee802154.h>
  20#include <net/ieee802154_netdev.h>
  21#include <net/rtnetlink.h> /* for rtnl_{un,}lock */
  22#include <linux/nl802154.h>
  23
  24#include "ieee802154.h"
  25#include "rdev-ops.h"
  26#include "core.h"
  27
  28static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid,
  29                                  u32 seq, int flags, struct wpan_phy *phy)
  30{
  31        void *hdr;
  32        int i, pages = 0;
  33        uint32_t *buf = kcalloc(32, sizeof(uint32_t), GFP_KERNEL);
  34
  35        pr_debug("%s\n", __func__);
  36
  37        if (!buf)
  38                return -EMSGSIZE;
  39
  40        hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags,
  41                          IEEE802154_LIST_PHY);
  42        if (!hdr)
  43                goto out;
  44
  45        rtnl_lock();
  46        if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
  47            nla_put_u8(msg, IEEE802154_ATTR_PAGE, phy->current_page) ||
  48            nla_put_u8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel))
  49                goto nla_put_failure;
  50        for (i = 0; i < 32; i++) {
  51                if (phy->supported.channels[i])
  52                        buf[pages++] = phy->supported.channels[i] | (i << 27);
  53        }
  54        if (pages &&
  55            nla_put(msg, IEEE802154_ATTR_CHANNEL_PAGE_LIST,
  56                    pages * sizeof(uint32_t), buf))
  57                goto nla_put_failure;
  58        rtnl_unlock();
  59        kfree(buf);
  60        genlmsg_end(msg, hdr);
  61        return 0;
  62
  63nla_put_failure:
  64        rtnl_unlock();
  65        genlmsg_cancel(msg, hdr);
  66out:
  67        kfree(buf);
  68        return -EMSGSIZE;
  69}
  70
  71int ieee802154_list_phy(struct sk_buff *skb, struct genl_info *info)
  72{
  73        /* Request for interface name, index, type, IEEE address,
  74         * PAN Id, short address
  75         */
  76        struct sk_buff *msg;
  77        struct wpan_phy *phy;
  78        const char *name;
  79        int rc = -ENOBUFS;
  80
  81        pr_debug("%s\n", __func__);
  82
  83        if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
  84                return -EINVAL;
  85
  86        name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
  87        if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
  88                return -EINVAL; /* phy name should be null-terminated */
  89
  90        phy = wpan_phy_find(name);
  91        if (!phy)
  92                return -ENODEV;
  93
  94        msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
  95        if (!msg)
  96                goto out_dev;
  97
  98        rc = ieee802154_nl_fill_phy(msg, info->snd_portid, info->snd_seq,
  99                                    0, phy);
 100        if (rc < 0)
 101                goto out_free;
 102
 103        wpan_phy_put(phy);
 104
 105        return genlmsg_reply(msg, info);
 106out_free:
 107        nlmsg_free(msg);
 108out_dev:
 109        wpan_phy_put(phy);
 110        return rc;
 111}
 112
 113struct dump_phy_data {
 114        struct sk_buff *skb;
 115        struct netlink_callback *cb;
 116        int idx, s_idx;
 117};
 118
 119static int ieee802154_dump_phy_iter(struct wpan_phy *phy, void *_data)
 120{
 121        int rc;
 122        struct dump_phy_data *data = _data;
 123
 124        pr_debug("%s\n", __func__);
 125
 126        if (data->idx++ < data->s_idx)
 127                return 0;
 128
 129        rc = ieee802154_nl_fill_phy(data->skb,
 130                                    NETLINK_CB(data->cb->skb).portid,
 131                                    data->cb->nlh->nlmsg_seq,
 132                                    NLM_F_MULTI,
 133                                    phy);
 134
 135        if (rc < 0) {
 136                data->idx--;
 137                return rc;
 138        }
 139
 140        return 0;
 141}
 142
 143int ieee802154_dump_phy(struct sk_buff *skb, struct netlink_callback *cb)
 144{
 145        struct dump_phy_data data = {
 146                .cb = cb,
 147                .skb = skb,
 148                .s_idx = cb->args[0],
 149                .idx = 0,
 150        };
 151
 152        pr_debug("%s\n", __func__);
 153
 154        wpan_phy_for_each(ieee802154_dump_phy_iter, &data);
 155
 156        cb->args[0] = data.idx;
 157
 158        return skb->len;
 159}
 160
 161int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info)
 162{
 163        struct sk_buff *msg;
 164        struct wpan_phy *phy;
 165        const char *name;
 166        const char *devname;
 167        int rc = -ENOBUFS;
 168        struct net_device *dev;
 169        int type = __IEEE802154_DEV_INVALID;
 170        unsigned char name_assign_type;
 171
 172        pr_debug("%s\n", __func__);
 173
 174        if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
 175                return -EINVAL;
 176
 177        name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
 178        if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
 179                return -EINVAL; /* phy name should be null-terminated */
 180
 181        if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
 182                devname = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
 183                if (devname[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1]
 184                                != '\0')
 185                        return -EINVAL; /* phy name should be null-terminated */
 186                name_assign_type = NET_NAME_USER;
 187        } else  {
 188                devname = "wpan%d";
 189                name_assign_type = NET_NAME_ENUM;
 190        }
 191
 192        if (strlen(devname) >= IFNAMSIZ)
 193                return -ENAMETOOLONG;
 194
 195        phy = wpan_phy_find(name);
 196        if (!phy)
 197                return -ENODEV;
 198
 199        msg = ieee802154_nl_new_reply(info, 0, IEEE802154_ADD_IFACE);
 200        if (!msg)
 201                goto out_dev;
 202
 203        if (info->attrs[IEEE802154_ATTR_HW_ADDR] &&
 204            nla_len(info->attrs[IEEE802154_ATTR_HW_ADDR]) !=
 205                        IEEE802154_ADDR_LEN) {
 206                rc = -EINVAL;
 207                goto nla_put_failure;
 208        }
 209
 210        if (info->attrs[IEEE802154_ATTR_DEV_TYPE]) {
 211                type = nla_get_u8(info->attrs[IEEE802154_ATTR_DEV_TYPE]);
 212                if (type >= __IEEE802154_DEV_MAX) {
 213                        rc = -EINVAL;
 214                        goto nla_put_failure;
 215                }
 216        }
 217
 218        dev = rdev_add_virtual_intf_deprecated(wpan_phy_to_rdev(phy), devname,
 219                                               name_assign_type, type);
 220        if (IS_ERR(dev)) {
 221                rc = PTR_ERR(dev);
 222                goto nla_put_failure;
 223        }
 224        dev_hold(dev);
 225
 226        if (info->attrs[IEEE802154_ATTR_HW_ADDR]) {
 227                struct sockaddr addr;
 228
 229                addr.sa_family = ARPHRD_IEEE802154;
 230                nla_memcpy(&addr.sa_data, info->attrs[IEEE802154_ATTR_HW_ADDR],
 231                           IEEE802154_ADDR_LEN);
 232
 233                /* strangely enough, some callbacks (inetdev_event) from
 234                 * dev_set_mac_address require RTNL_LOCK
 235                 */
 236                rtnl_lock();
 237                rc = dev_set_mac_address(dev, &addr, NULL);
 238                rtnl_unlock();
 239                if (rc)
 240                        goto dev_unregister;
 241        }
 242
 243        if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
 244            nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name)) {
 245                rc = -EMSGSIZE;
 246                goto nla_put_failure;
 247        }
 248        dev_put(dev);
 249
 250        wpan_phy_put(phy);
 251
 252        return ieee802154_nl_reply(msg, info);
 253
 254dev_unregister:
 255        rtnl_lock(); /* del_iface must be called with RTNL lock */
 256        rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev);
 257        dev_put(dev);
 258        rtnl_unlock();
 259nla_put_failure:
 260        nlmsg_free(msg);
 261out_dev:
 262        wpan_phy_put(phy);
 263        return rc;
 264}
 265
 266int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info)
 267{
 268        struct sk_buff *msg;
 269        struct wpan_phy *phy;
 270        const char *name;
 271        int rc;
 272        struct net_device *dev;
 273
 274        pr_debug("%s\n", __func__);
 275
 276        if (!info->attrs[IEEE802154_ATTR_DEV_NAME])
 277                return -EINVAL;
 278
 279        name = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
 280        if (name[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1] != '\0')
 281                return -EINVAL; /* name should be null-terminated */
 282
 283        rc = -ENODEV;
 284        dev = dev_get_by_name(genl_info_net(info), name);
 285        if (!dev)
 286                return rc;
 287        if (dev->type != ARPHRD_IEEE802154)
 288                goto out;
 289
 290        phy = dev->ieee802154_ptr->wpan_phy;
 291        BUG_ON(!phy);
 292        get_device(&phy->dev);
 293
 294        rc = -EINVAL;
 295        /* phy name is optional, but should be checked if it's given */
 296        if (info->attrs[IEEE802154_ATTR_PHY_NAME]) {
 297                struct wpan_phy *phy2;
 298
 299                const char *pname =
 300                        nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
 301                if (pname[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1]
 302                                != '\0')
 303                        /* name should be null-terminated */
 304                        goto out_dev;
 305
 306                phy2 = wpan_phy_find(pname);
 307                if (!phy2)
 308                        goto out_dev;
 309
 310                if (phy != phy2) {
 311                        wpan_phy_put(phy2);
 312                        goto out_dev;
 313                }
 314        }
 315
 316        rc = -ENOBUFS;
 317
 318        msg = ieee802154_nl_new_reply(info, 0, IEEE802154_DEL_IFACE);
 319        if (!msg)
 320                goto out_dev;
 321
 322        rtnl_lock();
 323        rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev);
 324
 325        /* We don't have device anymore */
 326        dev_put(dev);
 327        dev = NULL;
 328
 329        rtnl_unlock();
 330
 331        if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
 332            nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, name))
 333                goto nla_put_failure;
 334        wpan_phy_put(phy);
 335
 336        return ieee802154_nl_reply(msg, info);
 337
 338nla_put_failure:
 339        nlmsg_free(msg);
 340out_dev:
 341        wpan_phy_put(phy);
 342out:
 343        dev_put(dev);
 344
 345        return rc;
 346}
 347