iface.c 16.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * Copyright 2007-2012 Siemens AG
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Written by:
 * Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>
 * Sergey Lapin <slapin@ossfans.org>
 * Maxim Gorbachyov <maxim.gorbachev@siemens.com>
 * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
 */

#include <linux/netdevice.h>
#include <linux/module.h>
#include <linux/if_arp.h>
23
#include <linux/ieee802154.h>
24

25
#include <net/nl802154.h>
26 27
#include <net/mac802154.h>
#include <net/ieee802154_netdev.h>
28
#include <net/cfg802154.h>
29

30
#include "ieee802154_i.h"
31
#include "driver-ops.h"
32

33 34
static int mac802154_wpan_update_llsec(struct net_device *dev)
{
35
	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
36
	struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
37
	struct wpan_dev *wpan_dev = &sdata->wpan_dev;
38 39 40 41 42 43
	int rc = 0;

	if (ops->llsec) {
		struct ieee802154_llsec_params params;
		int changed = 0;

44
		params.pan_id = wpan_dev->pan_id;
45 46
		changed |= IEEE802154_LLSEC_PARAM_PAN_ID;

47
		params.hwaddr = wpan_dev->extended_addr;
48 49 50 51 52 53 54 55
		changed |= IEEE802154_LLSEC_PARAM_HWADDR;

		rc = ops->llsec->set_params(dev, &params, changed);
	}

	return rc;
}

56 57 58
static int
mac802154_wpan_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
59
	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
60
	struct wpan_dev *wpan_dev = &sdata->wpan_dev;
61 62 63 64
	struct sockaddr_ieee802154 *sa =
		(struct sockaddr_ieee802154 *)&ifr->ifr_addr;
	int err = -ENOIOCTLCMD;

65 66
	ASSERT_RTNL();

67
	spin_lock_bh(&sdata->mib_lock);
68 69 70

	switch (cmd) {
	case SIOCGIFADDR:
71 72 73
	{
		u16 pan_id, short_addr;

74 75
		pan_id = le16_to_cpu(wpan_dev->pan_id);
		short_addr = le16_to_cpu(wpan_dev->short_addr);
76 77
		if (pan_id == IEEE802154_PANID_BROADCAST ||
		    short_addr == IEEE802154_ADDR_BROADCAST) {
78 79 80 81 82 83
			err = -EADDRNOTAVAIL;
			break;
		}

		sa->family = AF_IEEE802154;
		sa->addr.addr_type = IEEE802154_ADDR_SHORT;
84 85
		sa->addr.pan_id = pan_id;
		sa->addr.short_addr = short_addr;
86 87 88

		err = 0;
		break;
89
	}
90
	case SIOCSIFADDR:
91 92 93 94 95
		if (netif_running(dev)) {
			spin_unlock_bh(&sdata->mib_lock);
			return -EBUSY;
		}

96
		dev_warn(&dev->dev,
M
Masanari Iida 已提交
97
			 "Using DEBUGing ioctl SIOCSIFADDR isn't recommended!\n");
98 99 100 101 102 103 104 105 106
		if (sa->family != AF_IEEE802154 ||
		    sa->addr.addr_type != IEEE802154_ADDR_SHORT ||
		    sa->addr.pan_id == IEEE802154_PANID_BROADCAST ||
		    sa->addr.short_addr == IEEE802154_ADDR_BROADCAST ||
		    sa->addr.short_addr == IEEE802154_ADDR_UNDEF) {
			err = -EINVAL;
			break;
		}

107 108
		wpan_dev->pan_id = cpu_to_le16(sa->addr.pan_id);
		wpan_dev->short_addr = cpu_to_le16(sa->addr.short_addr);
109

110
		err = mac802154_wpan_update_llsec(dev);
111 112 113
		break;
	}

114
	spin_unlock_bh(&sdata->mib_lock);
115 116 117 118 119
	return err;
}

static int mac802154_wpan_mac_addr(struct net_device *dev, void *p)
{
120
	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
121
	struct sockaddr *addr = p;
122
	__le64 extended_addr;
123 124 125 126

	if (netif_running(dev))
		return -EBUSY;

127
	ieee802154_be64_to_le64(&extended_addr, addr->sa_data);
128 129 130
	if (!ieee802154_is_valid_extended_addr(extended_addr))
		return -EINVAL;

131
	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
132
	sdata->wpan_dev.extended_addr = extended_addr;
133

134
	return mac802154_wpan_update_llsec(dev);
135 136
}

