linux/net/6lowpan/core.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 *
   4 * Authors:
   5 * (C) 2015 Pengutronix, Alexander Aring <aar@pengutronix.de>
   6 */
   7
   8#include <linux/module.h>
   9
  10#include <net/6lowpan.h>
  11#include <net/addrconf.h>
  12
  13#include "6lowpan_i.h"
  14
  15int lowpan_register_netdevice(struct net_device *dev,
  16                              enum lowpan_lltypes lltype)
  17{
  18        int i, ret;
  19
  20        switch (lltype) {
  21        case LOWPAN_LLTYPE_IEEE802154:
  22                dev->addr_len = EUI64_ADDR_LEN;
  23                break;
  24
  25        case LOWPAN_LLTYPE_BTLE:
  26                dev->addr_len = ETH_ALEN;
  27                break;
  28        }
  29
  30        dev->type = ARPHRD_6LOWPAN;
  31        dev->mtu = IPV6_MIN_MTU;
  32
  33        lowpan_dev(dev)->lltype = lltype;
  34
  35        spin_lock_init(&lowpan_dev(dev)->ctx.lock);
  36        for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
  37                lowpan_dev(dev)->ctx.table[i].id = i;
  38
  39        dev->ndisc_ops = &lowpan_ndisc_ops;
  40
  41        ret = register_netdevice(dev);
  42        if (ret < 0)
  43                return ret;
  44
  45        lowpan_dev_debugfs_init(dev);
  46
  47        return ret;
  48}
  49EXPORT_SYMBOL(lowpan_register_netdevice);
  50
  51int lowpan_register_netdev(struct net_device *dev,
  52                           enum lowpan_lltypes lltype)
  53{
  54        int ret;
  55
  56        rtnl_lock();
  57        ret = lowpan_register_netdevice(dev, lltype);
  58        rtnl_unlock();
  59        return ret;
  60}
  61EXPORT_SYMBOL(lowpan_register_netdev);
  62
  63void lowpan_unregister_netdevice(struct net_device *dev)
  64{
  65        unregister_netdevice(dev);
  66        lowpan_dev_debugfs_exit(dev);
  67}
  68EXPORT_SYMBOL(lowpan_unregister_netdevice);
  69
  70void lowpan_unregister_netdev(struct net_device *dev)
  71{
  72        rtnl_lock();
  73        lowpan_unregister_netdevice(dev);
  74        rtnl_unlock();
  75}
  76EXPORT_SYMBOL(lowpan_unregister_netdev);
  77
  78int addrconf_ifid_802154_6lowpan(u8 *eui, struct net_device *dev)
  79{
  80        struct wpan_dev *wpan_dev = lowpan_802154_dev(dev)->wdev->ieee802154_ptr;
  81
  82        /* Set short_addr autoconfiguration if short_addr is present only */
  83        if (!lowpan_802154_is_valid_src_short_addr(wpan_dev->short_addr))
  84                return -1;
  85
  86        /* For either address format, all zero addresses MUST NOT be used */
  87        if (wpan_dev->pan_id == cpu_to_le16(0x0000) &&
  88            wpan_dev->short_addr == cpu_to_le16(0x0000))
  89                return -1;
  90
  91        /* Alternatively, if no PAN ID is known, 16 zero bits may be used */
  92        if (wpan_dev->pan_id == cpu_to_le16(IEEE802154_PAN_ID_BROADCAST))
  93                memset(eui, 0, 2);
  94        else
  95                ieee802154_le16_to_be16(eui, &wpan_dev->pan_id);
  96
  97        /* The "Universal/Local" (U/L) bit shall be set to zero */
  98        eui[0] &= ~2;
  99        eui[2] = 0;
 100        eui[3] = 0xFF;
 101        eui[4] = 0xFE;
 102        eui[5] = 0;
 103        ieee802154_le16_to_be16(&eui[6], &wpan_dev->short_addr);
 104        return 0;
 105}
 106
 107static int lowpan_event(struct notifier_block *unused,
 108                        unsigned long event, void *ptr)
 109{
 110        struct net_device *dev = netdev_notifier_info_to_dev(ptr);
 111        struct inet6_dev *idev;
 112        struct in6_addr addr;
 113        int i;
 114
 115        if (dev->type != ARPHRD_6LOWPAN)
 116                return NOTIFY_DONE;
 117
 118        idev = __in6_dev_get(dev);
 119        if (!idev)
 120                return NOTIFY_DONE;
 121
 122        switch (event) {
 123        case NETDEV_UP:
 124        case NETDEV_CHANGE:
 125                /* (802.15.4 6LoWPAN short address slaac handling */
 126                if (lowpan_is_ll(dev, LOWPAN_LLTYPE_IEEE802154) &&
 127                    addrconf_ifid_802154_6lowpan(addr.s6_addr + 8, dev) == 0) {
 128                        __ipv6_addr_set_half(&addr.s6_addr32[0],
 129                                             htonl(0xFE800000), 0);
 130                        addrconf_add_linklocal(idev, &addr, 0);
 131                }
 132                break;
 133        case NETDEV_DOWN:
 134                for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
 135                        clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE,
 136                                  &lowpan_dev(dev)->ctx.table[i].flags);
 137                break;
 138        default:
 139                return NOTIFY_DONE;
 140        }
 141
 142        return NOTIFY_OK;
 143}
 144
 145static struct notifier_block lowpan_notifier = {
 146        .notifier_call = lowpan_event,
 147};
 148
 149static int __init lowpan_module_init(void)
 150{
 151        int ret;
 152
 153        lowpan_debugfs_init();
 154
 155        ret = register_netdevice_notifier(&lowpan_notifier);
 156        if (ret < 0) {
 157                lowpan_debugfs_exit();
 158                return ret;
 159        }
 160
 161        request_module_nowait("nhc_dest");
 162        request_module_nowait("nhc_fragment");
 163        request_module_nowait("nhc_hop");
 164        request_module_nowait("nhc_ipv6");
 165        request_module_nowait("nhc_mobility");
 166        request_module_nowait("nhc_routing");
 167        request_module_nowait("nhc_udp");
 168
 169        return 0;
 170}
 171
 172static void __exit lowpan_module_exit(void)
 173{
 174        lowpan_debugfs_exit();
 175        unregister_netdevice_notifier(&lowpan_notifier);
 176}
 177
 178module_init(lowpan_module_init);
 179module_exit(lowpan_module_exit);
 180
 181MODULE_LICENSE("GPL");
 182