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 55LIST_HEAD(lowpan_devices); 56static int lowpan_open_count; 57 58static __le16 lowpan_get_pan_id(const struct net_device *dev) 59{ 60 struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; 61 62 return ieee802154_mlme_ops(real_dev)->get_pan_id(real_dev); 63} 64 65static __le16 lowpan_get_short_addr(const struct net_device *dev) 66{ 67 struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; 68 69 return ieee802154_mlme_ops(real_dev)->get_short_addr(real_dev); 70} 71 72static u8 lowpan_get_dsn(const struct net_device *dev) 73{ 74 struct net_device *real_dev = lowpan_dev_info(dev)->real_dev; 75 76 return ieee802154_mlme_ops(real_dev)->get_dsn(real_dev); 77} 78 79static struct header_ops lowpan_header_ops = { 80 .create = lowpan_header_create, 81}; 82 83static struct lock_class_key lowpan_tx_busylock; 84static struct lock_class_key lowpan_netdev_xmit_lock_key; 85 86static void lowpan_set_lockdep_class_one(struct net_device *dev, 87 struct netdev_queue *txq, 88 void *_unused) 89{ 90 lockdep_set_class(&txq->_xmit_lock, 91 &lowpan_netdev_xmit_lock_key); 92} 93 94static int lowpan_dev_init(struct net_device *dev) 95{ 96 netdev_for_each_tx_queue(dev, lowpan_set_lockdep_class_one, NULL); 97 dev->qdisc_tx_busylock = &lowpan_tx_busylock; 98 return 0; 99} 100 101static const struct net_device_ops lowpan_netdev_ops = { 102 .ndo_init = lowpan_dev_init, 103 .ndo_start_xmit = lowpan_xmit, 104}; 105 106static struct ieee802154_mlme_ops lowpan_mlme = { 107 .get_pan_id = lowpan_get_pan_id, 108 .get_short_addr = lowpan_get_short_addr, 109 .get_dsn = lowpan_get_dsn, 110}; 111 112static void lowpan_setup(struct net_device *dev) 113{ 114 dev->addr_len = IEEE802154_ADDR_LEN; 115 memset(dev->broadcast, 0xff, IEEE802154_ADDR_LEN); 116 dev->type = ARPHRD_6LOWPAN; 117 /* Frame Control + Sequence Number + Address fields + Security Header */ 118 dev->hard_header_len = 2 + 1 + 20 + 14; 119 dev->needed_tailroom = 2; /* FCS */ 120 dev->mtu = IPV6_MIN_MTU; 121 dev->tx_queue_len = 0; 122 dev->flags = IFF_BROADCAST | IFF_MULTICAST; 123 dev->watchdog_timeo = 0; 124 125 dev->netdev_ops = &lowpan_netdev_ops; 126 dev->header_ops = &lowpan_header_ops; 127 dev->ml_priv = &lowpan_mlme; 128 dev->destructor = free_netdev; 129 dev->features |= NETIF_F_NETNS_LOCAL; 130} 131 132static int lowpan_validate(struct nlattr *tb[], struct nlattr *data[]) 133{ 134 if (tb[IFLA_ADDRESS]) { 135 if (nla_len(tb[IFLA_ADDRESS]) != IEEE802154_ADDR_LEN) 136 return -EINVAL; 137 } 138 return 0; 139} 140 141static int lowpan_newlink(struct net *src_net, struct net_device *dev, 142 struct nlattr *tb[], struct nlattr *data[]) 143{ 144 struct net_device *real_dev; 145 struct lowpan_dev_record *entry; 146 int ret; 147 148 ASSERT_RTNL(); 149 150 pr_debug("adding new link\n"); 151 152 if (!tb[IFLA_LINK] || 153 !net_eq(dev_net(dev), &init_net)) 154 return -EINVAL; 155 /* find and hold real wpan device */ 156 real_dev = dev_get_by_index(dev_net(dev), nla_get_u32(tb[IFLA_LINK])); 157 if (!real_dev) 158 return -ENODEV; 159 if (real_dev->type != ARPHRD_IEEE802154) { 160 dev_put(real_dev); 161 return -EINVAL; 162 } 163 164 lowpan_dev_info(dev)->real_dev = real_dev; 165 mutex_init(&lowpan_dev_info(dev)->dev_list_mtx); 166 167 entry = kzalloc(sizeof(*entry), GFP_KERNEL); 168 if (!entry) { 169 dev_put(real_dev); 170 lowpan_dev_info(dev)->real_dev = NULL; 171 return -ENOMEM; 172 } 173 174 entry->ldev = dev; 175 176 /* Set the lowpan hardware address to the wpan hardware address. */ 177 memcpy(dev->dev_addr, real_dev->dev_addr, IEEE802154_ADDR_LEN); 178 179 mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); 180 INIT_LIST_HEAD(&entry->list); 181 list_add_tail(&entry->list, &lowpan_devices); 182 mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); 183 184 ret = register_netdevice(dev); 185 if (ret >= 0) { 186 if (!lowpan_open_count) 187 lowpan_rx_init(); 188 lowpan_open_count++; 189 } 190 191 return ret; 192} 193 194static void lowpan_dellink(struct net_device *dev, struct list_head *head) 195{ 196 struct lowpan_dev_info *lowpan_dev = lowpan_dev_info(dev); 197 struct net_device *real_dev = lowpan_dev->real_dev; 198 struct lowpan_dev_record *entry, *tmp; 199 200 ASSERT_RTNL(); 201 202 lowpan_open_count--; 203 if (!lowpan_open_count) 204 lowpan_rx_exit(); 205 206 mutex_lock(&lowpan_dev_info(dev)->dev_list_mtx); 207 list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { 208 if (entry->ldev == dev) { 209 list_del(&entry->list); 210 kfree(entry); 211 } 212 } 213 mutex_unlock(&lowpan_dev_info(dev)->dev_list_mtx); 214 215 mutex_destroy(&lowpan_dev_info(dev)->dev_list_mtx); 216 217 unregister_netdevice_queue(dev, head); 218 219 dev_put(real_dev); 220} 221 222static struct rtnl_link_ops lowpan_link_ops __read_mostly = { 223 .kind = "lowpan", 224 .priv_size = sizeof(struct lowpan_dev_info), 225 .setup = lowpan_setup, 226 .newlink = lowpan_newlink, 227 .dellink = lowpan_dellink, 228 .validate = lowpan_validate, 229}; 230 231static inline int __init lowpan_netlink_init(void) 232{ 233 return rtnl_link_register(&lowpan_link_ops); 234} 235 236static inline void lowpan_netlink_fini(void) 237{ 238 rtnl_link_unregister(&lowpan_link_ops); 239} 240 241static int lowpan_device_event(struct notifier_block *unused, 242 unsigned long event, void *ptr) 243{ 244 struct net_device *dev = netdev_notifier_info_to_dev(ptr); 245 LIST_HEAD(del_list); 246 struct lowpan_dev_record *entry, *tmp; 247 248 if (dev->type != ARPHRD_IEEE802154) 249 goto out; 250 251 if (event == NETDEV_UNREGISTER) { 252 list_for_each_entry_safe(entry, tmp, &lowpan_devices, list) { 253 if (lowpan_dev_info(entry->ldev)->real_dev == dev) 254 lowpan_dellink(entry->ldev, &del_list); 255 } 256 257 unregister_netdevice_many(&del_list); 258 } 259 260out: 261 return NOTIFY_DONE; 262} 263 264static struct notifier_block lowpan_dev_notifier = { 265 .notifier_call = lowpan_device_event, 266}; 267 268static int __init lowpan_init_module(void) 269{ 270 int err = 0; 271 272 err = lowpan_net_frag_init(); 273 if (err < 0) 274 goto out; 275 276 err = lowpan_netlink_init(); 277 if (err < 0) 278 goto out_frag; 279 280 err = register_netdevice_notifier(&lowpan_dev_notifier); 281 if (err < 0) 282 goto out_pack; 283 284 return 0; 285 286out_pack: 287 lowpan_netlink_fini(); 288out_frag: 289 lowpan_net_frag_exit(); 290out: 291 return err; 292} 293 294static void __exit lowpan_cleanup_module(void) 295{ 296 lowpan_netlink_fini(); 297 298 lowpan_net_frag_exit(); 299 300 unregister_netdevice_notifier(&lowpan_dev_notifier); 301} 302 303module_init(lowpan_init_module); 304module_exit(lowpan_cleanup_module); 305MODULE_LICENSE("GPL"); 306MODULE_ALIAS_RTNL_LINK("lowpan"); 307