~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/ieee802154/nl-phy.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 }
347 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php