0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013 #include <linux/kernel.h>
0014 #include <linux/slab.h>
0015 #include <linux/if_arp.h>
0016 #include <net/netlink.h>
0017 #include <net/genetlink.h>
0018 #include <net/cfg802154.h>
0019 #include <net/af_ieee802154.h>
0020 #include <net/ieee802154_netdev.h>
0021 #include <net/rtnetlink.h> /* for rtnl_{un,}lock */
0022 #include <linux/nl802154.h>
0023
0024 #include "ieee802154.h"
0025 #include "rdev-ops.h"
0026 #include "core.h"
0027
0028 static int ieee802154_nl_fill_phy(struct sk_buff *msg, u32 portid,
0029 u32 seq, int flags, struct wpan_phy *phy)
0030 {
0031 void *hdr;
0032 int i, pages = 0;
0033 u32 *buf = kcalloc(IEEE802154_MAX_PAGE + 1, sizeof(u32), GFP_KERNEL);
0034
0035 pr_debug("%s\n", __func__);
0036
0037 if (!buf)
0038 return -EMSGSIZE;
0039
0040 hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags,
0041 IEEE802154_LIST_PHY);
0042 if (!hdr)
0043 goto out;
0044
0045 rtnl_lock();
0046 if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
0047 nla_put_u8(msg, IEEE802154_ATTR_PAGE, phy->current_page) ||
0048 nla_put_u8(msg, IEEE802154_ATTR_CHANNEL, phy->current_channel))
0049 goto nla_put_failure;
0050 for (i = 0; i <= IEEE802154_MAX_PAGE; i++) {
0051 if (phy->supported.channels[i])
0052 buf[pages++] = phy->supported.channels[i] | (i << 27);
0053 }
0054 if (pages &&
0055 nla_put(msg, IEEE802154_ATTR_CHANNEL_PAGE_LIST,
0056 pages * sizeof(uint32_t), buf))
0057 goto nla_put_failure;
0058 rtnl_unlock();
0059 kfree(buf);
0060 genlmsg_end(msg, hdr);
0061 return 0;
0062
0063 nla_put_failure:
0064 rtnl_unlock();
0065 genlmsg_cancel(msg, hdr);
0066 out:
0067 kfree(buf);
0068 return -EMSGSIZE;
0069 }
0070
0071 int ieee802154_list_phy(struct sk_buff *skb, struct genl_info *info)
0072 {
0073
0074
0075
0076 struct sk_buff *msg;
0077 struct wpan_phy *phy;
0078 const char *name;
0079 int rc = -ENOBUFS;
0080
0081 pr_debug("%s\n", __func__);
0082
0083 if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
0084 return -EINVAL;
0085
0086 name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
0087 if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
0088 return -EINVAL;
0089
0090 phy = wpan_phy_find(name);
0091 if (!phy)
0092 return -ENODEV;
0093
0094 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
0095 if (!msg)
0096 goto out_dev;
0097
0098 rc = ieee802154_nl_fill_phy(msg, info->snd_portid, info->snd_seq,
0099 0, phy);
0100 if (rc < 0)
0101 goto out_free;
0102
0103 wpan_phy_put(phy);
0104
0105 return genlmsg_reply(msg, info);
0106 out_free:
0107 nlmsg_free(msg);
0108 out_dev:
0109 wpan_phy_put(phy);
0110 return rc;
0111 }
0112
0113 struct dump_phy_data {
0114 struct sk_buff *skb;
0115 struct netlink_callback *cb;
0116 int idx, s_idx;
0117 };
0118
0119 static int ieee802154_dump_phy_iter(struct wpan_phy *phy, void *_data)
0120 {
0121 int rc;
0122 struct dump_phy_data *data = _data;
0123
0124 pr_debug("%s\n", __func__);
0125
0126 if (data->idx++ < data->s_idx)
0127 return 0;
0128
0129 rc = ieee802154_nl_fill_phy(data->skb,
0130 NETLINK_CB(data->cb->skb).portid,
0131 data->cb->nlh->nlmsg_seq,
0132 NLM_F_MULTI,
0133 phy);
0134
0135 if (rc < 0) {
0136 data->idx--;
0137 return rc;
0138 }
0139
0140 return 0;
0141 }
0142
0143 int ieee802154_dump_phy(struct sk_buff *skb, struct netlink_callback *cb)
0144 {
0145 struct dump_phy_data data = {
0146 .cb = cb,
0147 .skb = skb,
0148 .s_idx = cb->args[0],
0149 .idx = 0,
0150 };
0151
0152 pr_debug("%s\n", __func__);
0153
0154 wpan_phy_for_each(ieee802154_dump_phy_iter, &data);
0155
0156 cb->args[0] = data.idx;
0157
0158 return skb->len;
0159 }
0160
0161 int ieee802154_add_iface(struct sk_buff *skb, struct genl_info *info)
0162 {
0163 struct sk_buff *msg;
0164 struct wpan_phy *phy;
0165 const char *name;
0166 const char *devname;
0167 int rc = -ENOBUFS;
0168 struct net_device *dev;
0169 int type = __IEEE802154_DEV_INVALID;
0170 unsigned char name_assign_type;
0171
0172 pr_debug("%s\n", __func__);
0173
0174 if (!info->attrs[IEEE802154_ATTR_PHY_NAME])
0175 return -EINVAL;
0176
0177 name = nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
0178 if (name[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0')
0179 return -EINVAL;
0180
0181 if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
0182 devname = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
0183 if (devname[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1]
0184 != '\0')
0185 return -EINVAL;
0186 name_assign_type = NET_NAME_USER;
0187 } else {
0188 devname = "wpan%d";
0189 name_assign_type = NET_NAME_ENUM;
0190 }
0191
0192 if (strlen(devname) >= IFNAMSIZ)
0193 return -ENAMETOOLONG;
0194
0195 phy = wpan_phy_find(name);
0196 if (!phy)
0197 return -ENODEV;
0198
0199 msg = ieee802154_nl_new_reply(info, 0, IEEE802154_ADD_IFACE);
0200 if (!msg)
0201 goto out_dev;
0202
0203 if (info->attrs[IEEE802154_ATTR_HW_ADDR] &&
0204 nla_len(info->attrs[IEEE802154_ATTR_HW_ADDR]) !=
0205 IEEE802154_ADDR_LEN) {
0206 rc = -EINVAL;
0207 goto nla_put_failure;
0208 }
0209
0210 if (info->attrs[IEEE802154_ATTR_DEV_TYPE]) {
0211 type = nla_get_u8(info->attrs[IEEE802154_ATTR_DEV_TYPE]);
0212 if (type >= __IEEE802154_DEV_MAX) {
0213 rc = -EINVAL;
0214 goto nla_put_failure;
0215 }
0216 }
0217
0218 dev = rdev_add_virtual_intf_deprecated(wpan_phy_to_rdev(phy), devname,
0219 name_assign_type, type);
0220 if (IS_ERR(dev)) {
0221 rc = PTR_ERR(dev);
0222 goto nla_put_failure;
0223 }
0224 dev_hold(dev);
0225
0226 if (info->attrs[IEEE802154_ATTR_HW_ADDR]) {
0227 struct sockaddr addr;
0228
0229 addr.sa_family = ARPHRD_IEEE802154;
0230 nla_memcpy(&addr.sa_data, info->attrs[IEEE802154_ATTR_HW_ADDR],
0231 IEEE802154_ADDR_LEN);
0232
0233
0234
0235
0236 rtnl_lock();
0237 rc = dev_set_mac_address(dev, &addr, NULL);
0238 rtnl_unlock();
0239 if (rc)
0240 goto dev_unregister;
0241 }
0242
0243 if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
0244 nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name)) {
0245 rc = -EMSGSIZE;
0246 goto nla_put_failure;
0247 }
0248 dev_put(dev);
0249
0250 wpan_phy_put(phy);
0251
0252 return ieee802154_nl_reply(msg, info);
0253
0254 dev_unregister:
0255 rtnl_lock();
0256 rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev);
0257 dev_put(dev);
0258 rtnl_unlock();
0259 nla_put_failure:
0260 nlmsg_free(msg);
0261 out_dev:
0262 wpan_phy_put(phy);
0263 return rc;
0264 }
0265
0266 int ieee802154_del_iface(struct sk_buff *skb, struct genl_info *info)
0267 {
0268 struct sk_buff *msg;
0269 struct wpan_phy *phy;
0270 const char *name;
0271 int rc;
0272 struct net_device *dev;
0273
0274 pr_debug("%s\n", __func__);
0275
0276 if (!info->attrs[IEEE802154_ATTR_DEV_NAME])
0277 return -EINVAL;
0278
0279 name = nla_data(info->attrs[IEEE802154_ATTR_DEV_NAME]);
0280 if (name[nla_len(info->attrs[IEEE802154_ATTR_DEV_NAME]) - 1] != '\0')
0281 return -EINVAL;
0282
0283 rc = -ENODEV;
0284 dev = dev_get_by_name(genl_info_net(info), name);
0285 if (!dev)
0286 return rc;
0287 if (dev->type != ARPHRD_IEEE802154)
0288 goto out;
0289
0290 phy = dev->ieee802154_ptr->wpan_phy;
0291 BUG_ON(!phy);
0292 get_device(&phy->dev);
0293
0294 rc = -EINVAL;
0295
0296 if (info->attrs[IEEE802154_ATTR_PHY_NAME]) {
0297 struct wpan_phy *phy2;
0298
0299 const char *pname =
0300 nla_data(info->attrs[IEEE802154_ATTR_PHY_NAME]);
0301 if (pname[nla_len(info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1]
0302 != '\0')
0303
0304 goto out_dev;
0305
0306 phy2 = wpan_phy_find(pname);
0307 if (!phy2)
0308 goto out_dev;
0309
0310 if (phy != phy2) {
0311 wpan_phy_put(phy2);
0312 goto out_dev;
0313 }
0314 }
0315
0316 rc = -ENOBUFS;
0317
0318 msg = ieee802154_nl_new_reply(info, 0, IEEE802154_DEL_IFACE);
0319 if (!msg)
0320 goto out_dev;
0321
0322 rtnl_lock();
0323 rdev_del_virtual_intf_deprecated(wpan_phy_to_rdev(phy), dev);
0324
0325
0326 dev_put(dev);
0327 dev = NULL;
0328
0329 rtnl_unlock();
0330
0331 if (nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
0332 nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, name))
0333 goto nla_put_failure;
0334 wpan_phy_put(phy);
0335
0336 return ieee802154_nl_reply(msg, info);
0337
0338 nla_put_failure:
0339 nlmsg_free(msg);
0340 out_dev:
0341 wpan_phy_put(phy);
0342 out:
0343 dev_put(dev);
0344
0345 return rc;
0346 }