nl-phy.c (7792B)
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 u32 *buf = kcalloc(IEEE802154_MAX_PAGE + 1, sizeof(u32), 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 <= IEEE802154_MAX_PAGE; 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}