137 138 139 140 141 142 143 144
static int mac802154_slave_open(struct net_device *dev)
{
	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
	struct ieee802154_local *local = sdata->local;
	int res = 0;

	ASSERT_RTNL();

145
	set_bit(SDATA_STATE_RUNNING, &sdata->state);
146

147
	if (!local->open_count) {
148
		res = drv_start(local);
149 150 151 152 153
		WARN_ON(res);
		if (res)
			goto err;
	}

154
	local->open_count++;
155 156 157
	netif_start_queue(dev);
	return 0;
err:
158 159
	/* might already be clear but that doesn't matter */
	clear_bit(SDATA_STATE_RUNNING, &sdata->state);
160 161 162 163

	return res;
}

164 165 166 167 168 169 170 171 172 173 174 175 176
static int
ieee802154_check_mac_settings(struct ieee802154_local *local,
			      struct wpan_dev *wpan_dev,
			      struct wpan_dev *nwpan_dev)
{
	ASSERT_RTNL();

	if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) {
		if (wpan_dev->promiscuous_mode != nwpan_dev->promiscuous_mode)
			return -EBUSY;
	}

	if (local->hw.flags & IEEE802154_HW_AFILT) {
177 178 179
		if (wpan_dev->pan_id != nwpan_dev->pan_id ||
		    wpan_dev->short_addr != nwpan_dev->short_addr ||
		    wpan_dev->extended_addr != nwpan_dev->extended_addr)
180 181 182 183
			return -EBUSY;
	}

	if (local->hw.flags & IEEE802154_HW_CSMA_PARAMS) {
184 185 186
		if (wpan_dev->min_be != nwpan_dev->min_be ||
		    wpan_dev->max_be != nwpan_dev->max_be ||
		    wpan_dev->csma_retries != nwpan_dev->csma_retries)
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
			return -EBUSY;
	}

	if (local->hw.flags & IEEE802154_HW_FRAME_RETRIES) {
		if (wpan_dev->frame_retries != nwpan_dev->frame_retries)
			return -EBUSY;
	}

	if (local->hw.flags & IEEE802154_HW_LBT) {
		if (wpan_dev->lbt != nwpan_dev->lbt)
			return -EBUSY;
	}

	return 0;
}

static int
ieee802154_check_concurrent_iface(struct ieee802154_sub_if_data *sdata,
				  enum nl802154_iftype iftype)
{
	struct ieee802154_local *local = sdata->local;
	struct wpan_dev *wpan_dev = &sdata->wpan_dev;
	struct ieee802154_sub_if_data *nsdata;

	/* we hold the RTNL here so can safely walk the list */
	list_for_each_entry(nsdata, &local->interfaces, list) {
		if (nsdata != sdata && ieee802154_sdata_running(nsdata)) {
			int ret;

216 217 218 219 220 221 222 223 224
			/* TODO currently we don't support multiple node types
			 * we need to run skb_clone at rx path. Check if there
			 * exist really an use case if we need to support
			 * multiple node types at the same time.
			 */
			if (sdata->vif.type == NL802154_IFTYPE_NODE &&
			    nsdata->vif.type == NL802154_IFTYPE_NODE)
				return -EBUSY;

225 226 227 228 229 230 231 232 233 234 235 236 237
			/* check all phy mac sublayer settings are the same.
			 * We have only one phy, different values makes trouble.
			 */
			ret = ieee802154_check_mac_settings(local, wpan_dev,
							    &nsdata->wpan_dev);
			if (ret < 0)
				return ret;
		}
	}

	return 0;
}

238
static int mac802154_wpan_open(struct net_device *dev)
239 240
{
	int rc;
241
	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
242
	struct ieee802154_local *local = sdata->local;
243
	struct wpan_dev *wpan_dev = &sdata->wpan_dev;
244
	struct wpan_phy *phy = sdata->local->phy;
245

246 247 248 249
	rc = ieee802154_check_concurrent_iface(sdata, sdata->vif.type);
	if (rc < 0)
		return rc;

250 251 252 253 254 255
	rc = mac802154_slave_open(dev);
	if (rc < 0)
		return rc;

	mutex_lock(&phy->pib_lock);

256
	if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) {
257 258
		rc = drv_set_promiscuous_mode(local,
					      wpan_dev->promiscuous_mode);
259 260 261 262
		if (rc < 0)
			goto out;
	}

263
	if (local->hw.flags & IEEE802154_HW_AFILT) {
264
		rc = drv_set_pan_id(local, wpan_dev->pan_id);
265 266 267
		if (rc < 0)
			goto out;

268
		rc = drv_set_extended_addr(local, wpan_dev->extended_addr);
269 270
		if (rc < 0)
			goto out;
271

272
		rc = drv_set_short_addr(local, wpan_dev->short_addr);
273 274
		if (rc < 0)
			goto out;
275 276
	}

