// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 6 LoWPAN IPv6 UDP compression according to RFC6282
*
* Authors :
* Alexander Aring < aar @ pengutronix . de >
*
* Original written by :
* Alexander Smirnov < alex . bluesman . smirnov @ gmail . com >
* Jon Smirl < jonsmirl @ gmail . com >
*/
#include "nhc.h"
#define LOWPAN_NHC_UDP_MASK 0 xF8
#define LOWPAN_NHC_UDP_ID 0 xF0
#define LOWPAN_NHC_UDP_4BIT_PORT 0 xF0B0
#define LOWPAN_NHC_UDP_4BIT_MASK 0 xFFF0
#define LOWPAN_NHC_UDP_8BIT_PORT 0 xF000
#define LOWPAN_NHC_UDP_8BIT_MASK 0 xFF00
/* values for port compression, _with checksum_ ie bit 5 set to 0 */
/* all inline */
#define LOWPAN_NHC_UDP_CS_P_00 0 xF0
/* source 16bit inline, dest = 0xF0 + 8 bit inline */
#define LOWPAN_NHC_UDP_CS_P_01 0 xF1
/* source = 0xF0 + 8bit inline, dest = 16 bit inline */
#define LOWPAN_NHC_UDP_CS_P_10 0 xF2
/* source & dest = 0xF0B + 4bit inline */
#define LOWPAN_NHC_UDP_CS_P_11 0 xF3
/* checksum elided */
#define LOWPAN_NHC_UDP_CS_C 0 x04
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 & 0 x0f));
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;
/* UDP length needs to be inferred from the lower layers
* here , we obtain the hint from the remaining size of the
* frame
*/
switch (lowpan_dev(skb->dev)->lltype) {
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 ;
}
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),
LOWPAN_NHC_UDP_ID, LOWPAN_NHC_UDP_MASK, udp_uncompress, udp_compress);
module_lowpan_nhc(nhc_udp);
MODULE_DESCRIPTION("6LoWPAN next header RFC6282 UDP compression" );
MODULE_LICENSE("GPL" );
Messung V0.5 in Prozent C=88 H=100 G=94
¤ Dauer der Verarbeitung: 0.0 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland