xref: /openbmc/linux/net/6lowpan/core.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21802d0beSThomas Gleixner /*
3b72f6f51SAlexander Aring  *
4b72f6f51SAlexander Aring  * Authors:
5b72f6f51SAlexander Aring  * (C) 2015 Pengutronix, Alexander Aring <aar@pengutronix.de>
6b72f6f51SAlexander Aring  */
7b72f6f51SAlexander Aring 
8*c78b8b20SJakub Kicinski #include <linux/if_arp.h>
94ae935c1SAlexander Aring #include <linux/module.h>
104ae935c1SAlexander Aring 
11b72f6f51SAlexander Aring #include <net/6lowpan.h>
122ad3ed59SAlexander Aring #include <net/addrconf.h>
13b72f6f51SAlexander Aring 
14b1815fd9SAlexander Aring #include "6lowpan_i.h"
15b1815fd9SAlexander Aring 
lowpan_register_netdevice(struct net_device * dev,enum lowpan_lltypes lltype)1600f59314SAlexander Aring int lowpan_register_netdevice(struct net_device *dev,
1700f59314SAlexander Aring 			      enum lowpan_lltypes lltype)
18b72f6f51SAlexander Aring {
195609c185SAlexander Aring 	int i, ret;
20b1815fd9SAlexander Aring 
21be054fc8SPatrik Flykt 	switch (lltype) {
22be054fc8SPatrik Flykt 	case LOWPAN_LLTYPE_IEEE802154:
234d6a6aedSAlexander Aring 		dev->addr_len = EUI64_ADDR_LEN;
24be054fc8SPatrik Flykt 		break;
25be054fc8SPatrik Flykt 
26be054fc8SPatrik Flykt 	case LOWPAN_LLTYPE_BTLE:
27be054fc8SPatrik Flykt 		dev->addr_len = ETH_ALEN;
28be054fc8SPatrik Flykt 		break;
29be054fc8SPatrik Flykt 	}
30be054fc8SPatrik Flykt 
314d6a6aedSAlexander Aring 	dev->type = ARPHRD_6LOWPAN;
324d6a6aedSAlexander Aring 	dev->mtu = IPV6_MIN_MTU;
334d6a6aedSAlexander Aring 
342e4d60cbSAlexander Aring 	lowpan_dev(dev)->lltype = lltype;
3500f59314SAlexander Aring 
362e4d60cbSAlexander Aring 	spin_lock_init(&lowpan_dev(dev)->ctx.lock);
375609c185SAlexander Aring 	for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
382e4d60cbSAlexander Aring 		lowpan_dev(dev)->ctx.table[i].id = i;
395609c185SAlexander Aring 
40bbe5f5ceSAlexander Aring 	dev->ndisc_ops = &lowpan_ndisc_ops;
41bbe5f5ceSAlexander Aring 
4292e17ee7SAlexander Aring 	ret = register_netdevice(dev);
43b1815fd9SAlexander Aring 	if (ret < 0)
44b1815fd9SAlexander Aring 		return ret;
45b1815fd9SAlexander Aring 
46db50450dSGreg Kroah-Hartman 	lowpan_dev_debugfs_init(dev);
47b1815fd9SAlexander Aring 
48b1815fd9SAlexander Aring 	return ret;
49b72f6f51SAlexander Aring }
5000f59314SAlexander Aring EXPORT_SYMBOL(lowpan_register_netdevice);
5100f59314SAlexander Aring 
lowpan_register_netdev(struct net_device * dev,enum lowpan_lltypes lltype)5200f59314SAlexander Aring int lowpan_register_netdev(struct net_device *dev,
5300f59314SAlexander Aring 			   enum lowpan_lltypes lltype)
5400f59314SAlexander Aring {
5500f59314SAlexander Aring 	int ret;
5600f59314SAlexander Aring 
5700f59314SAlexander Aring 	rtnl_lock();
5800f59314SAlexander Aring 	ret = lowpan_register_netdevice(dev, lltype);
5900f59314SAlexander Aring 	rtnl_unlock();
6000f59314SAlexander Aring 	return ret;
6100f59314SAlexander Aring }
6200f59314SAlexander Aring EXPORT_SYMBOL(lowpan_register_netdev);
6300f59314SAlexander Aring 
lowpan_unregister_netdevice(struct net_device * dev)6400f59314SAlexander Aring void lowpan_unregister_netdevice(struct net_device *dev)
6500f59314SAlexander Aring {
6600f59314SAlexander Aring 	unregister_netdevice(dev);
67b1815fd9SAlexander Aring 	lowpan_dev_debugfs_exit(dev);
6800f59314SAlexander Aring }
6900f59314SAlexander Aring EXPORT_SYMBOL(lowpan_unregister_netdevice);
7000f59314SAlexander Aring 
lowpan_unregister_netdev(struct net_device * dev)7100f59314SAlexander Aring void lowpan_unregister_netdev(struct net_device *dev)
7200f59314SAlexander Aring {
7300f59314SAlexander Aring 	rtnl_lock();
7400f59314SAlexander Aring 	lowpan_unregister_netdevice(dev);
7500f59314SAlexander Aring 	rtnl_unlock();
7600f59314SAlexander Aring }
7700f59314SAlexander Aring EXPORT_SYMBOL(lowpan_unregister_netdev);
784ae935c1SAlexander Aring 
addrconf_ifid_802154_6lowpan(u8 * eui,struct net_device * dev)79bbe5f5ceSAlexander Aring int addrconf_ifid_802154_6lowpan(u8 *eui, struct net_device *dev)
802ad3ed59SAlexander Aring {
812ad3ed59SAlexander Aring 	struct wpan_dev *wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
822ad3ed59SAlexander Aring 
832ad3ed59SAlexander Aring 	/* Set short_addr autoconfiguration if short_addr is present only */
842ad3ed59SAlexander Aring 	if (!lowpan_802154_is_valid_src_short_addr(wpan_dev->short_addr))
852ad3ed59SAlexander Aring 		return -1;
862ad3ed59SAlexander Aring 
872ad3ed59SAlexander Aring 	/* For either address format, all zero addresses MUST NOT be used */
882ad3ed59SAlexander Aring 	if (wpan_dev->pan_id == cpu_to_le16(0x0000) &&
892ad3ed59SAlexander Aring 	    wpan_dev->short_addr == cpu_to_le16(0x0000))
902ad3ed59SAlexander Aring 		return -1;
912ad3ed59SAlexander Aring 
922ad3ed59SAlexander Aring 	/* Alternatively, if no PAN ID is known, 16 zero bits may be used */
932ad3ed59SAlexander Aring 	if (wpan_dev->pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST))
942ad3ed59SAlexander Aring 		memset(eui, 0, 2);
952ad3ed59SAlexander Aring 	else
962ad3ed59SAlexander Aring 		ieee802154_le16_to_be16(eui, &wpan_dev->pan_id);
972ad3ed59SAlexander Aring 
982ad3ed59SAlexander Aring 	/* The "Universal/Local" (U/L) bit shall be set to zero */
992ad3ed59SAlexander Aring 	eui[0] &= ~2;
1002ad3ed59SAlexander Aring 	eui[2] = 0;
1012ad3ed59SAlexander Aring 	eui[3] = 0xFF;
1022ad3ed59SAlexander Aring 	eui[4] = 0xFE;
1032ad3ed59SAlexander Aring 	eui[5] = 0;
1042ad3ed59SAlexander Aring 	ieee802154_le16_to_be16(&eui[6], &wpan_dev->short_addr);
1052ad3ed59SAlexander Aring 	return 0;
1062ad3ed59SAlexander Aring }
1072ad3ed59SAlexander Aring 
lowpan_event(struct notifier_block * unused,unsigned long event,void * ptr)1085609c185SAlexander Aring static int lowpan_event(struct notifier_block *unused,
1095609c185SAlexander Aring 			unsigned long event, void *ptr)
1105609c185SAlexander Aring {
1115609c185SAlexander Aring 	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
1122ad3ed59SAlexander Aring 	struct inet6_dev *idev;
1132ad3ed59SAlexander Aring 	struct in6_addr addr;
1145609c185SAlexander Aring 	int i;
1155609c185SAlexander Aring 
1165609c185SAlexander Aring 	if (dev->type != ARPHRD_6LOWPAN)
1175609c185SAlexander Aring 		return NOTIFY_DONE;
1185609c185SAlexander Aring 
1192ad3ed59SAlexander Aring 	idev = __in6_dev_get(dev);
1202ad3ed59SAlexander Aring 	if (!idev)
1212ad3ed59SAlexander Aring 		return NOTIFY_DONE;
1222ad3ed59SAlexander Aring 
1235609c185SAlexander Aring 	switch (event) {
1242ad3ed59SAlexander Aring 	case NETDEV_UP:
1252ad3ed59SAlexander Aring 	case NETDEV_CHANGE:
1262ad3ed59SAlexander Aring 		/* (802.15.4 6LoWPAN short address slaac handling */
1272ad3ed59SAlexander Aring 		if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154) &&
1282ad3ed59SAlexander Aring 		    addrconf_ifid_802154_6lowpan(addr.s6_addr + 8, dev) == 0) {
1292ad3ed59SAlexander Aring 			__ipv6_addr_set_half(&addr.s6_addr32[0],
1302ad3ed59SAlexander Aring 					     htonl(0xFE800000), 0);
1312ad3ed59SAlexander Aring 			addrconf_add_linklocal(idev, &addr, 0);
1322ad3ed59SAlexander Aring 		}
1332ad3ed59SAlexander Aring 		break;
1345609c185SAlexander Aring 	case NETDEV_DOWN:
1355609c185SAlexander Aring 		for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
1365609c185SAlexander Aring 			clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE,
1372e4d60cbSAlexander Aring 				  &lowpan_dev(dev)->ctx.table[i].flags);
1385609c185SAlexander Aring 		break;
1395609c185SAlexander Aring 	default:
1405609c185SAlexander Aring 		return NOTIFY_DONE;
1415609c185SAlexander Aring 	}
1425609c185SAlexander Aring 
1435609c185SAlexander Aring 	return NOTIFY_OK;
1445609c185SAlexander Aring }
1455609c185SAlexander Aring 
1465609c185SAlexander Aring static struct notifier_block lowpan_notifier = {
1475609c185SAlexander Aring 	.notifier_call = lowpan_event,
1485609c185SAlexander Aring };
1495609c185SAlexander Aring 
lowpan_module_init(void)1504ae935c1SAlexander Aring static int __init lowpan_module_init(void)
1514ae935c1SAlexander Aring {
152b1815fd9SAlexander Aring 	int ret;
153b1815fd9SAlexander Aring 
154db50450dSGreg Kroah-Hartman 	lowpan_debugfs_init();
155b1815fd9SAlexander Aring 
1565609c185SAlexander Aring 	ret = register_netdevice_notifier(&lowpan_notifier);
1575609c185SAlexander Aring 	if (ret < 0) {
1585609c185SAlexander Aring 		lowpan_debugfs_exit();
1595609c185SAlexander Aring 		return ret;
1605609c185SAlexander Aring 	}
1615609c185SAlexander Aring 
1624ae935c1SAlexander Aring 	request_module_nowait("nhc_dest");
1634ae935c1SAlexander Aring 	request_module_nowait("nhc_fragment");
1644ae935c1SAlexander Aring 	request_module_nowait("nhc_hop");
1654ae935c1SAlexander Aring 	request_module_nowait("nhc_ipv6");
1664ae935c1SAlexander Aring 	request_module_nowait("nhc_mobility");
1674ae935c1SAlexander Aring 	request_module_nowait("nhc_routing");
1684ae935c1SAlexander Aring 	request_module_nowait("nhc_udp");
1694ae935c1SAlexander Aring 
1704ae935c1SAlexander Aring 	return 0;
1714ae935c1SAlexander Aring }
172b1815fd9SAlexander Aring 
lowpan_module_exit(void)173b1815fd9SAlexander Aring static void __exit lowpan_module_exit(void)
174b1815fd9SAlexander Aring {
175b1815fd9SAlexander Aring 	lowpan_debugfs_exit();
1765609c185SAlexander Aring 	unregister_netdevice_notifier(&lowpan_notifier);
177b1815fd9SAlexander Aring }
178b1815fd9SAlexander Aring 
1794ae935c1SAlexander Aring module_init(lowpan_module_init);
180b1815fd9SAlexander Aring module_exit(lowpan_module_exit);
1814ae935c1SAlexander Aring 
1824ae935c1SAlexander Aring MODULE_LICENSE("GPL");
183