277
	if (local->hw.flags & IEEE802154_HW_LBT) {
278
		rc = drv_set_lbt_mode(local, wpan_dev->lbt);
279 280 281 282
		if (rc < 0)
			goto out;
	}

283
	if (local->hw.flags & IEEE802154_HW_CSMA_PARAMS) {
284 285 286
		rc = drv_set_csma_params(local, wpan_dev->min_be,
					 wpan_dev->max_be,
					 wpan_dev->csma_retries);
287 288 289 290
		if (rc < 0)
			goto out;
	}

291
	if (local->hw.flags & IEEE802154_HW_FRAME_RETRIES) {
292
		rc = drv_set_max_frame_retries(local, wpan_dev->frame_retries);
293 294 295 296 297 298 299 300 301 302 303 304
		if (rc < 0)
			goto out;
	}

	mutex_unlock(&phy->pib_lock);
	return 0;

out:
	mutex_unlock(&phy->pib_lock);
	return rc;
}

305 306 307 308 309 310 311
static int mac802154_slave_close(struct net_device *dev)
{
	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
	struct ieee802154_local *local = sdata->local;

	ASSERT_RTNL();

312 313
	hrtimer_cancel(&local->ifs_timer);

314
	netif_stop_queue(dev);
315
	local->open_count--;
316

317
	clear_bit(SDATA_STATE_RUNNING, &sdata->state);
318

319
	if (!local->open_count)
320
		drv_stop(local);
321 322 323 324

	return 0;
}

325
static int mac802154_set_header_security(struct ieee802154_sub_if_data *sdata,
326 327 328 329 330 331
					 struct ieee802154_hdr *hdr,
					 const struct ieee802154_mac_cb *cb)
{
	struct ieee802154_llsec_params params;
	u8 level;

332
	mac802154_llsec_get_params(&sdata->sec, &params);
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356

	if (!params.enabled && cb->secen_override && cb->secen)
		return -EINVAL;
	if (!params.enabled ||
	    (cb->secen_override && !cb->secen) ||
	    !params.out_level)
		return 0;
	if (cb->seclevel_override && !cb->seclevel)
		return -EINVAL;

	level = cb->seclevel_override ? cb->seclevel : params.out_level;

	hdr->fc.security_enabled = 1;
	hdr->sec.level = level;
	hdr->sec.key_id_mode = params.out_key.mode;
	if (params.out_key.mode == IEEE802154_SCF_KEY_SHORT_INDEX)
		hdr->sec.short_src = params.out_key.short_source;
	else if (params.out_key.mode == IEEE802154_SCF_KEY_HW_INDEX)
		hdr->sec.extended_src = params.out_key.extended_source;
	hdr->sec.key_id = params.out_key.id;

	return 0;
}

357 358 359
static int mac802154_header_create(struct sk_buff *skb,
				   struct net_device *dev,
				   unsigned short type,
360 361
				   const void *daddr,
				   const void *saddr,
362 363
				   unsigned len)
{
364
	struct ieee802154_hdr hdr;
365
	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
366
	struct wpan_dev *wpan_dev = &sdata->wpan_dev;
367
	struct ieee802154_mac_cb *cb = mac_cb(skb);
368
	int hlen;
369 370 371 372

	if (!daddr)
		return -EINVAL;

373
	memset(&hdr.fc, 0, sizeof(hdr.fc));
374 375 376 377
	hdr.fc.type = cb->type;
	hdr.fc.security_enabled = cb->secen;
	hdr.fc.ack_request = cb->ackreq;
	hdr.seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
378

379
	if (mac802154_set_header_security(sdata, &hdr, cb) < 0)
380 381
		return -EINVAL;

382
	if (!saddr) {
383
		spin_lock_bh(&sdata->mib_lock);
384

385 386 387
		if (wpan_dev->short_addr == cpu_to_le16(IEEE802154_ADDR_BROADCAST) ||
		    wpan_dev->short_addr == cpu_to_le16(IEEE802154_ADDR_UNDEF) ||
		    wpan_dev->pan_id == cpu_to_le16(IEEE802154_PANID_BROADCAST)) {
388
			hdr.source.mode = IEEE802154_ADDR_LONG;
389
			hdr.source.extended_addr = wpan_dev->extended_addr;
390
		} else {
391
			hdr.source.mode = IEEE802154_ADDR_SHORT;
392
			hdr.source.short_addr = wpan_dev->short_addr;
393 394
		}

395
		hdr.source.pan_id = wpan_dev->pan_id;
396

397
		spin_unlock_bh(&sdata->mib_lock);
398 399
	} else {
		hdr.source = *(const struct ieee802154_addr *)saddr;
400 401
	}

402
	hdr.dest = *(const struct ieee802154_addr *)daddr;
403

404 405 406
	hlen = ieee802154_hdr_push(skb, &hdr);
	if (hlen < 0)
		return -EINVAL;
407

408
	skb_reset_mac_header(skb);
409
	skb->mac_len = hlen;
410

411
	if (len > ieee802154_max_payload(&hdr))
412 413
		return -EMSGSIZE;

414
	return hlen;
415 416 417 418 419
}

