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