18c2ecf20Sopenharmony_ci/* Copyright 2011, Siemens AG 28c2ecf20Sopenharmony_ci * written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com> 38c2ecf20Sopenharmony_ci */ 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci/* Based on patches from Jon Smirl <jonsmirl@gmail.com> 68c2ecf20Sopenharmony_ci * Copyright (c) 2011 Jon Smirl <jonsmirl@gmail.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 98c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License version 2 108c2ecf20Sopenharmony_ci * as published by the Free Software Foundation. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, 138c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 148c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 158c2ecf20Sopenharmony_ci * GNU General Public License for more details. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* Jon's code is based on 6lowpan implementation for Contiki which is: 198c2ecf20Sopenharmony_ci * Copyright (c) 2008, Swedish Institute of Computer Science. 208c2ecf20Sopenharmony_ci * All rights reserved. 218c2ecf20Sopenharmony_ci * 228c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 238c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 248c2ecf20Sopenharmony_ci * are met: 258c2ecf20Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright 268c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 278c2ecf20Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright 288c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 298c2ecf20Sopenharmony_ci * documentation and/or other materials provided with the distribution. 308c2ecf20Sopenharmony_ci * 3. Neither the name of the Institute nor the names of its contributors 318c2ecf20Sopenharmony_ci * may be used to endorse or promote products derived from this software 328c2ecf20Sopenharmony_ci * without specific prior written permission. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 358c2ecf20Sopenharmony_ci * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 368c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 378c2ecf20Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 388c2ecf20Sopenharmony_ci * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 398c2ecf20Sopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 408c2ecf20Sopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 418c2ecf20Sopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 428c2ecf20Sopenharmony_ci * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 438c2ecf20Sopenharmony_ci * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 448c2ecf20Sopenharmony_ci * SUCH DAMAGE. 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#include <linux/module.h> 488c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 498c2ecf20Sopenharmony_ci#include <linux/ieee802154.h> 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#include <net/ipv6.h> 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#include "6lowpan_i.h" 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int open_count; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic const struct header_ops lowpan_header_ops = { 588c2ecf20Sopenharmony_ci .create = lowpan_header_create, 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic int lowpan_dev_init(struct net_device *ldev) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci netdev_lockdep_set_classes(ldev); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return 0; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic int lowpan_open(struct net_device *dev) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci if (!open_count) 718c2ecf20Sopenharmony_ci lowpan_rx_init(); 728c2ecf20Sopenharmony_ci open_count++; 738c2ecf20Sopenharmony_ci return 0; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int lowpan_stop(struct net_device *dev) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci open_count--; 798c2ecf20Sopenharmony_ci if (!open_count) 808c2ecf20Sopenharmony_ci lowpan_rx_exit(); 818c2ecf20Sopenharmony_ci return 0; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int lowpan_neigh_construct(struct net_device *dev, struct neighbour *n) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct lowpan_802154_neigh *neigh = lowpan_802154_neigh(neighbour_priv(n)); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* default no short_addr is available for a neighbour */ 898c2ecf20Sopenharmony_ci neigh->short_addr = cpu_to_le16(IEEE802154_ADDR_SHORT_UNSPEC); 908c2ecf20Sopenharmony_ci return 0; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int lowpan_get_iflink(const struct net_device *dev) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci return lowpan_802154_dev(dev)->wdev->ifindex; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic const struct net_device_ops lowpan_netdev_ops = { 998c2ecf20Sopenharmony_ci .ndo_init = lowpan_dev_init, 1008c2ecf20Sopenharmony_ci .ndo_start_xmit = lowpan_xmit, 1018c2ecf20Sopenharmony_ci .ndo_open = lowpan_open, 1028c2ecf20Sopenharmony_ci .ndo_stop = lowpan_stop, 1038c2ecf20Sopenharmony_ci .ndo_neigh_construct = lowpan_neigh_construct, 1048c2ecf20Sopenharmony_ci .ndo_get_iflink = lowpan_get_iflink, 1058c2ecf20Sopenharmony_ci}; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic void lowpan_setup(struct net_device *ldev) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci memset(ldev->broadcast, 0xff, IEEE802154_ADDR_LEN); 1108c2ecf20Sopenharmony_ci /* We need an ipv6hdr as minimum len when calling xmit */ 1118c2ecf20Sopenharmony_ci ldev->hard_header_len = sizeof(struct ipv6hdr); 1128c2ecf20Sopenharmony_ci ldev->flags = IFF_BROADCAST | IFF_MULTICAST; 1138c2ecf20Sopenharmony_ci ldev->priv_flags |= IFF_NO_QUEUE; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci ldev->netdev_ops = &lowpan_netdev_ops; 1168c2ecf20Sopenharmony_ci ldev->header_ops = &lowpan_header_ops; 1178c2ecf20Sopenharmony_ci ldev->needs_free_netdev = true; 1188c2ecf20Sopenharmony_ci ldev->features |= NETIF_F_NETNS_LOCAL; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int lowpan_validate(struct nlattr *tb[], struct nlattr *data[], 1228c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci if (tb[IFLA_ADDRESS]) { 1258c2ecf20Sopenharmony_ci if (nla_len(tb[IFLA_ADDRESS]) != IEEE802154_ADDR_LEN) 1268c2ecf20Sopenharmony_ci return -EINVAL; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci return 0; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic int lowpan_newlink(struct net *src_net, struct net_device *ldev, 1328c2ecf20Sopenharmony_ci struct nlattr *tb[], struct nlattr *data[], 1338c2ecf20Sopenharmony_ci struct netlink_ext_ack *extack) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct net_device *wdev; 1368c2ecf20Sopenharmony_ci int ret; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci ASSERT_RTNL(); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci pr_debug("adding new link\n"); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (!tb[IFLA_LINK]) 1438c2ecf20Sopenharmony_ci return -EINVAL; 1448c2ecf20Sopenharmony_ci /* find and hold wpan device */ 1458c2ecf20Sopenharmony_ci wdev = dev_get_by_index(dev_net(ldev), nla_get_u32(tb[IFLA_LINK])); 1468c2ecf20Sopenharmony_ci if (!wdev) 1478c2ecf20Sopenharmony_ci return -ENODEV; 1488c2ecf20Sopenharmony_ci if (wdev->type != ARPHRD_IEEE802154) { 1498c2ecf20Sopenharmony_ci dev_put(wdev); 1508c2ecf20Sopenharmony_ci return -EINVAL; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (wdev->ieee802154_ptr->lowpan_dev) { 1548c2ecf20Sopenharmony_ci dev_put(wdev); 1558c2ecf20Sopenharmony_ci return -EBUSY; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci lowpan_802154_dev(ldev)->wdev = wdev; 1598c2ecf20Sopenharmony_ci /* Set the lowpan hardware address to the wpan hardware address. */ 1608c2ecf20Sopenharmony_ci memcpy(ldev->dev_addr, wdev->dev_addr, IEEE802154_ADDR_LEN); 1618c2ecf20Sopenharmony_ci /* We need headroom for possible wpan_dev_hard_header call and tailroom 1628c2ecf20Sopenharmony_ci * for encryption/fcs handling. The lowpan interface will replace 1638c2ecf20Sopenharmony_ci * the IPv6 header with 6LoWPAN header. At worst case the 6LoWPAN 1648c2ecf20Sopenharmony_ci * header has LOWPAN_IPHC_MAX_HEADER_LEN more bytes than the IPv6 1658c2ecf20Sopenharmony_ci * header. 1668c2ecf20Sopenharmony_ci */ 1678c2ecf20Sopenharmony_ci ldev->needed_headroom = LOWPAN_IPHC_MAX_HEADER_LEN + 1688c2ecf20Sopenharmony_ci wdev->needed_headroom; 1698c2ecf20Sopenharmony_ci ldev->needed_tailroom = wdev->needed_tailroom; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci ldev->neigh_priv_len = sizeof(struct lowpan_802154_neigh); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci ret = lowpan_register_netdevice(ldev, LOWPAN_LLTYPE_IEEE802154); 1748c2ecf20Sopenharmony_ci if (ret < 0) { 1758c2ecf20Sopenharmony_ci dev_put(wdev); 1768c2ecf20Sopenharmony_ci return ret; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci wdev->ieee802154_ptr->lowpan_dev = ldev; 1808c2ecf20Sopenharmony_ci return 0; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic void lowpan_dellink(struct net_device *ldev, struct list_head *head) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct net_device *wdev = lowpan_802154_dev(ldev)->wdev; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci ASSERT_RTNL(); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci wdev->ieee802154_ptr->lowpan_dev = NULL; 1908c2ecf20Sopenharmony_ci lowpan_unregister_netdevice(ldev); 1918c2ecf20Sopenharmony_ci dev_put(wdev); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic struct rtnl_link_ops lowpan_link_ops __read_mostly = { 1958c2ecf20Sopenharmony_ci .kind = "lowpan", 1968c2ecf20Sopenharmony_ci .priv_size = LOWPAN_PRIV_SIZE(sizeof(struct lowpan_802154_dev)), 1978c2ecf20Sopenharmony_ci .setup = lowpan_setup, 1988c2ecf20Sopenharmony_ci .newlink = lowpan_newlink, 1998c2ecf20Sopenharmony_ci .dellink = lowpan_dellink, 2008c2ecf20Sopenharmony_ci .validate = lowpan_validate, 2018c2ecf20Sopenharmony_ci}; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic inline int __init lowpan_netlink_init(void) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci return rtnl_link_register(&lowpan_link_ops); 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic inline void lowpan_netlink_fini(void) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci rtnl_link_unregister(&lowpan_link_ops); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic int lowpan_device_event(struct notifier_block *unused, 2148c2ecf20Sopenharmony_ci unsigned long event, void *ptr) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct net_device *ndev = netdev_notifier_info_to_dev(ptr); 2178c2ecf20Sopenharmony_ci struct wpan_dev *wpan_dev; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (ndev->type != ARPHRD_IEEE802154) 2208c2ecf20Sopenharmony_ci return NOTIFY_DONE; 2218c2ecf20Sopenharmony_ci wpan_dev = ndev->ieee802154_ptr; 2228c2ecf20Sopenharmony_ci if (!wpan_dev) 2238c2ecf20Sopenharmony_ci return NOTIFY_DONE; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci switch (event) { 2268c2ecf20Sopenharmony_ci case NETDEV_UNREGISTER: 2278c2ecf20Sopenharmony_ci /* Check if wpan interface is unregistered that we 2288c2ecf20Sopenharmony_ci * also delete possible lowpan interfaces which belongs 2298c2ecf20Sopenharmony_ci * to the wpan interface. 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_ci if (wpan_dev->lowpan_dev) 2328c2ecf20Sopenharmony_ci lowpan_dellink(wpan_dev->lowpan_dev, NULL); 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci default: 2358c2ecf20Sopenharmony_ci return NOTIFY_DONE; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return NOTIFY_OK; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic struct notifier_block lowpan_dev_notifier = { 2428c2ecf20Sopenharmony_ci .notifier_call = lowpan_device_event, 2438c2ecf20Sopenharmony_ci}; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int __init lowpan_init_module(void) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci int err = 0; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci err = lowpan_net_frag_init(); 2508c2ecf20Sopenharmony_ci if (err < 0) 2518c2ecf20Sopenharmony_ci goto out; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci err = lowpan_netlink_init(); 2548c2ecf20Sopenharmony_ci if (err < 0) 2558c2ecf20Sopenharmony_ci goto out_frag; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci err = register_netdevice_notifier(&lowpan_dev_notifier); 2588c2ecf20Sopenharmony_ci if (err < 0) 2598c2ecf20Sopenharmony_ci goto out_pack; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci return 0; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ciout_pack: 2648c2ecf20Sopenharmony_ci lowpan_netlink_fini(); 2658c2ecf20Sopenharmony_ciout_frag: 2668c2ecf20Sopenharmony_ci lowpan_net_frag_exit(); 2678c2ecf20Sopenharmony_ciout: 2688c2ecf20Sopenharmony_ci return err; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic void __exit lowpan_cleanup_module(void) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci lowpan_netlink_fini(); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci lowpan_net_frag_exit(); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci unregister_netdevice_notifier(&lowpan_dev_notifier); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cimodule_init(lowpan_init_module); 2818c2ecf20Sopenharmony_cimodule_exit(lowpan_cleanup_module); 2828c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2838c2ecf20Sopenharmony_ciMODULE_ALIAS_RTNL_LINK("lowpan"); 284