2019-05-27 08:55:21 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2015-01-04 17:10:55 +01:00
# include <linux/if_arp.h>
# include <net/6lowpan.h>
2015-09-02 14:21:29 +02:00
# include <net/mac802154.h>
2015-01-04 17:10:55 +01:00
# include <net/ieee802154_netdev.h>
# include "6lowpan_i.h"
2015-09-02 14:21:26 +02:00
# define LOWPAN_DISPATCH_FIRST 0xc0
2015-09-02 14:21:25 +02:00
# define LOWPAN_DISPATCH_FRAG_MASK 0xf8
2015-09-02 14:21:26 +02:00
# define LOWPAN_DISPATCH_NALP 0x00
2015-09-03 14:54:19 +02:00
# define LOWPAN_DISPATCH_ESC 0x40
2015-09-02 14:21:27 +02:00
# define LOWPAN_DISPATCH_HC1 0x42
# define LOWPAN_DISPATCH_DFF 0x43
# define LOWPAN_DISPATCH_BC0 0x50
# define LOWPAN_DISPATCH_MESH 0x80
2015-09-02 14:21:26 +02:00
2015-09-02 14:21:25 +02:00
static int lowpan_give_skb_to_device ( struct sk_buff * skb )
2015-01-04 17:10:55 +01:00
{
skb - > protocol = htons ( ETH_P_IPV6 ) ;
2015-09-30 10:20:11 +02:00
skb - > dev - > stats . rx_packets + + ;
skb - > dev - > stats . rx_bytes + = skb - > len ;
2015-01-04 17:10:55 +01:00
2015-08-10 21:15:53 +02:00
return netif_rx ( skb ) ;
2015-01-04 17:10:55 +01:00
}
2015-09-02 14:21:25 +02:00
static int lowpan_rx_handlers_result ( struct sk_buff * skb , lowpan_rx_result res )
{
switch ( res ) {
case RX_CONTINUE :
/* nobody cared about this packet */
net_warn_ratelimited ( " %s: received unknown dispatch \n " ,
__func__ ) ;
2020-08-23 17:36:59 -05:00
fallthrough ;
2015-09-02 14:21:25 +02:00
case RX_DROP_UNUSABLE :
kfree_skb ( skb ) ;
2020-08-23 17:36:59 -05:00
fallthrough ;
2015-09-02 14:21:25 +02:00
case RX_DROP :
return NET_RX_DROP ;
case RX_QUEUED :
return lowpan_give_skb_to_device ( skb ) ;
default :
break ;
}
return NET_RX_DROP ;
}
static inline bool lowpan_is_frag1 ( u8 dispatch )
{
return ( dispatch & LOWPAN_DISPATCH_FRAG_MASK ) = = LOWPAN_DISPATCH_FRAG1 ;
}
static inline bool lowpan_is_fragn ( u8 dispatch )
{
return ( dispatch & LOWPAN_DISPATCH_FRAG_MASK ) = = LOWPAN_DISPATCH_FRAGN ;
}
static lowpan_rx_result lowpan_rx_h_frag ( struct sk_buff * skb )
{
int ret ;
if ( ! ( lowpan_is_frag1 ( * skb_network_header ( skb ) ) | |
lowpan_is_fragn ( * skb_network_header ( skb ) ) ) )
return RX_CONTINUE ;
ret = lowpan_frag_rcv ( skb , * skb_network_header ( skb ) &
LOWPAN_DISPATCH_FRAG_MASK ) ;
if ( ret = = 1 )
return RX_QUEUED ;
/* Packet is freed by lowpan_frag_rcv on error or put into the frag
* bucket .
*/
return RX_DROP ;
}
int lowpan_iphc_decompress ( struct sk_buff * skb )
2015-01-04 17:10:55 +01:00
{
2015-09-02 14:21:25 +02:00
struct ieee802154_hdr hdr ;
2015-01-04 17:10:55 +01:00
2015-09-02 14:21:25 +02:00
if ( ieee802154_hdr_peek_addrs ( skb , & hdr ) < 0 )
return - EINVAL ;
2015-10-13 13:42:58 +02:00
return lowpan_header_decompress ( skb , skb - > dev , & hdr . dest , & hdr . source ) ;
2015-01-04 17:10:55 +01:00
}
2015-09-02 14:21:25 +02:00
static lowpan_rx_result lowpan_rx_h_iphc ( struct sk_buff * skb )
{
int ret ;
if ( ! lowpan_is_iphc ( * skb_network_header ( skb ) ) )
return RX_CONTINUE ;
/* Setting datagram_offset to zero indicates non frag handling
* while doing lowpan_header_decompress .
*/
lowpan_802154_cb ( skb ) - > d_size = 0 ;
ret = lowpan_iphc_decompress ( skb ) ;
if ( ret < 0 )
return RX_DROP_UNUSABLE ;
return RX_QUEUED ;
}
lowpan_rx_result lowpan_rx_h_ipv6 ( struct sk_buff * skb )
{
if ( ! lowpan_is_ipv6 ( * skb_network_header ( skb ) ) )
return RX_CONTINUE ;
/* Pull off the 1-byte of 6lowpan header. */
skb_pull ( skb , 1 ) ;
return RX_QUEUED ;
}
2015-09-02 14:21:27 +02:00
static inline bool lowpan_is_esc ( u8 dispatch )
{
return dispatch = = LOWPAN_DISPATCH_ESC ;
}
static lowpan_rx_result lowpan_rx_h_esc ( struct sk_buff * skb )
{
if ( ! lowpan_is_esc ( * skb_network_header ( skb ) ) )
return RX_CONTINUE ;
net_warn_ratelimited ( " %s: %s \n " , skb - > dev - > name ,
" 6LoWPAN ESC not supported \n " ) ;
return RX_DROP_UNUSABLE ;
}
static inline bool lowpan_is_hc1 ( u8 dispatch )
{
return dispatch = = LOWPAN_DISPATCH_HC1 ;
}
static lowpan_rx_result lowpan_rx_h_hc1 ( struct sk_buff * skb )
{
if ( ! lowpan_is_hc1 ( * skb_network_header ( skb ) ) )
return RX_CONTINUE ;
net_warn_ratelimited ( " %s: %s \n " , skb - > dev - > name ,
" 6LoWPAN HC1 not supported \n " ) ;
return RX_DROP_UNUSABLE ;
}
static inline bool lowpan_is_dff ( u8 dispatch )
{
return dispatch = = LOWPAN_DISPATCH_DFF ;
}
static lowpan_rx_result lowpan_rx_h_dff ( struct sk_buff * skb )
{
if ( ! lowpan_is_dff ( * skb_network_header ( skb ) ) )
return RX_CONTINUE ;
net_warn_ratelimited ( " %s: %s \n " , skb - > dev - > name ,
" 6LoWPAN DFF not supported \n " ) ;
return RX_DROP_UNUSABLE ;
}
static inline bool lowpan_is_bc0 ( u8 dispatch )
{
return dispatch = = LOWPAN_DISPATCH_BC0 ;
}
static lowpan_rx_result lowpan_rx_h_bc0 ( struct sk_buff * skb )
{
if ( ! lowpan_is_bc0 ( * skb_network_header ( skb ) ) )
return RX_CONTINUE ;
net_warn_ratelimited ( " %s: %s \n " , skb - > dev - > name ,
" 6LoWPAN BC0 not supported \n " ) ;
return RX_DROP_UNUSABLE ;
}
static inline bool lowpan_is_mesh ( u8 dispatch )
{
return ( dispatch & LOWPAN_DISPATCH_FIRST ) = = LOWPAN_DISPATCH_MESH ;
}
static lowpan_rx_result lowpan_rx_h_mesh ( struct sk_buff * skb )
{
if ( ! lowpan_is_mesh ( * skb_network_header ( skb ) ) )
return RX_CONTINUE ;
net_warn_ratelimited ( " %s: %s \n " , skb - > dev - > name ,
" 6LoWPAN MESH not supported \n " ) ;
return RX_DROP_UNUSABLE ;
}
2015-09-02 14:21:25 +02:00
static int lowpan_invoke_rx_handlers ( struct sk_buff * skb )
{
lowpan_rx_result res ;
# define CALL_RXH(rxh) \
do { \
res = rxh ( skb ) ; \
if ( res ! = RX_CONTINUE ) \
goto rxh_next ; \
} while ( 0 )
/* likely at first */
CALL_RXH ( lowpan_rx_h_iphc ) ;
CALL_RXH ( lowpan_rx_h_frag ) ;
CALL_RXH ( lowpan_rx_h_ipv6 ) ;
2015-09-02 14:21:27 +02:00
CALL_RXH ( lowpan_rx_h_esc ) ;
CALL_RXH ( lowpan_rx_h_hc1 ) ;
CALL_RXH ( lowpan_rx_h_dff ) ;
CALL_RXH ( lowpan_rx_h_bc0 ) ;
CALL_RXH ( lowpan_rx_h_mesh ) ;
2015-09-02 14:21:25 +02:00
rxh_next :
return lowpan_rx_handlers_result ( skb , res ) ;
# undef CALL_RXH
}
2015-09-02 14:21:26 +02:00
static inline bool lowpan_is_nalp ( u8 dispatch )
{
return ( dispatch & LOWPAN_DISPATCH_FIRST ) = = LOWPAN_DISPATCH_NALP ;
}
2015-09-02 14:21:28 +02:00
/* Lookup for reserved dispatch values at:
* https : //www.iana.org/assignments/_6lowpan-parameters/_6lowpan-parameters.xhtml#_6lowpan-parameters-1
*
* Last Updated : 2015 - 01 - 22
*/
static inline bool lowpan_is_reserved ( u8 dispatch )
{
return ( ( dispatch > = 0x44 & & dispatch < = 0x4F ) | |
( dispatch > = 0x51 & & dispatch < = 0x5F ) | |
( dispatch > = 0xc8 & & dispatch < = 0xdf ) | |
2020-05-08 11:52:08 +08:00
dispatch > = 0xe8 ) ;
2015-09-02 14:21:28 +02:00
}
2015-09-02 14:21:26 +02:00
/* lowpan_rx_h_check checks on generic 6LoWPAN requirements
* in MAC and 6L oWPAN header .
*
* Don ' t manipulate the skb here , it could be shared buffer .
*/
static inline bool lowpan_rx_h_check ( struct sk_buff * skb )
{
2015-09-02 14:21:29 +02:00
__le16 fc = ieee802154_get_fc_from_skb ( skb ) ;
/* check on ieee802154 conform 6LoWPAN header */
if ( ! ieee802154_is_data ( fc ) | |
2016-07-06 23:32:26 +02:00
! ieee802154_skb_is_intra_pan_addressing ( fc , skb ) )
2015-09-02 14:21:29 +02:00
return false ;
2015-09-02 14:21:26 +02:00
/* check if we can dereference the dispatch */
if ( unlikely ( ! skb - > len ) )
return false ;
2015-09-02 14:21:28 +02:00
if ( lowpan_is_nalp ( * skb_network_header ( skb ) ) | |
lowpan_is_reserved ( * skb_network_header ( skb ) ) )
2015-09-02 14:21:26 +02:00
return false ;
return true ;
}
2015-09-02 14:21:16 +02:00
static int lowpan_rcv ( struct sk_buff * skb , struct net_device * wdev ,
struct packet_type * pt , struct net_device * orig_wdev )
2015-01-04 17:10:55 +01:00
{
2015-09-02 14:21:21 +02:00
struct net_device * ldev ;
2015-01-04 17:10:55 +01:00
2015-09-02 14:21:23 +02:00
if ( wdev - > type ! = ARPHRD_IEEE802154 | |
2015-09-02 14:21:26 +02:00
skb - > pkt_type = = PACKET_OTHERHOST | |
! lowpan_rx_h_check ( skb ) )
2015-10-22 12:11:27 +02:00
goto drop ;
2015-09-02 14:21:21 +02:00
ldev = wdev - > ieee802154_ptr - > lowpan_dev ;
if ( ! ldev | | ! netif_running ( ldev ) )
2015-10-22 12:11:27 +02:00
goto drop ;
2015-08-15 11:00:33 +02:00
2015-09-02 14:21:24 +02:00
/* Replacing skb->dev and followed rx handlers will manipulate skb. */
2015-01-04 17:10:55 +01:00
skb = skb_share_check ( skb , GFP_ATOMIC ) ;
if ( ! skb )
2015-10-22 12:11:27 +02:00
goto out ;
2015-09-02 14:21:24 +02:00
skb - > dev = ldev ;
2015-01-04 17:10:55 +01:00
2015-09-02 14:21:25 +02:00
/* When receive frag1 it's likely that we manipulate the buffer.
* When recevie iphc we manipulate the data buffer . So we need
* to unshare the buffer .
*/
if ( lowpan_is_frag1 ( * skb_network_header ( skb ) ) | |
lowpan_is_iphc ( * skb_network_header ( skb ) ) ) {
skb = skb_unshare ( skb , GFP_ATOMIC ) ;
if ( ! skb )
2015-10-22 12:11:27 +02:00
goto out ;
2015-01-04 17:10:55 +01:00
}
2015-09-02 14:21:25 +02:00
return lowpan_invoke_rx_handlers ( skb ) ;
2015-10-22 12:11:27 +02:00
drop :
kfree_skb ( skb ) ;
out :
return NET_RX_DROP ;
2015-01-04 17:10:55 +01:00
}
static struct packet_type lowpan_packet_type = {
. type = htons ( ETH_P_IEEE802154 ) ,
. func = lowpan_rcv ,
} ;
void lowpan_rx_init ( void )
{
dev_add_pack ( & lowpan_packet_type ) ;
}
void lowpan_rx_exit ( void )
{
dev_remove_pack ( & lowpan_packet_type ) ;
}