static int
mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
{
420 421
	struct ieee802154_hdr hdr;
	struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr;
422

423 424 425
	if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) {
		pr_debug("malformed packet\n");
		return 0;
426 427
	}

428 429
	*addr = hdr.source;
	return sizeof(*addr);
430 431 432 433 434 435 436 437
}

static struct header_ops mac802154_header_ops = {
	.create		= mac802154_header_create,
	.parse		= mac802154_header_parse,
};

static const struct net_device_ops mac802154_wpan_ops = {
438
	.ndo_open		= mac802154_wpan_open,
439
	.ndo_stop		= mac802154_slave_close,
440
	.ndo_start_xmit		= ieee802154_subif_start_xmit,
441 442 443 444
	.ndo_do_ioctl		= mac802154_wpan_ioctl,
	.ndo_set_mac_address	= mac802154_wpan_mac_addr,
};

445
static const struct net_device_ops mac802154_monitor_ops = {
446
	.ndo_open		= mac802154_wpan_open,
447 448 449 450
	.ndo_stop		= mac802154_slave_close,
	.ndo_start_xmit		= ieee802154_monitor_start_xmit,
};

451 452
static void mac802154_wpan_free(struct net_device *dev)
{
453
	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
454

455
	mac802154_llsec_destroy(&sdata->sec);
456 457 458 459

	free_netdev(dev);
}

460
static void ieee802154_if_setup(struct net_device *dev)
461
{
462 463
	dev->addr_len		= IEEE802154_EXTENDED_ADDR_LEN;
	memset(dev->broadcast, 0xff, IEEE802154_EXTENDED_ADDR_LEN);
464 465

	dev->hard_header_len	= MAC802154_FRAME_HARD_HEADER_LEN;
466
	dev->needed_tailroom	= 2 + 16; /* FCS + MIC */
467
	dev->mtu		= IEEE802154_MTU;
A
Alan Ott 已提交
468
	dev->tx_queue_len	= 300;
469
	dev->flags		= IFF_NOARP | IFF_BROADCAST;
470
}
471

472
static int
473 474
ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata,
		       enum nl802154_iftype type)
475
{
476 477
	struct wpan_dev *wpan_dev = &sdata->wpan_dev;

478
	/* set some type-dependent values */
479
	sdata->vif.type = type;
480
	sdata->wpan_dev.iftype = type;
481

482 483
	get_random_bytes(&wpan_dev->bsn, 1);
	get_random_bytes(&wpan_dev->dsn, 1);
484

485
	/* defaults per 802.15.4-2011 */
486 487 488
	wpan_dev->min_be = 3;
	wpan_dev->max_be = 5;
	wpan_dev->csma_retries = 4;
489
	/* for compatibility, actual default is 3 */
490
	wpan_dev->frame_retries = -1;
491

492 493
	wpan_dev->pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
	wpan_dev->short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
494

495
	switch (type) {
496
	case NL802154_IFTYPE_NODE:
497 498 499
		ieee802154_be64_to_le64(&wpan_dev->extended_addr,
					sdata->dev->dev_addr);

500 501 502 503
		sdata->dev->header_ops = &mac802154_header_ops;
		sdata->dev->destructor = mac802154_wpan_free;
		sdata->dev->netdev_ops = &mac802154_wpan_ops;
		sdata->dev->ml_priv = &mac802154_mlme_wpan;
504
		wpan_dev->promiscuous_mode = false;
505

506 507
		spin_lock_init(&sdata->mib_lock);
		mutex_init(&sdata->sec_mtx);
508

509 510
		mac802154_llsec_init(&sdata->sec);
		break;
511
	case NL802154_IFTYPE_MONITOR:
512 513
		sdata->dev->destructor = free_netdev;
		sdata->dev->netdev_ops = &mac802154_monitor_ops;
514
		wpan_dev->promiscuous_mode = true;
515 516 517 518
		break;
	default:
		BUG();
	}
519 520 521 522 523 524

	return 0;
}

