Linux 6.9-rc5
[sfrench/cifs-2.6.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         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
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                 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
254 dev_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();
259 nla_put_failure:
260         nlmsg_free(msg);
261 out_dev:
262         wpan_phy_put(phy);
263         return rc;
264 }
265
266 int 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
338 nla_put_failure:
339         nlmsg_free(msg);
340 out_dev:
341         wpan_phy_put(phy);
342 out:
343         dev_put(dev);
344
345         return rc;
346 }