2014-02-28 10:32:48 +04:00
/* Copyright 2011, Siemens AG
2011-08-25 06:34:42 +04:00
* written by Alexander Smirnov < alex . bluesman . smirnov @ gmail . com >
*/
2014-02-28 10:32:48 +04:00
/* Based on patches from Jon Smirl <jonsmirl@gmail.com>
2011-08-25 06:34:42 +04: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/bitops.h>
# include <linux/if_arp.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/netdevice.h>
2014-10-25 11:41:04 +04:00
# include <linux/ieee802154.h>
2011-08-25 06:34:42 +04:00
# include <net/af_ieee802154.h>
# include <net/ieee802154_netdev.h>
2014-03-05 17:29:05 +04:00
# include <net/6lowpan.h>
2011-08-25 06:34:42 +04:00
# include <net/ipv6.h>
2014-02-28 10:32:50 +04:00
# include "reassembly.h"
2011-08-25 06:34:42 +04:00
static LIST_HEAD ( lowpan_devices ) ;
2014-10-13 12:33:06 +04:00
static int lowpan_open_count ;
2011-08-25 06:34:42 +04:00
/* private device info */
struct lowpan_dev_info {
struct net_device * real_dev ; /* real WPAN device ptr */
struct mutex dev_list_mtx ; /* mutex for list ops */
2014-10-06 13:00:50 +04:00
u16 fragment_tag ;
2011-08-25 06:34:42 +04:00
} ;
struct lowpan_dev_record {
struct net_device * ldev ;
struct list_head list ;
} ;
2014-09-24 14:21:33 +04:00
/* don't save pan id, it's intra pan */
struct lowpan_addr {
u8 mode ;
union {
/* IPv6 needs big endian here */
__be64 extended_addr ;
__be16 short_addr ;
} u ;
} ;
struct lowpan_addr_info {
struct lowpan_addr daddr ;
struct lowpan_addr saddr ;
} ;
2011-08-25 06:34:42 +04:00
static inline struct
lowpan_dev_info * lowpan_dev_info ( const struct net_device * dev )
{
return netdev_priv ( dev ) ;
}
2014-09-24 14:21:33 +04:00
static inline struct
lowpan_addr_info * lowpan_skb_priv ( const struct sk_buff * skb )
{
WARN_ON_ONCE ( skb_headroom ( skb ) < sizeof ( struct lowpan_addr_info ) ) ;
return ( struct lowpan_addr_info * ) ( skb - > data -
sizeof ( struct lowpan_addr_info ) ) ;
}
2014-07-02 07:31:09 +04:00
static int lowpan_header_create ( struct sk_buff * skb , struct net_device * dev ,
unsigned short type , const void * _daddr ,
const void * _saddr , unsigned int len )
2011-08-25 06:34:42 +04:00
{
const u8 * saddr = _saddr ;
const u8 * daddr = _daddr ;
2014-09-24 14:21:33 +04:00
struct lowpan_addr_info * info ;
2011-08-25 06:34:42 +04:00
2013-02-05 14:23:43 +04:00
/* TODO:
* if this package isn ' t ipv6 one , where should it be routed ?
*/
2011-08-25 06:34:42 +04:00
if ( type ! = ETH_P_IPV6 )
return 0 ;
if ( ! saddr )
saddr = dev - > dev_addr ;
2013-12-12 23:15:25 +04:00
raw_dump_inline ( __func__ , " saddr " , ( unsigned char * ) saddr , 8 ) ;
raw_dump_inline ( __func__ , " daddr " , ( unsigned char * ) daddr , 8 ) ;
2011-08-25 06:34:42 +04:00
2014-09-24 14:21:33 +04:00
info = lowpan_skb_priv ( skb ) ;
2013-12-11 19:05:34 +04:00
2014-09-24 14:21:33 +04:00
/* TODO: Currently we only support extended_addr */
info - > daddr . mode = IEEE802154_ADDR_LONG ;
memcpy ( & info - > daddr . u . extended_addr , daddr ,
sizeof ( info - > daddr . u . extended_addr ) ) ;
info - > saddr . mode = IEEE802154_ADDR_LONG ;
memcpy ( & info - > saddr . u . extended_addr , saddr ,
sizeof ( info - > daddr . u . extended_addr ) ) ;
2014-05-14 19:43:08 +04:00
2014-09-24 14:21:33 +04:00
return 0 ;
2011-08-25 06:34:42 +04:00
}
2013-12-11 19:05:34 +04:00
static int lowpan_give_skb_to_devices ( struct sk_buff * skb ,
2014-07-02 07:31:09 +04:00
struct net_device * dev )
2013-01-16 23:09:47 +04:00
{
struct lowpan_dev_record * entry ;
struct sk_buff * skb_cp ;
int stat = NET_RX_SUCCESS ;
2014-10-23 18:40:53 +04:00
skb - > protocol = htons ( ETH_P_IPV6 ) ;
skb - > pkt_type = PACKET_HOST ;
2013-01-16 23:09:47 +04:00
rcu_read_lock ( ) ;
list_for_each_entry_rcu ( entry , & lowpan_devices , list )
if ( lowpan_dev_info ( entry - > ldev ) - > real_dev = = skb - > dev ) {
skb_cp = skb_copy ( skb , GFP_ATOMIC ) ;
if ( ! skb_cp ) {
2014-10-23 18:40:53 +04:00
kfree_skb ( skb ) ;
rcu_read_unlock ( ) ;
return NET_RX_DROP ;
2013-01-16 23:09:47 +04:00
}
skb_cp - > dev = entry - > ldev ;
stat = netif_rx ( skb_cp ) ;
2014-10-23 18:40:53 +04:00
if ( stat = = NET_RX_DROP )
break ;
2013-01-16 23:09:47 +04:00
}
rcu_read_unlock ( ) ;
2014-10-23 18:40:53 +04:00
consume_skb ( skb ) ;
2013-01-16 23:09:47 +04:00
return stat ;
}
2014-10-23 18:40:56 +04:00
static int
iphc_decompress ( struct sk_buff * skb , const struct ieee802154_hdr * hdr )
2011-08-25 06:34:42 +04:00
{
2013-12-11 19:05:34 +04:00
u8 iphc0 , iphc1 ;
2014-03-15 00:24:02 +04:00
struct ieee802154_addr_sa sa , da ;
void * sap , * dap ;
2011-08-25 06:34:42 +04:00
2013-12-12 23:15:25 +04:00
raw_dump_table ( __func__ , " raw skb data dump " , skb - > data , skb - > len ) ;
2011-08-25 06:34:42 +04:00
/* at least two bytes will be used for the encoding */
if ( skb - > len < 2 )
2014-11-06 22:15:13 +03:00
return - EINVAL ;
2012-06-25 07:49:01 +04:00
if ( lowpan_fetch_skb_u8 ( skb , & iphc0 ) )
2014-11-06 22:15:13 +03:00
return - EINVAL ;
2011-11-10 11:38:38 +04:00
2012-06-25 07:49:01 +04:00
if ( lowpan_fetch_skb_u8 ( skb , & iphc1 ) )
2014-11-06 22:15:13 +03:00
return - EINVAL ;
2011-08-25 06:34:42 +04:00
2014-03-15 00:24:02 +04:00
ieee802154_addr_to_sa ( & sa , & hdr - > source ) ;
ieee802154_addr_to_sa ( & da , & hdr - > dest ) ;
2011-08-25 06:34:42 +04:00
2014-03-15 00:24:02 +04:00
if ( sa . addr_type = = IEEE802154_ADDR_SHORT )
sap = & sa . short_addr ;
else
sap = & sa . hwaddr ;
if ( da . addr_type = = IEEE802154_ADDR_SHORT )
dap = & da . short_addr ;
else
dap = & da . hwaddr ;
2014-10-23 18:40:56 +04:00
return lowpan_header_decompress ( skb , skb - > dev , sap , sa . addr_type ,
IEEE802154_ADDR_LEN , dap , da . addr_type ,
IEEE802154_ADDR_LEN , iphc0 , iphc1 ) ;
2011-08-25 06:34:42 +04:00
}
2014-05-14 19:43:09 +04:00
static struct sk_buff *
lowpan_alloc_frag ( struct sk_buff * skb , int size ,
const struct ieee802154_hdr * master_hdr )
2011-11-10 11:38:38 +04:00
{
2014-05-14 19:43:09 +04:00
struct net_device * real_dev = lowpan_dev_info ( skb - > dev ) - > real_dev ;
2011-11-10 11:38:38 +04:00
struct sk_buff * frag ;
2014-05-14 19:43:09 +04:00
int rc ;
frag = alloc_skb ( real_dev - > hard_header_len +
real_dev - > needed_tailroom + size ,
GFP_ATOMIC ) ;
if ( likely ( frag ) ) {
frag - > dev = real_dev ;
frag - > priority = skb - > priority ;
skb_reserve ( frag , real_dev - > hard_header_len ) ;
skb_reset_network_header ( frag ) ;
* mac_cb ( frag ) = * mac_cb ( skb ) ;
rc = dev_hard_header ( frag , real_dev , 0 , & master_hdr - > dest ,
& master_hdr - > source , size ) ;
if ( rc < 0 ) {
kfree_skb ( frag ) ;
2014-10-06 13:00:52 +04:00
return ERR_PTR ( rc ) ;
2014-05-14 19:43:09 +04:00
}
} else {
2014-08-19 21:03:29 +04:00
frag = ERR_PTR ( - ENOMEM ) ;
2014-05-14 19:43:09 +04:00
}
2011-11-10 11:38:38 +04:00
2014-05-14 19:43:09 +04:00
return frag ;
}
2011-11-10 11:38:38 +04:00
2014-05-14 19:43:09 +04:00
static int
lowpan_xmit_fragment ( struct sk_buff * skb , const struct ieee802154_hdr * wpan_hdr ,
u8 * frag_hdr , int frag_hdrlen ,
int offset , int len )
{
struct sk_buff * frag ;
2011-11-10 11:38:38 +04:00
2014-05-14 19:43:09 +04:00
raw_dump_inline ( __func__ , " fragment header " , frag_hdr , frag_hdrlen ) ;
2011-11-10 11:38:38 +04:00
2014-05-14 19:43:09 +04:00
frag = lowpan_alloc_frag ( skb , frag_hdrlen + len , wpan_hdr ) ;
if ( IS_ERR ( frag ) )
return - PTR_ERR ( frag ) ;
2013-10-30 12:18:24 +04:00
2014-05-14 19:43:09 +04:00
memcpy ( skb_put ( frag , frag_hdrlen ) , frag_hdr , frag_hdrlen ) ;
memcpy ( skb_put ( frag , len ) , skb_network_header ( skb ) + offset , len ) ;
2011-11-10 11:38:38 +04:00
2014-05-14 19:43:09 +04:00
raw_dump_table ( __func__ , " fragment dump " , frag - > data , frag - > len ) ;
2011-11-10 11:38:38 +04:00
2013-10-28 13:24:16 +04:00
return dev_queue_xmit ( frag ) ;
2011-11-10 11:38:38 +04:00
}
static int
2014-05-14 19:43:09 +04:00
lowpan_xmit_fragmented ( struct sk_buff * skb , struct net_device * dev ,
const struct ieee802154_hdr * wpan_hdr )
2011-11-10 11:38:38 +04:00
{
2014-05-14 19:43:09 +04:00
u16 dgram_size , dgram_offset ;
__be16 frag_tag ;
u8 frag_hdr [ 5 ] ;
int frag_cap , frag_len , payload_cap , rc ;
int skb_unprocessed , skb_offset ;
2014-02-28 10:32:45 +04:00
dgram_size = lowpan_uncompress_size ( skb , & dgram_offset ) -
2014-05-14 19:43:09 +04:00
skb - > mac_len ;
2014-10-06 13:00:50 +04:00
frag_tag = htons ( lowpan_dev_info ( dev ) - > fragment_tag ) ;
lowpan_dev_info ( dev ) - > fragment_tag + + ;
2011-11-10 11:38:38 +04:00
2014-05-14 19:43:09 +04:00
frag_hdr [ 0 ] = LOWPAN_DISPATCH_FRAG1 | ( ( dgram_size > > 8 ) & 0x07 ) ;
frag_hdr [ 1 ] = dgram_size & 0xff ;
memcpy ( frag_hdr + 2 , & frag_tag , sizeof ( frag_tag ) ) ;
2011-11-10 11:38:38 +04:00
2014-05-14 19:43:09 +04:00
payload_cap = ieee802154_max_payload ( wpan_hdr ) ;
2013-03-25 21:59:26 +04:00
2014-05-14 19:43:09 +04:00
frag_len = round_down ( payload_cap - LOWPAN_FRAG1_HEAD_SIZE -
skb_network_header_len ( skb ) , 8 ) ;
2011-11-10 11:38:38 +04:00
2014-05-14 19:43:09 +04:00
skb_offset = skb_network_header_len ( skb ) ;
skb_unprocessed = skb - > len - skb - > mac_len - skb_offset ;
2011-11-10 11:38:38 +04:00
2014-05-14 19:43:09 +04:00
rc = lowpan_xmit_fragment ( skb , wpan_hdr , frag_hdr ,
LOWPAN_FRAG1_HEAD_SIZE , 0 ,
frag_len + skb_network_header_len ( skb ) ) ;
if ( rc ) {
pr_debug ( " %s unable to send FRAG1 packet (tag: %d) " ,
2014-10-06 13:00:50 +04:00
__func__ , ntohs ( frag_tag ) ) ;
2014-05-14 19:43:09 +04:00
goto err ;
}
2014-02-28 10:32:45 +04:00
2014-05-14 19:43:09 +04:00
frag_hdr [ 0 ] & = ~ LOWPAN_DISPATCH_FRAG1 ;
frag_hdr [ 0 ] | = LOWPAN_DISPATCH_FRAGN ;
frag_cap = round_down ( payload_cap - LOWPAN_FRAGN_HEAD_SIZE , 8 ) ;
2011-11-10 11:38:38 +04:00
2014-06-02 15:21:57 +04:00
do {
2014-05-14 19:43:09 +04:00
dgram_offset + = frag_len ;
skb_offset + = frag_len ;
skb_unprocessed - = frag_len ;
frag_len = min ( frag_cap , skb_unprocessed ) ;
2011-11-10 11:38:38 +04:00
2014-05-14 19:43:09 +04:00
frag_hdr [ 4 ] = dgram_offset > > 3 ;
2011-11-10 11:38:38 +04:00
2014-05-14 19:43:09 +04:00
rc = lowpan_xmit_fragment ( skb , wpan_hdr , frag_hdr ,
LOWPAN_FRAGN_HEAD_SIZE , skb_offset ,
frag_len ) ;
if ( rc ) {
2014-02-28 10:32:48 +04:00
pr_debug ( " %s unable to send a FRAGN packet. (tag: %d, offset: %d) \n " ,
2014-10-06 13:00:50 +04:00
__func__ , ntohs ( frag_tag ) , skb_offset ) ;
2014-05-14 19:43:09 +04:00
goto err ;
2013-03-25 21:59:27 +04:00
}
2014-06-02 15:21:58 +04:00
} while ( skb_unprocessed > frag_cap ) ;
2011-11-10 11:38:38 +04:00
2014-05-14 19:43:09 +04:00
consume_skb ( skb ) ;
return NET_XMIT_SUCCESS ;
err :
kfree_skb ( skb ) ;
return rc ;
2011-11-10 11:38:38 +04:00
}
2014-09-24 14:21:33 +04:00
static int lowpan_header ( struct sk_buff * skb , struct net_device * dev )
{
struct ieee802154_addr sa , da ;
struct ieee802154_mac_cb * cb = mac_cb_init ( skb ) ;
struct lowpan_addr_info info ;
void * daddr , * saddr ;
memcpy ( & info , lowpan_skb_priv ( skb ) , sizeof ( info ) ) ;
/* TODO: Currently we only support extended_addr */
daddr = & info . daddr . u . extended_addr ;
saddr = & info . saddr . u . extended_addr ;
lowpan_header_compress ( skb , dev , ETH_P_IPV6 , daddr , saddr , skb - > len ) ;
cb - > type = IEEE802154_FC_TYPE_DATA ;
/* prepare wpan address data */
sa . mode = IEEE802154_ADDR_LONG ;
sa . pan_id = ieee802154_mlme_ops ( dev ) - > get_pan_id ( dev ) ;
sa . extended_addr = ieee802154_devaddr_from_raw ( saddr ) ;
/* intra-PAN communications */
da . pan_id = sa . pan_id ;
/* if the destination address is the broadcast address, use the
* corresponding short address
*/
if ( lowpan_is_addr_broadcast ( ( const u8 * ) daddr ) ) {
da . mode = IEEE802154_ADDR_SHORT ;
da . short_addr = cpu_to_le16 ( IEEE802154_ADDR_BROADCAST ) ;
cb - > ackreq = false ;
} else {
da . mode = IEEE802154_ADDR_LONG ;
da . extended_addr = ieee802154_devaddr_from_raw ( daddr ) ;
cb - > ackreq = true ;
}
return dev_hard_header ( skb , lowpan_dev_info ( dev ) - > real_dev ,
ETH_P_IPV6 , ( void * ) & da , ( void * ) & sa , 0 ) ;
}
2011-08-25 06:34:42 +04:00
static netdev_tx_t lowpan_xmit ( struct sk_buff * skb , struct net_device * dev )
{
2014-05-14 19:43:09 +04:00
struct ieee802154_hdr wpan_hdr ;
2014-09-24 14:21:33 +04:00
int max_single , ret ;
2011-08-25 06:34:42 +04:00
2012-06-25 07:49:03 +04:00
pr_debug ( " package xmit \n " ) ;
2011-08-25 06:34:42 +04:00
2014-09-24 14:21:33 +04:00
/* We must take a copy of the skb before we modify/replace the ipv6
* header as the header could be used elsewhere
*/
skb = skb_unshare ( skb , GFP_ATOMIC ) ;
if ( ! skb )
return NET_XMIT_DROP ;
ret = lowpan_header ( skb , dev ) ;
if ( ret < 0 ) {
kfree_skb ( skb ) ;
return NET_XMIT_DROP ;
}
2014-05-14 19:43:09 +04:00
if ( ieee802154_hdr_peek ( skb , & wpan_hdr ) < 0 ) {
kfree_skb ( skb ) ;
return NET_XMIT_DROP ;
2011-11-10 11:38:38 +04:00
}
2014-05-14 19:43:09 +04:00
max_single = ieee802154_max_payload ( & wpan_hdr ) ;
2011-11-10 11:38:38 +04:00
2014-05-14 19:43:09 +04:00
if ( skb_tail_pointer ( skb ) - skb_network_header ( skb ) < = max_single ) {
skb - > dev = lowpan_dev_info ( dev ) - > real_dev ;
return dev_queue_xmit ( skb ) ;
} else {
netdev_tx_t rc ;
2011-08-25 06:34:42 +04:00
2014-05-14 19:43:09 +04:00
pr_debug ( " frame is too big, fragmentation is needed \n " ) ;
rc = lowpan_xmit_fragmented ( skb , dev , & wpan_hdr ) ;
return rc < 0 ? NET_XMIT_DROP : rc ;
}
2011-08-25 06:34:42 +04:00
}
2014-03-15 00:23:59 +04:00
static __le16 lowpan_get_pan_id ( const struct net_device * dev )
2012-04-26 03:24:56 +04:00
{
struct net_device * real_dev = lowpan_dev_info ( dev ) - > real_dev ;
2014-07-02 07:31:09 +04:00
2012-04-26 03:24:56 +04:00
return ieee802154_mlme_ops ( real_dev ) - > get_pan_id ( real_dev ) ;
}
2014-03-15 00:23:59 +04:00
static __le16 lowpan_get_short_addr ( const struct net_device * dev )
2012-04-26 03:24:56 +04:00
{
struct net_device * real_dev = lowpan_dev_info ( dev ) - > real_dev ;
2014-07-02 07:31:09 +04:00
2012-04-26 03:24:56 +04:00
return ieee802154_mlme_ops ( real_dev ) - > get_short_addr ( real_dev ) ;
}
2013-03-25 21:59:30 +04:00
static u8 lowpan_get_dsn ( const struct net_device * dev )
{
struct net_device * real_dev = lowpan_dev_info ( dev ) - > real_dev ;
2014-07-02 07:31:09 +04:00
2013-03-25 21:59:30 +04:00
return ieee802154_mlme_ops ( real_dev ) - > get_dsn ( real_dev ) ;
}
2011-08-25 06:34:42 +04:00
static struct header_ops lowpan_header_ops = {
. create = lowpan_header_create ,
} ;
2014-02-10 23:42:35 +04:00
static struct lock_class_key lowpan_tx_busylock ;
static struct lock_class_key lowpan_netdev_xmit_lock_key ;
static void lowpan_set_lockdep_class_one ( struct net_device * dev ,
struct netdev_queue * txq ,
void * _unused )
{
lockdep_set_class ( & txq - > _xmit_lock ,
& lowpan_netdev_xmit_lock_key ) ;
}
static int lowpan_dev_init ( struct net_device * dev )
{
netdev_for_each_tx_queue ( dev , lowpan_set_lockdep_class_one , NULL ) ;
dev - > qdisc_tx_busylock = & lowpan_tx_busylock ;
return 0 ;
}
2011-08-25 06:34:42 +04:00
static const struct net_device_ops lowpan_netdev_ops = {
2014-02-10 23:42:35 +04:00
. ndo_init = lowpan_dev_init ,
2011-08-25 06:34:42 +04:00
. ndo_start_xmit = lowpan_xmit ,
} ;
2012-04-26 03:24:56 +04:00
static struct ieee802154_mlme_ops lowpan_mlme = {
. get_pan_id = lowpan_get_pan_id ,
. get_short_addr = lowpan_get_short_addr ,
2013-03-25 21:59:30 +04:00
. get_dsn = lowpan_get_dsn ,
2012-04-26 03:24:56 +04:00
} ;
2011-08-25 06:34:42 +04:00
static void lowpan_setup ( struct net_device * dev )
{
dev - > addr_len = IEEE802154_ADDR_LEN ;
memset ( dev - > broadcast , 0xff , IEEE802154_ADDR_LEN ) ;
dev - > type = ARPHRD_IEEE802154 ;
/* Frame Control + Sequence Number + Address fields + Security Header */
dev - > hard_header_len = 2 + 1 + 20 + 14 ;
dev - > needed_tailroom = 2 ; /* FCS */
2014-08-19 21:03:31 +04:00
dev - > mtu = IPV6_MIN_MTU ;
2011-08-25 06:34:42 +04:00
dev - > tx_queue_len = 0 ;
2011-11-10 11:39:37 +04:00
dev - > flags = IFF_BROADCAST | IFF_MULTICAST ;
2011-08-25 06:34:42 +04:00
dev - > watchdog_timeo = 0 ;
dev - > netdev_ops = & lowpan_netdev_ops ;
dev - > header_ops = & lowpan_header_ops ;
2012-04-26 03:24:56 +04:00
dev - > ml_priv = & lowpan_mlme ;
2012-09-01 09:57:07 +04:00
dev - > destructor = free_netdev ;
2011-08-25 06:34:42 +04: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 ;
}
static int lowpan_rcv ( struct sk_buff * skb , struct net_device * dev ,
2014-07-02 07:31:09 +04:00
struct packet_type * pt , struct net_device * orig_dev )
2011-08-25 06:34:42 +04:00
{
2014-03-15 00:24:02 +04:00
struct ieee802154_hdr hdr ;
2014-02-28 10:32:50 +04:00
int ret ;
2012-09-01 09:57:06 +04:00
2014-03-17 21:30:19 +04:00
skb = skb_share_check ( skb , GFP_ATOMIC ) ;
if ( ! skb )
goto drop ;
2011-08-25 06:34:42 +04:00
if ( ! netif_running ( dev ) )
2014-02-28 10:32:50 +04:00
goto drop_skb ;
2011-08-25 06:34:42 +04:00
2014-10-05 22:23:12 +04:00
if ( skb - > pkt_type = = PACKET_OTHERHOST )
goto drop_skb ;
2011-08-25 06:34:42 +04:00
if ( dev - > type ! = ARPHRD_IEEE802154 )
2014-02-28 10:32:50 +04:00
goto drop_skb ;
2014-03-15 00:24:02 +04:00
if ( ieee802154_hdr_peek_addrs ( skb , & hdr ) < 0 )
goto drop_skb ;
2011-08-25 06:34:42 +04:00
/* check that it's our buffer */
2013-01-16 23:09:48 +04:00
if ( skb - > data [ 0 ] = = LOWPAN_DISPATCH_IPV6 ) {
/* Pull off the 1-byte of 6lowpan header. */
2014-03-17 21:30:19 +04:00
skb_pull ( skb , 1 ) ;
2014-10-23 18:40:53 +04:00
return lowpan_give_skb_to_devices ( skb , NULL ) ;
2013-01-16 23:09:48 +04:00
} else {
switch ( skb - > data [ 0 ] & 0xe0 ) {
case LOWPAN_DISPATCH_IPHC : /* ipv6 datagram */
2014-10-23 18:40:56 +04:00
ret = iphc_decompress ( skb , & hdr ) ;
2014-10-23 18:40:54 +04:00
if ( ret < 0 )
2014-11-06 22:15:13 +03:00
goto drop_skb ;
2014-10-23 18:40:53 +04:00
return lowpan_give_skb_to_devices ( skb , NULL ) ;
2013-01-16 23:09:48 +04:00
case LOWPAN_DISPATCH_FRAG1 : /* first fragment header */
2014-03-17 21:30:19 +04:00
ret = lowpan_frag_rcv ( skb , LOWPAN_DISPATCH_FRAG1 ) ;
2014-02-28 10:32:50 +04:00
if ( ret = = 1 ) {
2014-10-23 18:40:56 +04:00
ret = iphc_decompress ( skb , & hdr ) ;
2014-10-23 18:40:54 +04:00
if ( ret < 0 )
2014-11-06 22:15:13 +03:00
goto drop_skb ;
2014-10-23 18:40:53 +04:00
return lowpan_give_skb_to_devices ( skb , NULL ) ;
} else if ( ret = = - 1 ) {
return NET_RX_DROP ;
} else {
return NET_RX_SUCCESS ;
2014-02-28 10:32:50 +04:00
}
2013-01-16 23:09:48 +04:00
case LOWPAN_DISPATCH_FRAGN : /* next fragments headers */
2014-03-17 21:30:19 +04:00
ret = lowpan_frag_rcv ( skb , LOWPAN_DISPATCH_FRAGN ) ;
2014-02-28 10:32:50 +04:00
if ( ret = = 1 ) {
2014-10-23 18:40:56 +04:00
ret = iphc_decompress ( skb , & hdr ) ;
2014-10-23 18:40:54 +04:00
if ( ret < 0 )
2014-11-06 22:15:13 +03:00
goto drop_skb ;
2014-10-23 18:40:53 +04:00
return lowpan_give_skb_to_devices ( skb , NULL ) ;
} else if ( ret = = - 1 ) {
return NET_RX_DROP ;
} else {
return NET_RX_SUCCESS ;
2014-02-28 10:32:50 +04:00
}
2013-01-16 23:09:48 +04:00
default :
break ;
}
2011-11-10 11:38:38 +04:00
}
2011-08-25 06:34:42 +04:00
2014-02-28 10:32:50 +04:00
drop_skb :
2011-08-25 06:34:42 +04:00
kfree_skb ( skb ) ;
2014-02-28 10:32:50 +04:00
drop :
2011-08-25 06:34:42 +04:00
return NET_RX_DROP ;
}
2014-10-13 12:33:06 +04:00
static struct packet_type lowpan_packet_type = {
. type = htons ( ETH_P_IEEE802154 ) ,
. func = lowpan_rcv ,
} ;
2011-08-25 06:34:42 +04:00
static int lowpan_newlink ( struct net * src_net , struct net_device * dev ,
struct nlattr * tb [ ] , struct nlattr * data [ ] )
{
struct net_device * real_dev ;
struct lowpan_dev_record * entry ;
2014-10-13 12:33:06 +04:00
int ret ;
2011-08-25 06:34:42 +04:00
2014-10-13 12:33:07 +04:00
ASSERT_RTNL ( ) ;
2012-06-25 07:49:03 +04:00
pr_debug ( " adding new link \n " ) ;
2011-08-25 06:34:42 +04:00
if ( ! tb [ IFLA_LINK ] )
return - EINVAL ;
/* find and hold real wpan device */
real_dev = dev_get_by_index ( src_net , nla_get_u32 ( tb [ IFLA_LINK ] ) ) ;
if ( ! real_dev )
return - ENODEV ;
2013-11-07 11:44:45 +04:00
if ( real_dev - > type ! = ARPHRD_IEEE802154 ) {
dev_put ( real_dev ) ;
2013-10-06 07:15:18 +04:00
return - EINVAL ;
2013-11-07 11:44:45 +04:00
}
2011-08-25 06:34:42 +04:00
lowpan_dev_info ( dev ) - > real_dev = real_dev ;
mutex_init ( & lowpan_dev_info ( dev ) - > dev_list_mtx ) ;
2014-02-28 10:32:48 +04:00
entry = kzalloc ( sizeof ( * entry ) , GFP_KERNEL ) ;
2011-08-30 07:51:09 +04:00
if ( ! entry ) {
dev_put ( real_dev ) ;
lowpan_dev_info ( dev ) - > real_dev = NULL ;
2011-08-25 06:34:42 +04:00
return - ENOMEM ;
2011-08-30 07:51:09 +04:00
}
2011-08-25 06:34:42 +04:00
entry - > ldev = dev ;
2014-11-25 07:34:57 +03:00
/* Set the lowpan hardware address to the wpan hardware address. */
2013-10-06 07:15:19 +04:00
memcpy ( dev - > dev_addr , real_dev - > dev_addr , IEEE802154_ADDR_LEN ) ;
2011-08-25 06:34:42 +04:00
mutex_lock ( & lowpan_dev_info ( dev ) - > dev_list_mtx ) ;
INIT_LIST_HEAD ( & entry - > list ) ;
list_add_tail ( & entry - > list , & lowpan_devices ) ;
mutex_unlock ( & lowpan_dev_info ( dev ) - > dev_list_mtx ) ;
2014-10-13 12:33:06 +04:00
ret = register_netdevice ( dev ) ;
if ( ret > = 0 ) {
if ( ! lowpan_open_count )
dev_add_pack ( & lowpan_packet_type ) ;
lowpan_open_count + + ;
}
2011-08-25 06:34:42 +04:00
2014-10-13 12:33:06 +04:00
return ret ;
2011-08-25 06:34:42 +04:00
}
static void lowpan_dellink ( struct net_device * dev , struct list_head * head )
{
struct lowpan_dev_info * lowpan_dev = lowpan_dev_info ( dev ) ;
struct net_device * real_dev = lowpan_dev - > real_dev ;
2012-04-26 03:24:57 +04:00
struct lowpan_dev_record * entry , * tmp ;
2011-08-25 06:34:42 +04:00
ASSERT_RTNL ( ) ;
2014-10-13 12:33:06 +04:00
lowpan_open_count - - ;
if ( ! lowpan_open_count )
dev_remove_pack ( & lowpan_packet_type ) ;
2011-08-25 06:34:42 +04:00
mutex_lock ( & lowpan_dev_info ( dev ) - > dev_list_mtx ) ;
2011-08-30 07:46:40 +04:00
list_for_each_entry_safe ( entry , tmp , & lowpan_devices , list ) {
2011-08-25 06:34:42 +04:00
if ( entry - > ldev = = dev ) {
list_del ( & entry - > list ) ;
kfree ( entry ) ;
}
2011-08-30 07:46:40 +04:00
}
2011-08-25 06:34:42 +04:00
mutex_unlock ( & lowpan_dev_info ( dev ) - > dev_list_mtx ) ;
mutex_destroy ( & lowpan_dev_info ( dev ) - > dev_list_mtx ) ;
unregister_netdevice_queue ( dev , head ) ;
dev_put ( real_dev ) ;
}
static struct rtnl_link_ops lowpan_link_ops __read_mostly = {
. kind = " lowpan " ,
. priv_size = sizeof ( struct lowpan_dev_info ) ,
. 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-07 00:54:38 +04:00
static inline void lowpan_netlink_fini ( void )
2011-08-25 06:34:42 +04:00
{
rtnl_link_unregister ( & lowpan_link_ops ) ;
}
2012-09-01 09:57:07 +04:00
static int lowpan_device_event ( struct notifier_block * unused ,
2013-05-28 05:30:21 +04:00
unsigned long event , void * ptr )
2012-09-01 09:57:07 +04:00
{
2013-05-28 05:30:21 +04:00
struct net_device * dev = netdev_notifier_info_to_dev ( ptr ) ;
2012-09-01 09:57:07 +04:00
LIST_HEAD ( del_list ) ;
struct lowpan_dev_record * entry , * tmp ;
if ( dev - > type ! = ARPHRD_IEEE802154 )
goto out ;
if ( event = = NETDEV_UNREGISTER ) {
list_for_each_entry_safe ( entry , tmp , & lowpan_devices , list ) {
if ( lowpan_dev_info ( entry - > ldev ) - > real_dev = = dev )
lowpan_dellink ( entry - > ldev , & del_list ) ;
}
unregister_netdevice_many ( & del_list ) ;
2012-09-18 11:10:43 +04:00
}
2012-09-01 09:57:07 +04:00
out :
return NOTIFY_DONE ;
}
static struct notifier_block lowpan_dev_notifier = {
. notifier_call = lowpan_device_event ,
} ;
2011-08-25 06:34:42 +04:00
static int __init lowpan_init_module ( void )
{
int err = 0 ;
2014-02-28 10:32:50 +04:00
err = lowpan_net_frag_init ( ) ;
2011-08-25 06:34:42 +04:00
if ( err < 0 )
goto out ;
2014-02-28 10:32:50 +04:00
err = lowpan_netlink_init ( ) ;
if ( err < 0 )
goto out_frag ;
2012-09-01 09:57:07 +04:00
err = register_netdevice_notifier ( & lowpan_dev_notifier ) ;
2014-02-28 10:32:50 +04:00
if ( err < 0 )
goto out_pack ;
return 0 ;
out_pack :
lowpan_netlink_fini ( ) ;
out_frag :
lowpan_net_frag_exit ( ) ;
2011-08-25 06:34:42 +04:00
out :
return err ;
}
static void __exit lowpan_cleanup_module ( void )
{
lowpan_netlink_fini ( ) ;
2014-02-28 10:32:50 +04:00
lowpan_net_frag_exit ( ) ;
2012-09-01 09:57:07 +04:00
2014-02-28 10:32:50 +04:00
unregister_netdevice_notifier ( & lowpan_dev_notifier ) ;
2011-08-25 06:34:42 +04:00
}
module_init ( lowpan_init_module ) ;
module_exit ( lowpan_cleanup_module ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS_RTNL_LINK ( " lowpan " ) ;