linux/net/ieee802154/6lowpan/core.c
<<
>>
Prefs
   1/* Copyright 2011, Siemens AG
   2 * written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
   3 */
   4
   5/* Based on patches from Jon Smirl <jonsmirl@gmail.com>
   6 * Copyright (c) 2011 Jon Smirl <jonsmirl@gmail.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2
  10 * as published by the Free Software Foundation.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15 * GNU General Public License for more details.
  16 */
  17
  18/* Jon's code is based on 6lowpan implementation for Contiki which is:
  19 * Copyright (c) 2008, Swedish Institute of Computer Science.
  20 * All rights reserved.
  21 *
  22 * Redistribution and use in source and binary forms, with or without
  23 * modification, are permitted provided that the following conditions
  24 * are met:
  25 * 1. Redistributions of source code must retain the above copyright
  26 *    notice, this list of conditions and the following disclaimer.
  27 * 2. Redistributions in binary form must reproduce the above copyright
  28 *    notice, this list of conditions and the following disclaimer in the
  29 *    documentation and/or other materials provided with the distribution.
  30 * 3. Neither the name of the Institute nor the names of its contributors
  31 *    may be used to endorse or promote products derived from this software
  32 *    without specific prior written permission.
  33 *
  34 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
  35 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  36 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  37 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
  38 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  39 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  40 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  41 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  42 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  43 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  44 * SUCH DAMAGE.
  45 */
  46
  47#include <linux/module.h>
  48#include <linux/netdevice.h>
  49#include <linux/ieee802154.h>
  50
  51#include <net/ipv6.h>
  52
  53#include "6lowpan_i.h"
  54
  55static int open_count;
  56
  57static const struct header_ops lowpan_header_ops = {
  58        .create = lowpan_header_create,
  59};
  60
  61static int lowpan_dev_init(struct net_device *ldev)
  62{
  63        netdev_lockdep_set_classes(ldev);
  64
  65        return 0;
  66}
  67
  68static int lowpan_open(struct net_device *dev)
  69{
  70        if (!open_count)
  71                lowpan_rx_init();
  72        open_count++;
  73        return 0;
  74}
  75
  76static int lowpan_stop(struct net_device *dev)
  77{
  78        open_count--;
  79        if (!open_count)
  80                lowpan_rx_exit();
  81        return 0;
  82}
  83
  84static int lowpan_neigh_construct(struct net_device *dev, struct neighbour *n)
  85{
  86        struct lowpan_802154_neigh *neigh = lowpan_802154_neigh(neighbour_priv(n));
  87
  88        /* default no short_addr is available for a neighbour */
  89        neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC);
  90        return 0;
  91}
  92
  93static int lowpan_get_iflink(const struct net_device *dev)
  94{
  95        return lowpan_802154_dev(dev)->wdev->ifindex;
  96}
  97
  98static const struct net_device_ops lowpan_netdev_ops = {
  99        .ndo_init               = lowpan_dev_init,
 100        .ndo_start_xmit         = lowpan_xmit,
 101        .ndo_open               = lowpan_open,
 102        .ndo_stop               = lowpan_stop,
 103        .ndo_neigh_construct    = lowpan_neigh_construct,
 104        .ndo_get_iflink         = lowpan_get_iflink,
 105};
 106
 107static void lowpan_setup(struct net_device *ldev)
 108{
 109        memset(ldev->broadcast, 0xff, IEEE802154_ADDR_LEN);
 110        /* We need an ipv6hdr as minimum len when calling xmit */
 111        ldev->hard_header_len   = sizeof(struct ipv6hdr);
 112        ldev->flags             = IFF_BROADCAST | IFF_MULTICAST;
 113        ldev->priv_flags        |= IFF_NO_QUEUE;
 114
 115        ldev->netdev_ops        = &lowpan_netdev_ops;
 116        ldev->header_ops        = &lowpan_header_ops;
 117        ldev->needs_free_netdev = true;
 118        ldev->features          |= NETIF_F_NETNS_LOCAL;
 119}
 120
 121static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[],
 122                           struct netlink_ext_ack *extack)
 123{
 124        if (tb[IFLA_ADDRESS]) {
 125                if (nla_len(tb[IFLA_ADDRESS]) != IEEE802154_ADDR_LEN)
 126                        return -EINVAL;
 127        }
 128        return 0;
 129}
 130
 131static int lowpan_newlink(struct net *src_net, struct net_device *ldev,
 132                          struct nlattr *tb[], struct nlattr *data[],
 133                          struct netlink_ext_ack *extack)
 134{
 135        struct net_device *wdev;
 136        int ret;
 137
 138        ASSERT_RTNL();
 139
 140        pr_debug("adding new link\n");
 141
 142        if (!tb[IFLA_LINK])
 143                return -EINVAL;
 144        /* find and hold wpan device */
 145        wdev = dev_get_by_index(dev_net(ldev), nla_get_u32(tb[IFLA_LINK]));
 146        if (!wdev)
 147                return -ENODEV;
 148        if (wdev->type != ARPHRD_IEEE802154) {
 149                dev_put(wdev);
 150                return -EINVAL;
 151        }
 152
 153        if (wdev->ieee802154_ptr->lowpan_dev) {
 154                dev_put(wdev);
 155                return -EBUSY;
 156        }
 157
 158        lowpan_802154_dev(ldev)->wdev = wdev;
 159        /* Set the lowpan hardware address to the wpan hardware address. */
 160        memcpy(ldev->dev_addr, wdev->dev_addr, IEEE802154_ADDR_LEN);
 161        /* We need headroom for possible wpan_dev_hard_header call and tailroom
 162         * for encryption/fcs handling. The lowpan interface will replace
 163         * the IPv6 header with 6LoWPAN header. At worst case the 6LoWPAN
 164         * header has LOWPAN_IPHC_MAX_HEADER_LEN more bytes than the IPv6
 165         * header.
 166         */
 167        ldev->needed_headroom = LOWPAN_IPHC_MAX_HEADER_LEN +
 168                                wdev->needed_headroom;
 169        ldev->needed_tailroom = wdev->needed_tailroom;
 170
 171        ldev->neigh_priv_len = sizeof(struct lowpan_802154_neigh);
 172
 173        ret = lowpan_register_netdevice(ldev, LOWPAN_LLTYPE_IEEE802154);
 174        if (ret < 0) {
 175                dev_put(wdev);
 176                return ret;
 177        }
 178
 179        wdev->ieee802154_ptr->lowpan_dev = ldev;
 180        return 0;
 181}
 182
 183static void lowpan_dellink(struct net_device *ldev, struct list_head *head)
 184{
 185        struct net_device *wdev = lowpan_802154_dev(ldev)->wdev;
 186
 187        ASSERT_RTNL();
 188
 189        wdev->ieee802154_ptr->lowpan_dev = NULL;
 190        lowpan_unregister_netdevice(ldev);
 191        dev_put(wdev);
 192}
 193
 194static struct rtnl_link_ops lowpan_link_ops __read_mostly = {
 195        .kind           = "lowpan",
 196        .priv_size      = LOWPAN_PRIV_SIZE(sizeof(struct lowpan_802154_dev)),
 197        .setup          = lowpan_setup,
 198        .newlink        = lowpan_newlink,
 199        .dellink        = lowpan_dellink,
 200        .validate       = lowpan_validate,
 201};
 202
 203static inline int __init lowpan_netlink_init(void)
 204{
 205        return rtnl_link_register(&lowpan_link_ops);
 206}
 207
 208static inline void lowpan_netlink_fini(void)
 209{
 210        rtnl_link_unregister(&lowpan_link_ops);
 211}
 212
 213static int lowpan_device_event(struct notifier_block *unused,
 214                               unsigned long event, void *ptr)
 215{
 216        struct net_device *ndev = netdev_notifier_info_to_dev(ptr);
 217        struct wpan_dev *wpan_dev;
 218
 219        if (ndev->type != ARPHRD_IEEE802154)
 220                return NOTIFY_DONE;
 221        wpan_dev = ndev->ieee802154_ptr;
 222        if (!wpan_dev)
 223                return NOTIFY_DONE;
 224
 225        switch (event) {
 226        case NETDEV_UNREGISTER:
 227                /* Check if wpan interface is unregistered that we
 228                 * also delete possible lowpan interfaces which belongs
 229                 * to the wpan interface.
 230                 */
 231                if (wpan_dev->lowpan_dev)
 232                        lowpan_dellink(wpan_dev->lowpan_dev, NULL);
 233                break;
 234        default:
 235                return NOTIFY_DONE;
 236        }
 237
 238        return NOTIFY_OK;
 239}
 240
 241static struct notifier_block lowpan_dev_notifier = {
 242        .notifier_call = lowpan_device_event,
 243};
 244
 245static int __init lowpan_init_module(void)
 246{
 247        int err = 0;
 248
 249        err = lowpan_net_frag_init();
 250        if (err < 0)
 251                goto out;
 252
 253        err = lowpan_netlink_init();
 254        if (err < 0)
 255                goto out_frag;
 256
 257        err = register_netdevice_notifier(&lowpan_dev_notifier);
 258        if (err < 0)
 259                goto out_pack;
 260
 261        return 0;
 262
 263out_pack:
 264        lowpan_netlink_fini();
 265out_frag:
 266        lowpan_net_frag_exit();
 267out:
 268        return err;
 269}
 270
 271static void __exit lowpan_cleanup_module(void)
 272{
 273        lowpan_netlink_fini();
 274
 275        lowpan_net_frag_exit();
 276
 277        unregister_netdevice_notifier(&lowpan_dev_notifier);
 278}
 279
 280module_init(lowpan_init_module);
 281module_exit(lowpan_cleanup_module);
 282MODULE_LICENSE("GPL");
 283MODULE_ALIAS_RTNL_LINK("lowpan");
 284