162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Authors:
562306a36Sopenharmony_ci * (C) 2015 Pengutronix, Alexander Aring <aar@pengutronix.de>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/if_arp.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <net/6lowpan.h>
1262306a36Sopenharmony_ci#include <net/addrconf.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "6lowpan_i.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ciint lowpan_register_netdevice(struct net_device *dev,
1762306a36Sopenharmony_ci			      enum lowpan_lltypes lltype)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	int i, ret;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	switch (lltype) {
2262306a36Sopenharmony_ci	case LOWPAN_LLTYPE_IEEE802154:
2362306a36Sopenharmony_ci		dev->addr_len = EUI64_ADDR_LEN;
2462306a36Sopenharmony_ci		break;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	case LOWPAN_LLTYPE_BTLE:
2762306a36Sopenharmony_ci		dev->addr_len = ETH_ALEN;
2862306a36Sopenharmony_ci		break;
2962306a36Sopenharmony_ci	}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	dev->type = ARPHRD_6LOWPAN;
3262306a36Sopenharmony_ci	dev->mtu = IPV6_MIN_MTU;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	lowpan_dev(dev)->lltype = lltype;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	spin_lock_init(&lowpan_dev(dev)->ctx.lock);
3762306a36Sopenharmony_ci	for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
3862306a36Sopenharmony_ci		lowpan_dev(dev)->ctx.table[i].id = i;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	dev->ndisc_ops = &lowpan_ndisc_ops;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	ret = register_netdevice(dev);
4362306a36Sopenharmony_ci	if (ret < 0)
4462306a36Sopenharmony_ci		return ret;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	lowpan_dev_debugfs_init(dev);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	return ret;
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ciEXPORT_SYMBOL(lowpan_register_netdevice);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ciint lowpan_register_netdev(struct net_device *dev,
5362306a36Sopenharmony_ci			   enum lowpan_lltypes lltype)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	int ret;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	rtnl_lock();
5862306a36Sopenharmony_ci	ret = lowpan_register_netdevice(dev, lltype);
5962306a36Sopenharmony_ci	rtnl_unlock();
6062306a36Sopenharmony_ci	return ret;
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ciEXPORT_SYMBOL(lowpan_register_netdev);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_civoid lowpan_unregister_netdevice(struct net_device *dev)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	unregister_netdevice(dev);
6762306a36Sopenharmony_ci	lowpan_dev_debugfs_exit(dev);
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ciEXPORT_SYMBOL(lowpan_unregister_netdevice);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_civoid lowpan_unregister_netdev(struct net_device *dev)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	rtnl_lock();
7462306a36Sopenharmony_ci	lowpan_unregister_netdevice(dev);
7562306a36Sopenharmony_ci	rtnl_unlock();
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ciEXPORT_SYMBOL(lowpan_unregister_netdev);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ciint addrconf_ifid_802154_6lowpan(u8 *eui, struct net_device *dev)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	struct wpan_dev *wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	/* Set short_addr autoconfiguration if short_addr is present only */
8462306a36Sopenharmony_ci	if (!lowpan_802154_is_valid_src_short_addr(wpan_dev->short_addr))
8562306a36Sopenharmony_ci		return -1;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* For either address format, all zero addresses MUST NOT be used */
8862306a36Sopenharmony_ci	if (wpan_dev->pan_id == cpu_to_le16(0x0000) &&
8962306a36Sopenharmony_ci	    wpan_dev->short_addr == cpu_to_le16(0x0000))
9062306a36Sopenharmony_ci		return -1;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/* Alternatively, if no PAN ID is known, 16 zero bits may be used */
9362306a36Sopenharmony_ci	if (wpan_dev->pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST))
9462306a36Sopenharmony_ci		memset(eui, 0, 2);
9562306a36Sopenharmony_ci	else
9662306a36Sopenharmony_ci		ieee802154_le16_to_be16(eui, &wpan_dev->pan_id);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	/* The "Universal/Local" (U/L) bit shall be set to zero */
9962306a36Sopenharmony_ci	eui[0] &= ~2;
10062306a36Sopenharmony_ci	eui[2] = 0;
10162306a36Sopenharmony_ci	eui[3] = 0xFF;
10262306a36Sopenharmony_ci	eui[4] = 0xFE;
10362306a36Sopenharmony_ci	eui[5] = 0;
10462306a36Sopenharmony_ci	ieee802154_le16_to_be16(&eui[6], &wpan_dev->short_addr);
10562306a36Sopenharmony_ci	return 0;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic int lowpan_event(struct notifier_block *unused,
10962306a36Sopenharmony_ci			unsigned long event, void *ptr)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
11262306a36Sopenharmony_ci	struct inet6_dev *idev;
11362306a36Sopenharmony_ci	struct in6_addr addr;
11462306a36Sopenharmony_ci	int i;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (dev->type != ARPHRD_6LOWPAN)
11762306a36Sopenharmony_ci		return NOTIFY_DONE;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	idev = __in6_dev_get(dev);
12062306a36Sopenharmony_ci	if (!idev)
12162306a36Sopenharmony_ci		return NOTIFY_DONE;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	switch (event) {
12462306a36Sopenharmony_ci	case NETDEV_UP:
12562306a36Sopenharmony_ci	case NETDEV_CHANGE:
12662306a36Sopenharmony_ci		/* (802.15.4 6LoWPAN short address slaac handling */
12762306a36Sopenharmony_ci		if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154) &&
12862306a36Sopenharmony_ci		    addrconf_ifid_802154_6lowpan(addr.s6_addr + 8, dev) == 0) {
12962306a36Sopenharmony_ci			__ipv6_addr_set_half(&addr.s6_addr32[0],
13062306a36Sopenharmony_ci					     htonl(0xFE800000), 0);
13162306a36Sopenharmony_ci			addrconf_add_linklocal(idev, &addr, 0);
13262306a36Sopenharmony_ci		}
13362306a36Sopenharmony_ci		break;
13462306a36Sopenharmony_ci	case NETDEV_DOWN:
13562306a36Sopenharmony_ci		for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
13662306a36Sopenharmony_ci			clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE,
13762306a36Sopenharmony_ci				  &lowpan_dev(dev)->ctx.table[i].flags);
13862306a36Sopenharmony_ci		break;
13962306a36Sopenharmony_ci	default:
14062306a36Sopenharmony_ci		return NOTIFY_DONE;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	return NOTIFY_OK;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic struct notifier_block lowpan_notifier = {
14762306a36Sopenharmony_ci	.notifier_call = lowpan_event,
14862306a36Sopenharmony_ci};
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic int __init lowpan_module_init(void)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	int ret;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	lowpan_debugfs_init();
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	ret = register_netdevice_notifier(&lowpan_notifier);
15762306a36Sopenharmony_ci	if (ret < 0) {
15862306a36Sopenharmony_ci		lowpan_debugfs_exit();
15962306a36Sopenharmony_ci		return ret;
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	request_module_nowait("nhc_dest");
16362306a36Sopenharmony_ci	request_module_nowait("nhc_fragment");
16462306a36Sopenharmony_ci	request_module_nowait("nhc_hop");
16562306a36Sopenharmony_ci	request_module_nowait("nhc_ipv6");
16662306a36Sopenharmony_ci	request_module_nowait("nhc_mobility");
16762306a36Sopenharmony_ci	request_module_nowait("nhc_routing");
16862306a36Sopenharmony_ci	request_module_nowait("nhc_udp");
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	return 0;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic void __exit lowpan_module_exit(void)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	lowpan_debugfs_exit();
17662306a36Sopenharmony_ci	unregister_netdevice_notifier(&lowpan_notifier);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cimodule_init(lowpan_module_init);
18062306a36Sopenharmony_cimodule_exit(lowpan_module_exit);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
183