root/net/ieee802154/nl-phy.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ieee802154_nl_fill_phy
  2. ieee802154_list_phy
  3. ieee802154_dump_phy_iter
  4. ieee802154_dump_phy
  5. ieee802154_add_iface
  6. ieee802154_del_iface

   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 
  28 static 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 
  63 nla_put_failure:
  64         rtnl_unlock();
  65         genlmsg_cancel(msg, hdr);
  66 out:
  67         kfree(buf);
  68         return -EMSGSIZE;
  69 }
  70 
  71 int 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);
 106 out_free:
 107         nlmsg_free(msg);
 108 out_dev:
 109         wpan_phy_put(phy);
 110         return rc;
 111 }
 112 
 113 struct dump_phy_data {
 114         struct sk_buff *skb;
 115         struct netlink_callback *cb;
 116         int idx, s_idx;
 117 };
 118 
 119 static 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 
 143 int 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 
 161 int 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                 goto nla_put_failure;
 246         dev_put(dev);
 247 
 248         wpan_phy_put(phy);
 249 
 250         return ieee802154_nl_reply(msg, info);
 251 
 252 dev_unregister:
 253         rtnl_lock(); /* del_iface must be called with RTNL lock */
 254         rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev);
 255         dev_put(dev);
 256         rtnl_unlock();
 257 nla_put_failure:
 258         nlmsg_free(msg);
 259 out_dev:
 260         wpan_phy_put(phy);
 261         return rc;
 262 }
 263 
 264 int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info)
 265 {
 266         struct sk_buff *msg;
 267         struct wpan_phy *phy;
 268         const char *name;
 269         int rc;
 270         struct net_device *dev;
 271 
 272         pr_debug("%s\n", __func__);
 273 
 274         if (!info->attrs[IEEE802154_ATTR_DEV_NAME])
 275                 return -EINVAL;
 276 
 277         name = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
 278         if (name[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1] != '\0')
 279                 return -EINVAL; /* name should be null-terminated */
 280 
 281         rc = -ENODEV;
 282         dev = dev_get_by_name(genl_info_net(info), name);
 283         if (!dev)
 284                 return rc;
 285         if (dev->type != ARPHRD_IEEE802154)
 286                 goto out;
 287 
 288         phy = dev->ieee802154_ptr->wpan_phy;
 289         BUG_ON(!phy);
 290         get_device(&phy->dev);
 291 
 292         rc = -EINVAL;
 293         /* phy name is optional, but should be checked if it's given */
 294         if (info->attrs[IEEE802154_ATTR_PHY_NAME]) {
 295                 struct wpan_phy *phy2;
 296 
 297                 const char *pname =
 298                         nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
 299                 if (pname[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1]
 300                                 != '\0')
 301                         /* name should be null-terminated */
 302                         goto out_dev;
 303 
 304                 phy2 = wpan_phy_find(pname);
 305                 if (!phy2)
 306                         goto out_dev;
 307 
 308                 if (phy != phy2) {
 309                         wpan_phy_put(phy2);
 310                         goto out_dev;
 311                 }
 312         }
 313 
 314         rc = -ENOBUFS;
 315 
 316         msg = ieee802154_nl_new_reply(info, 0, IEEE802154_DEL_IFACE);
 317         if (!msg)
 318                 goto out_dev;
 319 
 320         rtnl_lock();
 321         rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev);
 322 
 323         /* We don't have device anymore */
 324         dev_put(dev);
 325         dev = NULL;
 326 
 327         rtnl_unlock();
 328 
 329         if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
 330             nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, name))
 331                 goto nla_put_failure;
 332         wpan_phy_put(phy);
 333 
 334         return ieee802154_nl_reply(msg, info);
 335 
 336 nla_put_failure:
 337         nlmsg_free(msg);
 338 out_dev:
 339         wpan_phy_put(phy);
 340 out:
 341         if (dev)
 342                 dev_put(dev);
 343 
 344         return rc;
 345 }

/* [<][>][^][v][top][bottom][index][help] */