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