struct net_device *
ieee802154_if_add(struct ieee802154_local *local, const char *name,
525
		  enum nl802154_iftype type, __le64 extended_addr)
526
{
527 528 529 530 531 532
	struct net_device *ndev = NULL;
	struct ieee802154_sub_if_data *sdata = NULL;
	int ret = -ENOMEM;

	ASSERT_RTNL();

533 534
	ndev = alloc_netdev(sizeof(*sdata) + local->hw.vif_data_size, name,
			    NET_NAME_UNKNOWN, ieee802154_if_setup);
535 536 537 538 539 540 541 542
	if (!ndev)
		return ERR_PTR(-ENOMEM);

	ndev->needed_headroom = local->hw.extra_tx_headroom;

	ret = dev_alloc_name(ndev, ndev->name);
	if (ret < 0)
		goto err;
543

544 545
	ieee802154_le64_to_be64(ndev->perm_addr,
				&local->hw.phy->perm_extended_addr);
546
	switch (type) {
547
	case NL802154_IFTYPE_NODE:
548
		ndev->type = ARPHRD_IEEE802154;
549 550 551 552 553
		if (ieee802154_is_valid_extended_addr(extended_addr))
			ieee802154_le64_to_be64(ndev->dev_addr, &extended_addr);
		else
			memcpy(ndev->dev_addr, ndev->perm_addr,
			       IEEE802154_EXTENDED_ADDR_LEN);
554
		break;
555
	case NL802154_IFTYPE_MONITOR:
556
		ndev->type = ARPHRD_IEEE802154_MONITOR;
557
		break;
558 559 560
	default:
		ret = -EINVAL;
		goto err;
561
	}
562 563 564 565 566 567 568 569 570 571 572 573 574

	/* TODO check this */
	SET_NETDEV_DEV(ndev, &local->phy->dev);
	sdata = netdev_priv(ndev);
	ndev->ieee802154_ptr = &sdata->wpan_dev;
	memcpy(sdata->name, ndev->name, IFNAMSIZ);
	sdata->dev = ndev;
	sdata->wpan_dev.wpan_phy = local->hw.phy;
	sdata->local = local;

	/* setup type-dependent data */
	ret = ieee802154_setup_sdata(sdata, type);
	if (ret)
575 576
		goto err;

577 578 579
	ret = register_netdevice(ndev);
	if (ret < 0)
		goto err;
580 581 582 583

	mutex_lock(&local->iflist_mtx);
	list_add_tail_rcu(&sdata->list, &local->interfaces);
	mutex_unlock(&local->iflist_mtx);
584

585
	return ndev;
586 587

err:
588 589
	free_netdev(ndev);
	return ERR_PTR(ret);
590 591
}

592 593 594 595 596 597 598 599 600 601 602
void ieee802154_if_remove(struct ieee802154_sub_if_data *sdata)
{
	ASSERT_RTNL();

	mutex_lock(&sdata->local->iflist_mtx);
	list_del_rcu(&sdata->list);
	mutex_unlock(&sdata->local->iflist_mtx);

	synchronize_rcu();
	unregister_netdevice(sdata->dev);
}
603 604 605

void ieee802154_remove_interfaces(struct ieee802154_local *local)
{
606
	struct ieee802154_sub_if_data *sdata, *tmp;
607

608
	mutex_lock(&local->iflist_mtx);
609
	list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
610 611 612 613
		list_del(&sdata->list);

		unregister_netdevice(sdata->dev);
	}
614
	mutex_unlock(&local->iflist_mtx);
615
}
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650

static int netdev_notify(struct notifier_block *nb,
			 unsigned long state, void *ptr)
{
	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
	struct ieee802154_sub_if_data *sdata;

	if (state != NETDEV_CHANGENAME)
		return NOTIFY_DONE;

	if (!dev->ieee802154_ptr || !dev->ieee802154_ptr->wpan_phy)
		return NOTIFY_DONE;

	if (dev->ieee802154_ptr->wpan_phy->privid != mac802154_wpan_phy_privid)
		return NOTIFY_DONE;

	sdata = IEEE802154_DEV_TO_SUB_IF(dev);
	memcpy(sdata->name, dev->name, IFNAMSIZ);

	return NOTIFY_OK;
}

static struct notifier_block mac802154_netdev_notifier = {
	.notifier_call = netdev_notify,
};

int ieee802154_iface_init(void)
{
	return register_netdevice_notifier(&mac802154_netdev_notifier);
}

void ieee802154_iface_exit(void)
{
	unregister_netdevice_notifier(&mac802154_netdev_notifier);
}