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, size: sizeof(u32), GFP_KERNEL); |
34 | |
35 | pr_debug("%s\n" , __func__); |
36 | |
37 | if (!buf) |
38 | return -EMSGSIZE; |
39 | |
40 | hdr = genlmsg_put(skb: msg, portid: 0, seq, family: &nl802154_family, flags, |
41 | cmd: IEEE802154_LIST_PHY); |
42 | if (!hdr) |
43 | goto out; |
44 | |
45 | rtnl_lock(); |
46 | if (nla_put_string(skb: msg, attrtype: IEEE802154_ATTR_PHY_NAME, str: wpan_phy_name(phy)) || |
47 | nla_put_u8(skb: msg, attrtype: IEEE802154_ATTR_PAGE, value: phy->current_page) || |
48 | nla_put_u8(skb: msg, attrtype: IEEE802154_ATTR_CHANNEL, value: 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(skb: msg, attrtype: IEEE802154_ATTR_CHANNEL_PAGE_LIST, |
56 | attrlen: pages * sizeof(uint32_t), data: buf)) |
57 | goto nla_put_failure; |
58 | rtnl_unlock(); |
59 | kfree(objp: buf); |
60 | genlmsg_end(skb: msg, hdr); |
61 | return 0; |
62 | |
63 | nla_put_failure: |
64 | rtnl_unlock(); |
65 | genlmsg_cancel(skb: msg, hdr); |
66 | out: |
67 | kfree(objp: 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(nla: info->attrs[IEEE802154_ATTR_PHY_NAME]); |
87 | if (name[nla_len(nla: info->attrs[IEEE802154_ATTR_PHY_NAME]) - 1] != '\0') |
88 | return -EINVAL; /* phy name should be null-terminated */ |
89 | |
90 | phy = wpan_phy_find(str: 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, portid: info->snd_portid, seq: info->snd_seq, |
99 | flags: 0, phy); |
100 | if (rc < 0) |
101 | goto out_free; |
102 | |
103 | wpan_phy_put(phy); |
104 | |
105 | return genlmsg_reply(skb: msg, info); |
106 | out_free: |
107 | nlmsg_free(skb: 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(msg: data->skb, |
130 | NETLINK_CB(data->cb->skb).portid, |
131 | seq: 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(fn: ieee802154_dump_phy_iter, data: &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(nla: info->attrs[IEEE802154_ATTR_PHY_NAME]); |
178 | if (name[nla_len(nla: 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(nla: info->attrs[IEEE802154_ATTR_DEV_NAME]); |
183 | if (devname[nla_len(nla: 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(str: name); |
196 | if (!phy) |
197 | return -ENODEV; |
198 | |
199 | msg = ieee802154_nl_new_reply(info, flags: 0, req: IEEE802154_ADD_IFACE); |
200 | if (!msg) |
201 | goto out_dev; |
202 | |
203 | if (info->attrs[IEEE802154_ATTR_HW_ADDR] && |
204 | nla_len(nla: 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(nla: 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(rdev: wpan_phy_to_rdev(wpan_phy: phy), name: devname, |
219 | name_assign_type, type); |
220 | if (IS_ERR(ptr: dev)) { |
221 | rc = PTR_ERR(ptr: 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(dest: &addr.sa_data, src: 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, sa: &addr, NULL); |
238 | rtnl_unlock(); |
239 | if (rc) |
240 | goto dev_unregister; |
241 | } |
242 | |
243 | if (nla_put_string(skb: msg, attrtype: IEEE802154_ATTR_PHY_NAME, str: wpan_phy_name(phy)) || |
244 | nla_put_string(skb: msg, attrtype: IEEE802154_ATTR_DEV_NAME, str: 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(rdev: wpan_phy_to_rdev(wpan_phy: phy), dev); |
257 | dev_put(dev); |
258 | rtnl_unlock(); |
259 | nla_put_failure: |
260 | nlmsg_free(skb: 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(nla: info->attrs[IEEE802154_ATTR_DEV_NAME]); |
280 | if (name[nla_len(nla: 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(net: 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(dev: &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(nla: info->attrs[IEEE802154_ATTR_PHY_NAME]); |
301 | if (pname[nla_len(nla: 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(str: pname); |
307 | if (!phy2) |
308 | goto out_dev; |
309 | |
310 | if (phy != phy2) { |
311 | wpan_phy_put(phy: phy2); |
312 | goto out_dev; |
313 | } |
314 | } |
315 | |
316 | rc = -ENOBUFS; |
317 | |
318 | msg = ieee802154_nl_new_reply(info, flags: 0, req: IEEE802154_DEL_IFACE); |
319 | if (!msg) |
320 | goto out_dev; |
321 | |
322 | rtnl_lock(); |
323 | rdev_del_virtual_intf_deprecated(rdev: wpan_phy_to_rdev(wpan_phy: 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(skb: msg, attrtype: IEEE802154_ATTR_PHY_NAME, str: wpan_phy_name(phy)) || |
332 | nla_put_string(skb: msg, attrtype: IEEE802154_ATTR_DEV_NAME, str: 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(skb: msg); |
340 | out_dev: |
341 | wpan_phy_put(phy); |
342 | out: |
343 | dev_put(dev); |
344 | |
345 | return rc; |
346 | } |
347 | |