2012-05-16 00:50:21 +04:00
/*
* Copyright ( C ) 2007 - 2012 Siemens AG
*
* 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 .
*
* Written by :
* Pavel Smolenskiy < pavel . smolenskiy @ gmail . com >
* Maxim Gorbachyov < maxim . gorbachev @ siemens . com >
* Dmitry Eremin - Solenikov < dbaryshkov @ gmail . com >
* Alexander Smirnov < alex . bluesman . smirnov @ gmail . com >
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/netdevice.h>
# include <linux/crc-ccitt.h>
2014-10-29 23:34:36 +03:00
# include <asm/unaligned.h>
2012-05-16 00:50:21 +04:00
# include <net/mac802154.h>
# include <net/ieee802154_netdev.h>
2014-11-17 10:20:49 +03:00
# include <net/nl802154.h>
2012-05-16 00:50:21 +04:00
2014-10-25 11:41:00 +04:00
# include "ieee802154_i.h"
2012-05-16 00:50:21 +04:00
2014-10-29 23:34:35 +03:00
static int ieee802154_deliver_skb ( struct sk_buff * skb )
2014-10-27 19:13:32 +03:00
{
2014-10-27 19:13:36 +03:00
skb - > ip_summed = CHECKSUM_UNNECESSARY ;
2014-10-27 19:13:35 +03:00
skb - > protocol = htons ( ETH_P_IEEE802154 ) ;
2014-10-27 19:13:32 +03:00
return netif_receive_skb ( skb ) ;
}
static int
2014-10-27 19:13:43 +03:00
ieee802154_subif_frame ( struct ieee802154_sub_if_data * sdata ,
struct sk_buff * skb , const struct ieee802154_hdr * hdr )
2014-10-27 19:13:32 +03:00
{
2014-11-09 10:36:45 +03:00
struct wpan_dev * wpan_dev = & sdata - > wpan_dev ;
2014-10-27 19:13:32 +03:00
__le16 span , sshort ;
int rc ;
pr_debug ( " getting packet via slave interface %s \n " , sdata - > dev - > name ) ;
2014-11-09 10:36:45 +03:00
span = wpan_dev - > pan_id ;
sshort = wpan_dev - > short_addr ;
2014-10-27 19:13:32 +03:00
switch ( mac_cb ( skb ) - > dest . mode ) {
case IEEE802154_ADDR_NONE :
if ( mac_cb ( skb ) - > dest . mode ! = IEEE802154_ADDR_NONE )
/* FIXME: check if we are PAN coordinator */
skb - > pkt_type = PACKET_OTHERHOST ;
else
/* ACK comes with both addresses empty */
skb - > pkt_type = PACKET_HOST ;
break ;
case IEEE802154_ADDR_LONG :
if ( mac_cb ( skb ) - > dest . pan_id ! = span & &
mac_cb ( skb ) - > dest . pan_id ! = cpu_to_le16 ( IEEE802154_PANID_BROADCAST ) )
skb - > pkt_type = PACKET_OTHERHOST ;
2014-11-09 10:36:45 +03:00
else if ( mac_cb ( skb ) - > dest . extended_addr = = wpan_dev - > extended_addr )
2014-10-27 19:13:32 +03:00
skb - > pkt_type = PACKET_HOST ;
else
skb - > pkt_type = PACKET_OTHERHOST ;
break ;
case IEEE802154_ADDR_SHORT :
if ( mac_cb ( skb ) - > dest . pan_id ! = span & &
mac_cb ( skb ) - > dest . pan_id ! = cpu_to_le16 ( IEEE802154_PANID_BROADCAST ) )
skb - > pkt_type = PACKET_OTHERHOST ;
else if ( mac_cb ( skb ) - > dest . short_addr = = sshort )
skb - > pkt_type = PACKET_HOST ;
else if ( mac_cb ( skb ) - > dest . short_addr = =
cpu_to_le16 ( IEEE802154_ADDR_BROADCAST ) )
skb - > pkt_type = PACKET_BROADCAST ;
else
skb - > pkt_type = PACKET_OTHERHOST ;
break ;
default :
pr_debug ( " invalid dest mode \n " ) ;
2014-12-05 14:49:09 +03:00
goto fail ;
2014-10-27 19:13:32 +03:00
}
skb - > dev = sdata - > dev ;
2015-09-28 10:00:26 +03:00
/* TODO this should be moved after netif_receive_skb call, otherwise
* wireshark will show a mac header with security fields and the
* payload is already decrypted .
*/
2014-10-27 19:13:32 +03:00
rc = mac802154_llsec_decrypt ( & sdata - > sec , skb ) ;
if ( rc ) {
pr_debug ( " decryption failed: %i \n " , rc ) ;
goto fail ;
}
sdata - > dev - > stats . rx_packets + + ;
sdata - > dev - > stats . rx_bytes + = skb - > len ;
switch ( mac_cb ( skb ) - > type ) {
2016-07-25 18:46:40 +03:00
case IEEE802154_FC_TYPE_BEACON :
case IEEE802154_FC_TYPE_ACK :
case IEEE802154_FC_TYPE_MAC_CMD :
goto fail ;
2014-10-27 19:13:32 +03:00
case IEEE802154_FC_TYPE_DATA :
2014-10-29 23:34:35 +03:00
return ieee802154_deliver_skb ( skb ) ;
2014-10-27 19:13:32 +03:00
default :
2016-07-25 18:46:41 +03:00
pr_warn_ratelimited ( " ieee802154: bad frame received "
" (type = %d) \n " , mac_cb ( skb ) - > type ) ;
2014-10-27 19:13:32 +03:00
goto fail ;
}
fail :
kfree_skb ( skb ) ;
return NET_RX_DROP ;
}
2014-10-27 19:13:43 +03:00
static void
ieee802154_print_addr ( const char * name , const struct ieee802154_addr * addr )
2014-10-27 19:13:32 +03:00
{
if ( addr - > mode = = IEEE802154_ADDR_NONE )
pr_debug ( " %s not present \n " , name ) ;
pr_debug ( " %s PAN ID: %04x \n " , name , le16_to_cpu ( addr - > pan_id ) ) ;
if ( addr - > mode = = IEEE802154_ADDR_SHORT ) {
pr_debug ( " %s is short: %04x \n " , name ,
le16_to_cpu ( addr - > short_addr ) ) ;
} else {
u64 hw = swab64 ( ( __force u64 ) addr - > extended_addr ) ;
pr_debug ( " %s is hardware: %8phC \n " , name , & hw ) ;
}
}
2014-10-27 19:13:43 +03:00
static int
ieee802154_parse_frame_start ( struct sk_buff * skb , struct ieee802154_hdr * hdr )
2014-10-27 19:13:32 +03:00
{
int hlen ;
struct ieee802154_mac_cb * cb = mac_cb_init ( skb ) ;
2014-10-27 19:13:38 +03:00
skb_reset_mac_header ( skb ) ;
2014-10-27 19:13:32 +03:00
hlen = ieee802154_hdr_pull ( skb , hdr ) ;
if ( hlen < 0 )
return - EINVAL ;
skb - > mac_len = hlen ;
pr_debug ( " fc: %04x dsn: %02x \n " , le16_to_cpup ( ( __le16 * ) & hdr - > fc ) ,
hdr - > seq ) ;
cb - > type = hdr - > fc . type ;
cb - > ackreq = hdr - > fc . ack_request ;
cb - > secen = hdr - > fc . security_enabled ;
2014-10-27 19:13:43 +03:00
ieee802154_print_addr ( " destination " , & hdr - > dest ) ;
ieee802154_print_addr ( " source " , & hdr - > source ) ;
2014-10-27 19:13:32 +03:00
cb - > source = hdr - > source ;
cb - > dest = hdr - > dest ;
if ( hdr - > fc . security_enabled ) {
u64 key ;
pr_debug ( " seclevel %i \n " , hdr - > sec . level ) ;
switch ( hdr - > sec . key_id_mode ) {
case IEEE802154_SCF_KEY_IMPLICIT :
pr_debug ( " implicit key \n " ) ;
break ;
case IEEE802154_SCF_KEY_INDEX :
pr_debug ( " key %02x \n " , hdr - > sec . key_id ) ;
break ;
case IEEE802154_SCF_KEY_SHORT_INDEX :
pr_debug ( " key %04x:%04x %02x \n " ,
le32_to_cpu ( hdr - > sec . short_src ) > > 16 ,
le32_to_cpu ( hdr - > sec . short_src ) & 0xffff ,
hdr - > sec . key_id ) ;
break ;
case IEEE802154_SCF_KEY_HW_INDEX :
key = swab64 ( ( __force u64 ) hdr - > sec . extended_src ) ;
pr_debug ( " key source %8phC %02x \n " , & key ,
hdr - > sec . key_id ) ;
break ;
}
}
return 0 ;
}
static void
2014-10-27 19:13:43 +03:00
__ieee802154_rx_handle_packet ( struct ieee802154_local * local ,
struct sk_buff * skb )
2014-10-27 19:13:32 +03:00
{
int ret ;
struct ieee802154_sub_if_data * sdata ;
struct ieee802154_hdr hdr ;
2014-10-27 19:13:43 +03:00
ret = ieee802154_parse_frame_start ( skb , & hdr ) ;
2014-10-27 19:13:32 +03:00
if ( ret ) {
pr_debug ( " got invalid frame \n " ) ;
kfree_skb ( skb ) ;
return ;
}
list_for_each_entry_rcu ( sdata , & local - > interfaces , list ) {
2015-06-12 10:14:24 +03:00
if ( sdata - > wpan_dev . iftype ! = NL802154_IFTYPE_NODE )
continue ;
if ( ! ieee802154_sdata_running ( sdata ) )
2014-10-27 19:13:32 +03:00
continue ;
2014-10-27 19:13:43 +03:00
ieee802154_subif_frame ( sdata , skb , & hdr ) ;
2014-10-27 19:13:32 +03:00
skb = NULL ;
break ;
}
2015-11-14 22:22:41 +03:00
kfree_skb ( skb ) ;
2014-10-27 19:13:32 +03:00
}
2014-10-27 19:13:33 +03:00
static void
2014-10-27 19:13:43 +03:00
ieee802154_monitors_rx ( struct ieee802154_local * local , struct sk_buff * skb )
2014-10-27 19:13:32 +03:00
{
struct sk_buff * skb2 ;
struct ieee802154_sub_if_data * sdata ;
2014-10-27 19:13:38 +03:00
skb_reset_mac_header ( skb ) ;
2014-10-27 19:13:36 +03:00
skb - > ip_summed = CHECKSUM_UNNECESSARY ;
2014-10-27 19:13:37 +03:00
skb - > pkt_type = PACKET_OTHERHOST ;
2014-10-27 19:13:35 +03:00
skb - > protocol = htons ( ETH_P_IEEE802154 ) ;
2014-10-27 19:13:32 +03:00
list_for_each_entry_rcu ( sdata , & local - > interfaces , list ) {
2015-06-06 18:30:46 +03:00
if ( sdata - > wpan_dev . iftype ! = NL802154_IFTYPE_MONITOR )
2014-10-29 23:34:41 +03:00
continue ;
if ( ! ieee802154_sdata_running ( sdata ) )
2014-10-27 19:13:32 +03:00
continue ;
skb2 = skb_clone ( skb , GFP_ATOMIC ) ;
2014-10-29 23:34:42 +03:00
if ( skb2 ) {
skb2 - > dev = sdata - > dev ;
ieee802154_deliver_skb ( skb2 ) ;
2014-10-27 19:13:32 +03:00
2014-10-29 23:34:42 +03:00
sdata - > dev - > stats . rx_packets + + ;
sdata - > dev - > stats . rx_bytes + = skb - > len ;
}
2014-10-27 19:13:32 +03:00
}
}
2015-07-07 08:20:43 +03:00
void ieee802154_rx ( struct ieee802154_local * local , struct sk_buff * skb )
2012-05-16 00:50:21 +04:00
{
2014-10-29 23:34:37 +03:00
u16 crc ;
2012-05-16 00:50:21 +04:00
2014-10-27 19:13:34 +03:00
WARN_ON_ONCE ( softirq_count ( ) = = 0 ) ;
2015-06-24 12:36:36 +03:00
if ( local - > suspended )
goto drop ;
2014-10-29 23:34:36 +03:00
/* TODO: When a transceiver omits the checksum here, we
* add an own calculated one . This is currently an ugly
* solution because the monitor needs a crc here .
*/
if ( local - > hw . flags & IEEE802154_HW_RX_OMIT_CKSUM ) {
2014-10-29 23:34:37 +03:00
crc = crc_ccitt ( 0 , skb - > data , skb - > len ) ;
2014-10-29 23:34:36 +03:00
put_unaligned_le16 ( crc , skb_put ( skb , 2 ) ) ;
2012-05-16 00:50:21 +04:00
}
2014-10-27 19:13:39 +03:00
rcu_read_lock ( ) ;
2014-10-27 19:13:43 +03:00
ieee802154_monitors_rx ( local , skb ) ;
2014-06-11 14:03:07 +04:00
2014-10-29 23:34:37 +03:00
/* Check if transceiver doesn't validate the checksum.
* If not we validate the checksum here .
*/
if ( local - > hw . flags & IEEE802154_HW_RX_DROP_BAD_CKSUM ) {
crc = crc_ccitt ( 0 , skb - > data , skb - > len ) ;
if ( crc ) {
rcu_read_unlock ( ) ;
2015-06-24 12:36:36 +03:00
goto drop ;
2014-10-29 23:34:37 +03:00
}
}
2014-10-29 23:34:36 +03:00
/* remove crc */
skb_trim ( skb , skb - > len - 2 ) ;
2014-10-27 19:13:39 +03:00
2014-10-29 23:34:36 +03:00
__ieee802154_rx_handle_packet ( local , skb ) ;
2014-06-11 14:03:07 +04:00
2014-10-29 23:34:36 +03:00
rcu_read_unlock ( ) ;
2015-06-24 12:36:36 +03:00
return ;
drop :
kfree_skb ( skb ) ;
2012-05-16 00:50:21 +04:00
}
void
2014-10-25 19:16:34 +04:00
ieee802154_rx_irqsafe ( struct ieee802154_hw * hw , struct sk_buff * skb , u8 lqi )
2012-05-16 00:50:21 +04:00
{
2014-10-25 19:16:39 +04:00
struct ieee802154_local * local = hw_to_local ( hw ) ;
2012-05-16 00:50:21 +04:00
2014-10-27 19:13:30 +03:00
mac_cb ( skb ) - > lqi = lqi ;
skb - > pkt_type = IEEE802154_RX_MSG ;
skb_queue_tail ( & local - > skb_queue , skb ) ;
tasklet_schedule ( & local - > tasklet ) ;
2012-05-16 00:50:21 +04:00
}
EXPORT_SYMBOL ( ieee802154_rx_irqsafe ) ;