2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-01-09 18:42:58 +03:00
/*
* 6L oWPAN IPv6 UDP compression according to RFC6282
*
* Authors :
* Alexander Aring < aar @ pengutronix . de >
*
2021-03-24 09:22:24 +03:00
* Original written by :
2015-01-09 18:42:58 +03:00
* Alexander Smirnov < alex . bluesman . smirnov @ gmail . com >
* Jon Smirl < jonsmirl @ gmail . com >
*/
# include "nhc.h"
2015-10-20 09:31:22 +03:00
# define LOWPAN_NHC_UDP_MASK 0xF8
# define LOWPAN_NHC_UDP_ID 0xF0
# define LOWPAN_NHC_UDP_4BIT_PORT 0xF0B0
# define LOWPAN_NHC_UDP_4BIT_MASK 0xFFF0
# define LOWPAN_NHC_UDP_8BIT_PORT 0xF000
# define LOWPAN_NHC_UDP_8BIT_MASK 0xFF00
/* values for port compression, _with checksum_ ie bit 5 set to 0 */
/* all inline */
# define LOWPAN_NHC_UDP_CS_P_00 0xF0
/* source 16bit inline, dest = 0xF0 + 8 bit inline */
# define LOWPAN_NHC_UDP_CS_P_01 0xF1
/* source = 0xF0 + 8bit inline, dest = 16 bit inline */
# define LOWPAN_NHC_UDP_CS_P_10 0xF2
/* source & dest = 0xF0B + 4bit inline */
# define LOWPAN_NHC_UDP_CS_P_11 0xF3
/* checksum elided */
# define LOWPAN_NHC_UDP_CS_C 0x04
2015-01-09 18:42:58 +03:00
static int udp_uncompress ( struct sk_buff * skb , size_t needed )
{
u8 tmp = 0 , val = 0 ;
struct udphdr uh ;
bool fail ;
int err ;
fail = lowpan_fetch_skb ( skb , & tmp , sizeof ( tmp ) ) ;
pr_debug ( " UDP header uncompression \n " ) ;
switch ( tmp & LOWPAN_NHC_UDP_CS_P_11 ) {
case LOWPAN_NHC_UDP_CS_P_00 :
fail | = lowpan_fetch_skb ( skb , & uh . source , sizeof ( uh . source ) ) ;
fail | = lowpan_fetch_skb ( skb , & uh . dest , sizeof ( uh . dest ) ) ;
break ;
case LOWPAN_NHC_UDP_CS_P_01 :
fail | = lowpan_fetch_skb ( skb , & uh . source , sizeof ( uh . source ) ) ;
fail | = lowpan_fetch_skb ( skb , & val , sizeof ( val ) ) ;
uh . dest = htons ( val + LOWPAN_NHC_UDP_8BIT_PORT ) ;
break ;
case LOWPAN_NHC_UDP_CS_P_10 :
fail | = lowpan_fetch_skb ( skb , & val , sizeof ( val ) ) ;
uh . source = htons ( val + LOWPAN_NHC_UDP_8BIT_PORT ) ;
fail | = lowpan_fetch_skb ( skb , & uh . dest , sizeof ( uh . dest ) ) ;
break ;
case LOWPAN_NHC_UDP_CS_P_11 :
fail | = lowpan_fetch_skb ( skb , & val , sizeof ( val ) ) ;
uh . source = htons ( LOWPAN_NHC_UDP_4BIT_PORT + ( val > > 4 ) ) ;
uh . dest = htons ( LOWPAN_NHC_UDP_4BIT_PORT + ( val & 0x0f ) ) ;
break ;
default :
BUG ( ) ;
}
pr_debug ( " uncompressed UDP ports: src = %d, dst = %d \n " ,
ntohs ( uh . source ) , ntohs ( uh . dest ) ) ;
/* checksum */
if ( tmp & LOWPAN_NHC_UDP_CS_C ) {
pr_debug_ratelimited ( " checksum elided currently not supported \n " ) ;
fail = true ;
} else {
fail | = lowpan_fetch_skb ( skb , & uh . check , sizeof ( uh . check ) ) ;
}
if ( fail )
return - EINVAL ;
2021-03-24 09:22:24 +03:00
/* UDP length needs to be inferred from the lower layers
2015-01-09 18:42:58 +03:00
* here , we obtain the hint from the remaining size of the
* frame
*/
2016-04-11 12:04:18 +03:00
switch ( lowpan_dev ( skb - > dev ) - > lltype ) {
2015-09-02 15:21:25 +03:00
case LOWPAN_LLTYPE_IEEE802154 :
if ( lowpan_802154_cb ( skb ) - > d_size )
uh . len = htons ( lowpan_802154_cb ( skb ) - > d_size -
sizeof ( struct ipv6hdr ) ) ;
else
uh . len = htons ( skb - > len + sizeof ( struct udphdr ) ) ;
break ;
default :
uh . len = htons ( skb - > len + sizeof ( struct udphdr ) ) ;
break ;
}
2015-01-09 18:42:58 +03:00
pr_debug ( " uncompressed UDP length: src = %d " , ntohs ( uh . len ) ) ;
/* replace the compressed UDP head by the uncompressed UDP
* header
*/
err = skb_cow ( skb , needed ) ;
if ( unlikely ( err ) )
return err ;
skb_push ( skb , sizeof ( struct udphdr ) ) ;
skb_copy_to_linear_data ( skb , & uh , sizeof ( struct udphdr ) ) ;
return 0 ;
}
static int udp_compress ( struct sk_buff * skb , u8 * * hc_ptr )
{
const struct udphdr * uh = udp_hdr ( skb ) ;
u8 tmp ;
if ( ( ( ntohs ( uh - > source ) & LOWPAN_NHC_UDP_4BIT_MASK ) = =
LOWPAN_NHC_UDP_4BIT_PORT ) & &
( ( ntohs ( uh - > dest ) & LOWPAN_NHC_UDP_4BIT_MASK ) = =
LOWPAN_NHC_UDP_4BIT_PORT ) ) {
pr_debug ( " UDP header: both ports compression to 4 bits \n " ) ;
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_11 ;
lowpan_push_hc_data ( hc_ptr , & tmp , sizeof ( tmp ) ) ;
/* source and destination port */
tmp = ntohs ( uh - > dest ) - LOWPAN_NHC_UDP_4BIT_PORT +
( ( ntohs ( uh - > source ) - LOWPAN_NHC_UDP_4BIT_PORT ) < < 4 ) ;
lowpan_push_hc_data ( hc_ptr , & tmp , sizeof ( tmp ) ) ;
} else if ( ( ntohs ( uh - > dest ) & LOWPAN_NHC_UDP_8BIT_MASK ) = =
LOWPAN_NHC_UDP_8BIT_PORT ) {
pr_debug ( " UDP header: remove 8 bits of dest \n " ) ;
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_01 ;
lowpan_push_hc_data ( hc_ptr , & tmp , sizeof ( tmp ) ) ;
/* source port */
lowpan_push_hc_data ( hc_ptr , & uh - > source , sizeof ( uh - > source ) ) ;
/* destination port */
tmp = ntohs ( uh - > dest ) - LOWPAN_NHC_UDP_8BIT_PORT ;
lowpan_push_hc_data ( hc_ptr , & tmp , sizeof ( tmp ) ) ;
} else if ( ( ntohs ( uh - > source ) & LOWPAN_NHC_UDP_8BIT_MASK ) = =
LOWPAN_NHC_UDP_8BIT_PORT ) {
pr_debug ( " UDP header: remove 8 bits of source \n " ) ;
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_10 ;
lowpan_push_hc_data ( hc_ptr , & tmp , sizeof ( tmp ) ) ;
/* source port */
tmp = ntohs ( uh - > source ) - LOWPAN_NHC_UDP_8BIT_PORT ;
lowpan_push_hc_data ( hc_ptr , & tmp , sizeof ( tmp ) ) ;
/* destination port */
lowpan_push_hc_data ( hc_ptr , & uh - > dest , sizeof ( uh - > dest ) ) ;
} else {
pr_debug ( " UDP header: can't compress \n " ) ;
/* compression value */
tmp = LOWPAN_NHC_UDP_CS_P_00 ;
lowpan_push_hc_data ( hc_ptr , & tmp , sizeof ( tmp ) ) ;
/* source port */
lowpan_push_hc_data ( hc_ptr , & uh - > source , sizeof ( uh - > source ) ) ;
/* destination port */
lowpan_push_hc_data ( hc_ptr , & uh - > dest , sizeof ( uh - > dest ) ) ;
}
/* checksum is always inline */
lowpan_push_hc_data ( hc_ptr , & uh - > check , sizeof ( uh - > check ) ) ;
return 0 ;
}
LOWPAN_NHC ( nhc_udp , " RFC6282 UDP " , NEXTHDR_UDP , sizeof ( struct udphdr ) ,
2022-04-28 06:05:33 +03:00
LOWPAN_NHC_UDP_ID , LOWPAN_NHC_UDP_MASK , udp_uncompress , udp_compress ) ;
2015-01-09 18:42:58 +03:00
module_lowpan_nhc ( nhc_udp ) ;
MODULE_DESCRIPTION ( " 6LoWPAN next header RFC6282 UDP compression " ) ;
MODULE_LICENSE ( " GPL " ) ;