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 36 static 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 = kcalloc(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 71 nla_put_failure: 72 rtnl_unlock(); 73 genlmsg_cancel(msg, hdr); 74 out: 75 kfree(buf); 76 return -EMSGSIZE; 77 } 78 79 int 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); 114 out_free: 115 nlmsg_free(msg); 116 out_dev: 117 wpan_phy_put(phy); 118 return rc; 119 } 120 121 struct dump_phy_data { 122 struct sk_buff *skb; 123 struct netlink_callback *cb; 124 int idx, s_idx; 125 }; 126 127 static 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 151 int 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 169 int 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 260 dev_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(); 265 nla_put_failure: 266 nlmsg_free(msg); 267 out_dev: 268 wpan_phy_put(phy); 269 return rc; 270 } 271 272 int 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 344 nla_put_failure: 345 nlmsg_free(msg); 346 out_dev: 347 wpan_phy_put(phy); 348 out: 349 if (dev) 350 dev_put(dev); 351 352 return rc; 353 } 354