Merge tag 'gvt-next-fixes-2019-09-06' of https://github.com/intel/gvt-linux into...
[linux-2.6-microblaze.git] / net / ieee802154 / nl-phy.c
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 }