xref: /openbmc/linux/net/ieee802154/nl-mac.c (revision c4ee0af3)
1 /*
2  * Netlink inteface for IEEE 802.15.4 stack
3  *
4  * Copyright 2007, 2008 Siemens AG
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Written by:
20  * Sergey Lapin <slapin@ossfans.org>
21  * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
22  * Maxim Osipov <maxim.osipov@siemens.com>
23  */
24 
25 #include <linux/gfp.h>
26 #include <linux/kernel.h>
27 #include <linux/if_arp.h>
28 #include <linux/netdevice.h>
29 #include <net/netlink.h>
30 #include <net/genetlink.h>
31 #include <net/sock.h>
32 #include <linux/nl802154.h>
33 #include <linux/export.h>
34 #include <net/af_ieee802154.h>
35 #include <net/nl802154.h>
36 #include <net/ieee802154.h>
37 #include <net/ieee802154_netdev.h>
38 #include <net/wpan-phy.h>
39 
40 #include "ieee802154.h"
41 
42 int ieee802154_nl_assoc_indic(struct net_device *dev,
43 		struct ieee802154_addr *addr, u8 cap)
44 {
45 	struct sk_buff *msg;
46 
47 	pr_debug("%s\n", __func__);
48 
49 	if (addr->addr_type != IEEE802154_ADDR_LONG) {
50 		pr_err("%s: received non-long source address!\n", __func__);
51 		return -EINVAL;
52 	}
53 
54 	msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_INDIC);
55 	if (!msg)
56 		return -ENOBUFS;
57 
58 	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
59 	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
60 	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
61 		    dev->dev_addr) ||
62 	    nla_put(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN,
63 		    addr->hwaddr) ||
64 	    nla_put_u8(msg, IEEE802154_ATTR_CAPABILITY, cap))
65 		goto nla_put_failure;
66 
67 	return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
68 
69 nla_put_failure:
70 	nlmsg_free(msg);
71 	return -ENOBUFS;
72 }
73 EXPORT_SYMBOL(ieee802154_nl_assoc_indic);
74 
75 int ieee802154_nl_assoc_confirm(struct net_device *dev, u16 short_addr,
76 		u8 status)
77 {
78 	struct sk_buff *msg;
79 
80 	pr_debug("%s\n", __func__);
81 
82 	msg = ieee802154_nl_create(0, IEEE802154_ASSOCIATE_CONF);
83 	if (!msg)
84 		return -ENOBUFS;
85 
86 	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
87 	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
88 	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
89 		    dev->dev_addr) ||
90 	    nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR, short_addr) ||
91 	    nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
92 		goto nla_put_failure;
93 	return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
94 
95 nla_put_failure:
96 	nlmsg_free(msg);
97 	return -ENOBUFS;
98 }
99 EXPORT_SYMBOL(ieee802154_nl_assoc_confirm);
100 
101 int ieee802154_nl_disassoc_indic(struct net_device *dev,
102 		struct ieee802154_addr *addr, u8 reason)
103 {
104 	struct sk_buff *msg;
105 
106 	pr_debug("%s\n", __func__);
107 
108 	msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_INDIC);
109 	if (!msg)
110 		return -ENOBUFS;
111 
112 	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
113 	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
114 	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
115 		    dev->dev_addr))
116 		goto nla_put_failure;
117 	if (addr->addr_type == IEEE802154_ADDR_LONG) {
118 		if (nla_put(msg, IEEE802154_ATTR_SRC_HW_ADDR, IEEE802154_ADDR_LEN,
119 			    addr->hwaddr))
120 			goto nla_put_failure;
121 	} else {
122 		if (nla_put_u16(msg, IEEE802154_ATTR_SRC_SHORT_ADDR,
123 				addr->short_addr))
124 			goto nla_put_failure;
125 	}
126 	if (nla_put_u8(msg, IEEE802154_ATTR_REASON, reason))
127 		goto nla_put_failure;
128 	return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
129 
130 nla_put_failure:
131 	nlmsg_free(msg);
132 	return -ENOBUFS;
133 }
134 EXPORT_SYMBOL(ieee802154_nl_disassoc_indic);
135 
136 int ieee802154_nl_disassoc_confirm(struct net_device *dev, u8 status)
137 {
138 	struct sk_buff *msg;
139 
140 	pr_debug("%s\n", __func__);
141 
142 	msg = ieee802154_nl_create(0, IEEE802154_DISASSOCIATE_CONF);
143 	if (!msg)
144 		return -ENOBUFS;
145 
146 	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
147 	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
148 	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
149 		    dev->dev_addr) ||
150 	    nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
151 		goto nla_put_failure;
152 	return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
153 
154 nla_put_failure:
155 	nlmsg_free(msg);
156 	return -ENOBUFS;
157 }
158 EXPORT_SYMBOL(ieee802154_nl_disassoc_confirm);
159 
160 int ieee802154_nl_beacon_indic(struct net_device *dev,
161 		u16 panid, u16 coord_addr)
162 {
163 	struct sk_buff *msg;
164 
165 	pr_debug("%s\n", __func__);
166 
167 	msg = ieee802154_nl_create(0, IEEE802154_BEACON_NOTIFY_INDIC);
168 	if (!msg)
169 		return -ENOBUFS;
170 
171 	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
172 	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
173 	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
174 		    dev->dev_addr) ||
175 	    nla_put_u16(msg, IEEE802154_ATTR_COORD_SHORT_ADDR, coord_addr) ||
176 	    nla_put_u16(msg, IEEE802154_ATTR_COORD_PAN_ID, panid))
177 		goto nla_put_failure;
178 	return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
179 
180 nla_put_failure:
181 	nlmsg_free(msg);
182 	return -ENOBUFS;
183 }
184 EXPORT_SYMBOL(ieee802154_nl_beacon_indic);
185 
186 int ieee802154_nl_scan_confirm(struct net_device *dev,
187 		u8 status, u8 scan_type, u32 unscanned, u8 page,
188 		u8 *edl/* , struct list_head *pan_desc_list */)
189 {
190 	struct sk_buff *msg;
191 
192 	pr_debug("%s\n", __func__);
193 
194 	msg = ieee802154_nl_create(0, IEEE802154_SCAN_CONF);
195 	if (!msg)
196 		return -ENOBUFS;
197 
198 	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
199 	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
200 	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
201 		    dev->dev_addr) ||
202 	    nla_put_u8(msg, IEEE802154_ATTR_STATUS, status) ||
203 	    nla_put_u8(msg, IEEE802154_ATTR_SCAN_TYPE, scan_type) ||
204 	    nla_put_u32(msg, IEEE802154_ATTR_CHANNELS, unscanned) ||
205 	    nla_put_u8(msg, IEEE802154_ATTR_PAGE, page) ||
206 	    (edl &&
207 	     nla_put(msg, IEEE802154_ATTR_ED_LIST, 27, edl)))
208 		goto nla_put_failure;
209 	return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
210 
211 nla_put_failure:
212 	nlmsg_free(msg);
213 	return -ENOBUFS;
214 }
215 EXPORT_SYMBOL(ieee802154_nl_scan_confirm);
216 
217 int ieee802154_nl_start_confirm(struct net_device *dev, u8 status)
218 {
219 	struct sk_buff *msg;
220 
221 	pr_debug("%s\n", __func__);
222 
223 	msg = ieee802154_nl_create(0, IEEE802154_START_CONF);
224 	if (!msg)
225 		return -ENOBUFS;
226 
227 	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
228 	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
229 	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
230 		    dev->dev_addr) ||
231 	    nla_put_u8(msg, IEEE802154_ATTR_STATUS, status))
232 		goto nla_put_failure;
233 	return ieee802154_nl_mcast(msg, IEEE802154_COORD_MCGRP);
234 
235 nla_put_failure:
236 	nlmsg_free(msg);
237 	return -ENOBUFS;
238 }
239 EXPORT_SYMBOL(ieee802154_nl_start_confirm);
240 
241 static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,
242 	u32 seq, int flags, struct net_device *dev)
243 {
244 	void *hdr;
245 	struct wpan_phy *phy;
246 
247 	pr_debug("%s\n", __func__);
248 
249 	hdr = genlmsg_put(msg, 0, seq, &nl802154_family, flags,
250 		IEEE802154_LIST_IFACE);
251 	if (!hdr)
252 		goto out;
253 
254 	phy = ieee802154_mlme_ops(dev)->get_phy(dev);
255 	BUG_ON(!phy);
256 
257 	if (nla_put_string(msg, IEEE802154_ATTR_DEV_NAME, dev->name) ||
258 	    nla_put_string(msg, IEEE802154_ATTR_PHY_NAME, wpan_phy_name(phy)) ||
259 	    nla_put_u32(msg, IEEE802154_ATTR_DEV_INDEX, dev->ifindex) ||
260 	    nla_put(msg, IEEE802154_ATTR_HW_ADDR, IEEE802154_ADDR_LEN,
261 		    dev->dev_addr) ||
262 	    nla_put_u16(msg, IEEE802154_ATTR_SHORT_ADDR,
263 			ieee802154_mlme_ops(dev)->get_short_addr(dev)) ||
264 	    nla_put_u16(msg, IEEE802154_ATTR_PAN_ID,
265 			ieee802154_mlme_ops(dev)->get_pan_id(dev)))
266 		goto nla_put_failure;
267 	wpan_phy_put(phy);
268 	return genlmsg_end(msg, hdr);
269 
270 nla_put_failure:
271 	wpan_phy_put(phy);
272 	genlmsg_cancel(msg, hdr);
273 out:
274 	return -EMSGSIZE;
275 }
276 
277 /* Requests from userspace */
278 static struct net_device *ieee802154_nl_get_dev(struct genl_info *info)
279 {
280 	struct net_device *dev;
281 
282 	if (info->attrs[IEEE802154_ATTR_DEV_NAME]) {
283 		char name[IFNAMSIZ + 1];
284 		nla_strlcpy(name, info->attrs[IEEE802154_ATTR_DEV_NAME],
285 				sizeof(name));
286 		dev = dev_get_by_name(&init_net, name);
287 	} else if (info->attrs[IEEE802154_ATTR_DEV_INDEX])
288 		dev = dev_get_by_index(&init_net,
289 			nla_get_u32(info->attrs[IEEE802154_ATTR_DEV_INDEX]));
290 	else
291 		return NULL;
292 
293 	if (!dev)
294 		return NULL;
295 
296 	if (dev->type != ARPHRD_IEEE802154) {
297 		dev_put(dev);
298 		return NULL;
299 	}
300 
301 	return dev;
302 }
303 
304 int ieee802154_associate_req(struct sk_buff *skb, struct genl_info *info)
305 {
306 	struct net_device *dev;
307 	struct ieee802154_addr addr;
308 	u8 page;
309 	int ret = -EOPNOTSUPP;
310 
311 	if (!info->attrs[IEEE802154_ATTR_CHANNEL] ||
312 	    !info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
313 	    (!info->attrs[IEEE802154_ATTR_COORD_HW_ADDR] &&
314 		!info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]) ||
315 	    !info->attrs[IEEE802154_ATTR_CAPABILITY])
316 		return -EINVAL;
317 
318 	dev = ieee802154_nl_get_dev(info);
319 	if (!dev)
320 		return -ENODEV;
321 	if (!ieee802154_mlme_ops(dev)->assoc_req)
322 		goto out;
323 
324 	if (info->attrs[IEEE802154_ATTR_COORD_HW_ADDR]) {
325 		addr.addr_type = IEEE802154_ADDR_LONG;
326 		nla_memcpy(addr.hwaddr,
327 				info->attrs[IEEE802154_ATTR_COORD_HW_ADDR],
328 				IEEE802154_ADDR_LEN);
329 	} else {
330 		addr.addr_type = IEEE802154_ADDR_SHORT;
331 		addr.short_addr = nla_get_u16(
332 				info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
333 	}
334 	addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
335 
336 	if (info->attrs[IEEE802154_ATTR_PAGE])
337 		page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
338 	else
339 		page = 0;
340 
341 	ret = ieee802154_mlme_ops(dev)->assoc_req(dev, &addr,
342 			nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]),
343 			page,
344 			nla_get_u8(info->attrs[IEEE802154_ATTR_CAPABILITY]));
345 
346 out:
347 	dev_put(dev);
348 	return ret;
349 }
350 
351 int ieee802154_associate_resp(struct sk_buff *skb, struct genl_info *info)
352 {
353 	struct net_device *dev;
354 	struct ieee802154_addr addr;
355 	int ret = -EOPNOTSUPP;
356 
357 	if (!info->attrs[IEEE802154_ATTR_STATUS] ||
358 	    !info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] ||
359 	    !info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR])
360 		return -EINVAL;
361 
362 	dev = ieee802154_nl_get_dev(info);
363 	if (!dev)
364 		return -ENODEV;
365 	if (!ieee802154_mlme_ops(dev)->assoc_resp)
366 		goto out;
367 
368 	addr.addr_type = IEEE802154_ADDR_LONG;
369 	nla_memcpy(addr.hwaddr, info->attrs[IEEE802154_ATTR_DEST_HW_ADDR],
370 			IEEE802154_ADDR_LEN);
371 	addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
372 
373 
374 	ret = ieee802154_mlme_ops(dev)->assoc_resp(dev, &addr,
375 		nla_get_u16(info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]),
376 		nla_get_u8(info->attrs[IEEE802154_ATTR_STATUS]));
377 
378 out:
379 	dev_put(dev);
380 	return ret;
381 }
382 
383 int ieee802154_disassociate_req(struct sk_buff *skb, struct genl_info *info)
384 {
385 	struct net_device *dev;
386 	struct ieee802154_addr addr;
387 	int ret = -EOPNOTSUPP;
388 
389 	if ((!info->attrs[IEEE802154_ATTR_DEST_HW_ADDR] &&
390 		!info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]) ||
391 	    !info->attrs[IEEE802154_ATTR_REASON])
392 		return -EINVAL;
393 
394 	dev = ieee802154_nl_get_dev(info);
395 	if (!dev)
396 		return -ENODEV;
397 	if (!ieee802154_mlme_ops(dev)->disassoc_req)
398 		goto out;
399 
400 	if (info->attrs[IEEE802154_ATTR_DEST_HW_ADDR]) {
401 		addr.addr_type = IEEE802154_ADDR_LONG;
402 		nla_memcpy(addr.hwaddr,
403 				info->attrs[IEEE802154_ATTR_DEST_HW_ADDR],
404 				IEEE802154_ADDR_LEN);
405 	} else {
406 		addr.addr_type = IEEE802154_ADDR_SHORT;
407 		addr.short_addr = nla_get_u16(
408 				info->attrs[IEEE802154_ATTR_DEST_SHORT_ADDR]);
409 	}
410 	addr.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
411 
412 	ret = ieee802154_mlme_ops(dev)->disassoc_req(dev, &addr,
413 			nla_get_u8(info->attrs[IEEE802154_ATTR_REASON]));
414 
415 out:
416 	dev_put(dev);
417 	return ret;
418 }
419 
420 /*
421  * PANid, channel, beacon_order = 15, superframe_order = 15,
422  * PAN_coordinator, battery_life_extension = 0,
423  * coord_realignment = 0, security_enable = 0
424 */
425 int ieee802154_start_req(struct sk_buff *skb, struct genl_info *info)
426 {
427 	struct net_device *dev;
428 	struct ieee802154_addr addr;
429 
430 	u8 channel, bcn_ord, sf_ord;
431 	u8 page;
432 	int pan_coord, blx, coord_realign;
433 	int ret = -EOPNOTSUPP;
434 
435 	if (!info->attrs[IEEE802154_ATTR_COORD_PAN_ID] ||
436 	    !info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR] ||
437 	    !info->attrs[IEEE802154_ATTR_CHANNEL] ||
438 	    !info->attrs[IEEE802154_ATTR_BCN_ORD] ||
439 	    !info->attrs[IEEE802154_ATTR_SF_ORD] ||
440 	    !info->attrs[IEEE802154_ATTR_PAN_COORD] ||
441 	    !info->attrs[IEEE802154_ATTR_BAT_EXT] ||
442 	    !info->attrs[IEEE802154_ATTR_COORD_REALIGN]
443 	 )
444 		return -EINVAL;
445 
446 	dev = ieee802154_nl_get_dev(info);
447 	if (!dev)
448 		return -ENODEV;
449 	if (!ieee802154_mlme_ops(dev)->start_req)
450 		goto out;
451 
452 	addr.addr_type = IEEE802154_ADDR_SHORT;
453 	addr.short_addr = nla_get_u16(
454 			info->attrs[IEEE802154_ATTR_COORD_SHORT_ADDR]);
455 	addr.pan_id = nla_get_u16(info->attrs[IEEE802154_ATTR_COORD_PAN_ID]);
456 
457 	channel = nla_get_u8(info->attrs[IEEE802154_ATTR_CHANNEL]);
458 	bcn_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_BCN_ORD]);
459 	sf_ord = nla_get_u8(info->attrs[IEEE802154_ATTR_SF_ORD]);
460 	pan_coord = nla_get_u8(info->attrs[IEEE802154_ATTR_PAN_COORD]);
461 	blx = nla_get_u8(info->attrs[IEEE802154_ATTR_BAT_EXT]);
462 	coord_realign = nla_get_u8(info->attrs[IEEE802154_ATTR_COORD_REALIGN]);
463 
464 	if (info->attrs[IEEE802154_ATTR_PAGE])
465 		page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
466 	else
467 		page = 0;
468 
469 
470 	if (addr.short_addr == IEEE802154_ADDR_BROADCAST) {
471 		ieee802154_nl_start_confirm(dev, IEEE802154_NO_SHORT_ADDRESS);
472 		dev_put(dev);
473 		return -EINVAL;
474 	}
475 
476 	ret = ieee802154_mlme_ops(dev)->start_req(dev, &addr, channel, page,
477 		bcn_ord, sf_ord, pan_coord, blx, coord_realign);
478 
479 out:
480 	dev_put(dev);
481 	return ret;
482 }
483 
484 int ieee802154_scan_req(struct sk_buff *skb, struct genl_info *info)
485 {
486 	struct net_device *dev;
487 	int ret = -EOPNOTSUPP;
488 	u8 type;
489 	u32 channels;
490 	u8 duration;
491 	u8 page;
492 
493 	if (!info->attrs[IEEE802154_ATTR_SCAN_TYPE] ||
494 	    !info->attrs[IEEE802154_ATTR_CHANNELS] ||
495 	    !info->attrs[IEEE802154_ATTR_DURATION])
496 		return -EINVAL;
497 
498 	dev = ieee802154_nl_get_dev(info);
499 	if (!dev)
500 		return -ENODEV;
501 	if (!ieee802154_mlme_ops(dev)->scan_req)
502 		goto out;
503 
504 	type = nla_get_u8(info->attrs[IEEE802154_ATTR_SCAN_TYPE]);
505 	channels = nla_get_u32(info->attrs[IEEE802154_ATTR_CHANNELS]);
506 	duration = nla_get_u8(info->attrs[IEEE802154_ATTR_DURATION]);
507 
508 	if (info->attrs[IEEE802154_ATTR_PAGE])
509 		page = nla_get_u8(info->attrs[IEEE802154_ATTR_PAGE]);
510 	else
511 		page = 0;
512 
513 
514 	ret = ieee802154_mlme_ops(dev)->scan_req(dev, type, channels, page,
515 			duration);
516 
517 out:
518 	dev_put(dev);
519 	return ret;
520 }
521 
522 int ieee802154_list_iface(struct sk_buff *skb, struct genl_info *info)
523 {
524 	/* Request for interface name, index, type, IEEE address,
525 	   PAN Id, short address */
526 	struct sk_buff *msg;
527 	struct net_device *dev = NULL;
528 	int rc = -ENOBUFS;
529 
530 	pr_debug("%s\n", __func__);
531 
532 	dev = ieee802154_nl_get_dev(info);
533 	if (!dev)
534 		return -ENODEV;
535 
536 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
537 	if (!msg)
538 		goto out_dev;
539 
540 	rc = ieee802154_nl_fill_iface(msg, info->snd_portid, info->snd_seq,
541 			0, dev);
542 	if (rc < 0)
543 		goto out_free;
544 
545 	dev_put(dev);
546 
547 	return genlmsg_reply(msg, info);
548 out_free:
549 	nlmsg_free(msg);
550 out_dev:
551 	dev_put(dev);
552 	return rc;
553 
554 }
555 
556 int ieee802154_dump_iface(struct sk_buff *skb, struct netlink_callback *cb)
557 {
558 	struct net *net = sock_net(skb->sk);
559 	struct net_device *dev;
560 	int idx;
561 	int s_idx = cb->args[0];
562 
563 	pr_debug("%s\n", __func__);
564 
565 	idx = 0;
566 	for_each_netdev(net, dev) {
567 		if (idx < s_idx || (dev->type != ARPHRD_IEEE802154))
568 			goto cont;
569 
570 		if (ieee802154_nl_fill_iface(skb, NETLINK_CB(cb->skb).portid,
571 			cb->nlh->nlmsg_seq, NLM_F_MULTI, dev) < 0)
572 			break;
573 cont:
574 		idx++;
575 	}
576 	cb->args[0] = idx;
577 
578 	return skb->len;
579 }
580