2014-02-28 07:32:48 +01:00
/* Copyright 2011, Siemens AG
2011-08-24 19:34:42 -07:00
* written by Alexander Smirnov < alex . bluesman . smirnov @ gmail . com >
*/
2014-02-28 07:32:48 +01:00
/* Based on patches from Jon Smirl <jonsmirl@gmail.com>
2011-08-24 19:34:42 -07:00
* Copyright ( c ) 2011 Jon Smirl < jonsmirl @ gmail . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
/* Jon's code is based on 6lowpan implementation for Contiki which is:
* Copyright ( c ) 2008 , Swedish Institute of Computer Science .
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* 2. Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ` ` AS IS ' ' AND
* ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION )
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT
* LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE .
*/
# include <linux/module.h>
# include <linux/netdevice.h>
2014-10-25 09:41:04 +02:00
# include <linux/ieee802154.h>
2015-01-04 17:10:56 +01:00
2011-08-24 19:34:42 -07:00
# include <net/ipv6.h>
2015-01-04 17:10:54 +01:00
# include "6lowpan_i.h"
2011-08-24 19:34:42 -07:00
2015-08-15 11:00:32 +02:00
static int open_count ;
2011-08-24 19:34:42 -07:00
static struct header_ops lowpan_header_ops = {
. create = lowpan_header_create ,
} ;
2014-02-10 11:42:35 -08:00
static struct lock_class_key lowpan_tx_busylock ;
static struct lock_class_key lowpan_netdev_xmit_lock_key ;
2015-09-02 14:21:16 +02:00
static void lowpan_set_lockdep_class_one ( struct net_device * ldev ,
2014-02-10 11:42:35 -08:00
struct netdev_queue * txq ,
void * _unused )
{
lockdep_set_class ( & txq - > _xmit_lock ,
& lowpan_netdev_xmit_lock_key ) ;
}
2015-09-02 14:21:16 +02:00
static int lowpan_dev_init ( struct net_device * ldev )
2014-02-10 11:42:35 -08:00
{
2015-09-02 14:21:16 +02:00
netdev_for_each_tx_queue ( ldev , lowpan_set_lockdep_class_one , NULL ) ;
ldev - > qdisc_tx_busylock = & lowpan_tx_busylock ;
2014-02-10 11:42:35 -08:00
return 0 ;
}
2015-09-02 14:21:17 +02:00
static int lowpan_open ( struct net_device * dev )
{
if ( ! open_count )
lowpan_rx_init ( ) ;
open_count + + ;
return 0 ;
}
static int lowpan_stop ( struct net_device * dev )
{
open_count - - ;
if ( ! open_count )
lowpan_rx_exit ( ) ;
return 0 ;
}
2011-08-24 19:34:42 -07:00
static const struct net_device_ops lowpan_netdev_ops = {
2014-02-10 11:42:35 -08:00
. ndo_init = lowpan_dev_init ,
2011-08-24 19:34:42 -07:00
. ndo_start_xmit = lowpan_xmit ,
2015-09-02 14:21:17 +02:00
. ndo_open = lowpan_open ,
. ndo_stop = lowpan_stop ,
2011-08-24 19:34:42 -07:00
} ;
2015-09-02 14:21:16 +02:00
static void lowpan_setup ( struct net_device * ldev )
2011-08-24 19:34:42 -07:00
{
2015-09-02 14:21:16 +02:00
ldev - > addr_len = IEEE802154_ADDR_LEN ;
memset ( ldev - > broadcast , 0xff , IEEE802154_ADDR_LEN ) ;
ldev - > type = ARPHRD_6LOWPAN ;
2011-08-24 19:34:42 -07:00
/* Frame Control + Sequence Number + Address fields + Security Header */
2015-09-02 14:21:16 +02:00
ldev - > hard_header_len = 2 + 1 + 20 + 14 ;
ldev - > needed_tailroom = 2 ; /* FCS */
ldev - > mtu = IPV6_MIN_MTU ;
ldev - > priv_flags | = IFF_NO_QUEUE ;
ldev - > flags = IFF_BROADCAST | IFF_MULTICAST ;
ldev - > netdev_ops = & lowpan_netdev_ops ;
ldev - > header_ops = & lowpan_header_ops ;
ldev - > destructor = free_netdev ;
ldev - > features | = NETIF_F_NETNS_LOCAL ;
2011-08-24 19:34:42 -07:00
}
static int lowpan_validate ( struct nlattr * tb [ ] , struct nlattr * data [ ] )
{
if ( tb [ IFLA_ADDRESS ] ) {
if ( nla_len ( tb [ IFLA_ADDRESS ] ) ! = IEEE802154_ADDR_LEN )
return - EINVAL ;
}
return 0 ;
}
2015-09-02 14:21:16 +02:00
static int lowpan_newlink ( struct net * src_net , struct net_device * ldev ,
2011-08-24 19:34:42 -07:00
struct nlattr * tb [ ] , struct nlattr * data [ ] )
{
2015-09-02 14:21:16 +02:00
struct net_device * wdev ;
2014-10-13 10:33:06 +02:00
int ret ;
2011-08-24 19:34:42 -07:00
2014-10-13 10:33:07 +02:00
ASSERT_RTNL ( ) ;
2012-06-25 03:49:03 +00:00
pr_debug ( " adding new link \n " ) ;
2011-08-24 19:34:42 -07:00
2015-02-05 18:21:30 +01:00
if ( ! tb [ IFLA_LINK ] | |
2015-09-02 14:21:16 +02:00
! net_eq ( dev_net ( ldev ) , & init_net ) )
2011-08-24 19:34:42 -07:00
return - EINVAL ;
2015-09-02 14:21:16 +02:00
/* find and hold wpan device */
wdev = dev_get_by_index ( dev_net ( ldev ) , nla_get_u32 ( tb [ IFLA_LINK ] ) ) ;
if ( ! wdev )
2011-08-24 19:34:42 -07:00
return - ENODEV ;
2015-09-02 14:21:16 +02:00
if ( wdev - > type ! = ARPHRD_IEEE802154 ) {
dev_put ( wdev ) ;
2013-10-05 23:15:18 -04:00
return - EINVAL ;
2013-11-07 10:44:45 +03:00
}
2011-08-24 19:34:42 -07:00
2015-09-02 14:21:16 +02:00
if ( wdev - > ieee802154_ptr - > lowpan_dev ) {
dev_put ( wdev ) ;
2015-08-10 21:15:53 +02:00
return - EBUSY ;
2011-08-30 03:51:09 +00:00
}
2011-08-24 19:34:42 -07:00
2015-09-02 14:21:16 +02:00
lowpan_dev_info ( ldev ) - > wdev = wdev ;
2014-11-25 10:04:57 +05:30
/* Set the lowpan hardware address to the wpan hardware address. */
2015-09-02 14:21:16 +02:00
memcpy ( ldev - > dev_addr , wdev - > dev_addr , IEEE802154_ADDR_LEN ) ;
2013-10-05 23:15:19 -04:00
2015-09-02 14:21:16 +02:00
lowpan_netdev_setup ( ldev , LOWPAN_LLTYPE_IEEE802154 ) ;
2015-08-11 21:44:08 +02:00
2015-09-02 14:21:16 +02:00
ret = register_netdevice ( ldev ) ;
2015-08-15 11:00:32 +02:00
if ( ret < 0 ) {
2015-09-02 14:21:16 +02:00
dev_put ( wdev ) ;
2015-08-15 11:00:32 +02:00
return ret ;
2014-10-13 10:33:06 +02:00
}
2011-08-24 19:34:42 -07:00
2015-09-02 14:21:16 +02:00
wdev - > ieee802154_ptr - > lowpan_dev = ldev ;
2015-08-15 11:00:32 +02:00
return 0 ;
2011-08-24 19:34:42 -07:00
}
2015-09-02 14:21:16 +02:00
static void lowpan_dellink ( struct net_device * ldev , struct list_head * head )
2011-08-24 19:34:42 -07:00
{
2015-09-02 14:21:16 +02:00
struct net_device * wdev = lowpan_dev_info ( ldev ) - > wdev ;
2011-08-24 19:34:42 -07:00
ASSERT_RTNL ( ) ;
2015-09-02 14:21:16 +02:00
wdev - > ieee802154_ptr - > lowpan_dev = NULL ;
unregister_netdevice ( ldev ) ;
dev_put ( wdev ) ;
2011-08-24 19:34:42 -07:00
}
static struct rtnl_link_ops lowpan_link_ops __read_mostly = {
. kind = " lowpan " ,
2015-08-11 21:44:08 +02:00
. priv_size = LOWPAN_PRIV_SIZE ( sizeof ( struct lowpan_dev_info ) ) ,
2011-08-24 19:34:42 -07:00
. setup = lowpan_setup ,
. newlink = lowpan_newlink ,
. dellink = lowpan_dellink ,
. validate = lowpan_validate ,
} ;
static inline int __init lowpan_netlink_init ( void )
{
return rtnl_link_register ( & lowpan_link_ops ) ;
}
2013-02-06 15:54:38 -05:00
static inline void lowpan_netlink_fini ( void )
2011-08-24 19:34:42 -07:00
{
rtnl_link_unregister ( & lowpan_link_ops ) ;
}
2012-09-01 05:57:07 +00:00
static int lowpan_device_event ( struct notifier_block * unused ,
2013-05-28 01:30:21 +00:00
unsigned long event , void * ptr )
2012-09-01 05:57:07 +00:00
{
2015-09-02 14:21:16 +02:00
struct net_device * wdev = netdev_notifier_info_to_dev ( ptr ) ;
2012-09-01 05:57:07 +00:00
2015-09-02 14:21:16 +02:00
if ( wdev - > type ! = ARPHRD_IEEE802154 )
2012-09-01 05:57:07 +00:00
goto out ;
2015-08-10 21:15:53 +02:00
switch ( event ) {
case NETDEV_UNREGISTER :
/* Check if wpan interface is unregistered that we
* also delete possible lowpan interfaces which belongs
* to the wpan interface .
*/
2015-09-02 14:21:18 +02:00
if ( wdev - > ieee802154_ptr - > lowpan_dev )
2015-09-02 14:21:16 +02:00
lowpan_dellink ( wdev - > ieee802154_ptr - > lowpan_dev , NULL ) ;
2015-08-10 21:15:53 +02:00
break ;
default :
break ;
2012-09-18 07:10:43 +00:00
}
2012-09-01 05:57:07 +00:00
out :
return NOTIFY_DONE ;
}
static struct notifier_block lowpan_dev_notifier = {
. notifier_call = lowpan_device_event ,
} ;
2011-08-24 19:34:42 -07:00
static int __init lowpan_init_module ( void )
{
int err = 0 ;
2014-02-28 07:32:50 +01:00
err = lowpan_net_frag_init ( ) ;
2011-08-24 19:34:42 -07:00
if ( err < 0 )
goto out ;
2014-02-28 07:32:50 +01:00
err = lowpan_netlink_init ( ) ;
if ( err < 0 )
goto out_frag ;
2012-09-01 05:57:07 +00:00
err = register_netdevice_notifier ( & lowpan_dev_notifier ) ;
2014-02-28 07:32:50 +01:00
if ( err < 0 )
goto out_pack ;
return 0 ;
out_pack :
lowpan_netlink_fini ( ) ;
out_frag :
lowpan_net_frag_exit ( ) ;
2011-08-24 19:34:42 -07:00
out :
return err ;
}
static void __exit lowpan_cleanup_module ( void )
{
lowpan_netlink_fini ( ) ;
2014-02-28 07:32:50 +01:00
lowpan_net_frag_exit ( ) ;
2012-09-01 05:57:07 +00:00
2014-02-28 07:32:50 +01:00
unregister_netdevice_notifier ( & lowpan_dev_notifier ) ;
2011-08-24 19:34:42 -07:00
}
module_init ( lowpan_init_module ) ;
module_exit ( lowpan_cleanup_module ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_RTNL_LINK ( " lowpan " ) ;