1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * 6LoWPAN IPv6 UDP compression according to RFC6282
6 * Alexander Aring <aar@pengutronix.de>
9 * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
10 * Jon Smirl <jonsmirl@gmail.com>
15 #define LOWPAN_NHC_UDP_MASK 0xF8
16 #define LOWPAN_NHC_UDP_ID 0xF0
17 #define LOWPAN_NHC_UDP_IDLEN 1
19 #define LOWPAN_NHC_UDP_4BIT_PORT 0xF0B0
20 #define LOWPAN_NHC_UDP_4BIT_MASK 0xFFF0
21 #define LOWPAN_NHC_UDP_8BIT_PORT 0xF000
22 #define LOWPAN_NHC_UDP_8BIT_MASK 0xFF00
24 /* values for port compression, _with checksum_ ie bit 5 set to 0 */
27 #define LOWPAN_NHC_UDP_CS_P_00 0xF0
28 /* source 16bit inline, dest = 0xF0 + 8 bit inline */
29 #define LOWPAN_NHC_UDP_CS_P_01 0xF1
30 /* source = 0xF0 + 8bit inline, dest = 16 bit inline */
31 #define LOWPAN_NHC_UDP_CS_P_10 0xF2
32 /* source & dest = 0xF0B + 4bit inline */
33 #define LOWPAN_NHC_UDP_CS_P_11 0xF3
35 #define LOWPAN_NHC_UDP_CS_C 0x04
37 static int udp_uncompress(struct sk_buff
*skb
, size_t needed
)
44 fail
= lowpan_fetch_skb(skb
, &tmp
, sizeof(tmp
));
46 pr_debug("UDP header uncompression\n");
47 switch (tmp
& LOWPAN_NHC_UDP_CS_P_11
) {
48 case LOWPAN_NHC_UDP_CS_P_00
:
49 fail
|= lowpan_fetch_skb(skb
, &uh
.source
, sizeof(uh
.source
));
50 fail
|= lowpan_fetch_skb(skb
, &uh
.dest
, sizeof(uh
.dest
));
52 case LOWPAN_NHC_UDP_CS_P_01
:
53 fail
|= lowpan_fetch_skb(skb
, &uh
.source
, sizeof(uh
.source
));
54 fail
|= lowpan_fetch_skb(skb
, &val
, sizeof(val
));
55 uh
.dest
= htons(val
+ LOWPAN_NHC_UDP_8BIT_PORT
);
57 case LOWPAN_NHC_UDP_CS_P_10
:
58 fail
|= lowpan_fetch_skb(skb
, &val
, sizeof(val
));
59 uh
.source
= htons(val
+ LOWPAN_NHC_UDP_8BIT_PORT
);
60 fail
|= lowpan_fetch_skb(skb
, &uh
.dest
, sizeof(uh
.dest
));
62 case LOWPAN_NHC_UDP_CS_P_11
:
63 fail
|= lowpan_fetch_skb(skb
, &val
, sizeof(val
));
64 uh
.source
= htons(LOWPAN_NHC_UDP_4BIT_PORT
+ (val
>> 4));
65 uh
.dest
= htons(LOWPAN_NHC_UDP_4BIT_PORT
+ (val
& 0x0f));
71 pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
72 ntohs(uh
.source
), ntohs(uh
.dest
));
75 if (tmp
& LOWPAN_NHC_UDP_CS_C
) {
76 pr_debug_ratelimited("checksum elided currently not supported\n");
79 fail
|= lowpan_fetch_skb(skb
, &uh
.check
, sizeof(uh
.check
));
85 /* UDP length needs to be infered from the lower layers
86 * here, we obtain the hint from the remaining size of the
89 switch (lowpan_dev(skb
->dev
)->lltype
) {
90 case LOWPAN_LLTYPE_IEEE802154
:
91 if (lowpan_802154_cb(skb
)->d_size
)
92 uh
.len
= htons(lowpan_802154_cb(skb
)->d_size
-
93 sizeof(struct ipv6hdr
));
95 uh
.len
= htons(skb
->len
+ sizeof(struct udphdr
));
98 uh
.len
= htons(skb
->len
+ sizeof(struct udphdr
));
101 pr_debug("uncompressed UDP length: src = %d", ntohs(uh
.len
));
103 /* replace the compressed UDP head by the uncompressed UDP
106 err
= skb_cow(skb
, needed
);
110 skb_push(skb
, sizeof(struct udphdr
));
111 skb_copy_to_linear_data(skb
, &uh
, sizeof(struct udphdr
));
116 static int udp_compress(struct sk_buff
*skb
, u8
**hc_ptr
)
118 const struct udphdr
*uh
= udp_hdr(skb
);
121 if (((ntohs(uh
->source
) & LOWPAN_NHC_UDP_4BIT_MASK
) ==
122 LOWPAN_NHC_UDP_4BIT_PORT
) &&
123 ((ntohs(uh
->dest
) & LOWPAN_NHC_UDP_4BIT_MASK
) ==
124 LOWPAN_NHC_UDP_4BIT_PORT
)) {
125 pr_debug("UDP header: both ports compression to 4 bits\n");
126 /* compression value */
127 tmp
= LOWPAN_NHC_UDP_CS_P_11
;
128 lowpan_push_hc_data(hc_ptr
, &tmp
, sizeof(tmp
));
129 /* source and destination port */
130 tmp
= ntohs(uh
->dest
) - LOWPAN_NHC_UDP_4BIT_PORT
+
131 ((ntohs(uh
->source
) - LOWPAN_NHC_UDP_4BIT_PORT
) << 4);
132 lowpan_push_hc_data(hc_ptr
, &tmp
, sizeof(tmp
));
133 } else if ((ntohs(uh
->dest
) & LOWPAN_NHC_UDP_8BIT_MASK
) ==
134 LOWPAN_NHC_UDP_8BIT_PORT
) {
135 pr_debug("UDP header: remove 8 bits of dest\n");
136 /* compression value */
137 tmp
= LOWPAN_NHC_UDP_CS_P_01
;
138 lowpan_push_hc_data(hc_ptr
, &tmp
, sizeof(tmp
));
140 lowpan_push_hc_data(hc_ptr
, &uh
->source
, sizeof(uh
->source
));
141 /* destination port */
142 tmp
= ntohs(uh
->dest
) - LOWPAN_NHC_UDP_8BIT_PORT
;
143 lowpan_push_hc_data(hc_ptr
, &tmp
, sizeof(tmp
));
144 } else if ((ntohs(uh
->source
) & LOWPAN_NHC_UDP_8BIT_MASK
) ==
145 LOWPAN_NHC_UDP_8BIT_PORT
) {
146 pr_debug("UDP header: remove 8 bits of source\n");
147 /* compression value */
148 tmp
= LOWPAN_NHC_UDP_CS_P_10
;
149 lowpan_push_hc_data(hc_ptr
, &tmp
, sizeof(tmp
));
151 tmp
= ntohs(uh
->source
) - LOWPAN_NHC_UDP_8BIT_PORT
;
152 lowpan_push_hc_data(hc_ptr
, &tmp
, sizeof(tmp
));
153 /* destination port */
154 lowpan_push_hc_data(hc_ptr
, &uh
->dest
, sizeof(uh
->dest
));
156 pr_debug("UDP header: can't compress\n");
157 /* compression value */
158 tmp
= LOWPAN_NHC_UDP_CS_P_00
;
159 lowpan_push_hc_data(hc_ptr
, &tmp
, sizeof(tmp
));
161 lowpan_push_hc_data(hc_ptr
, &uh
->source
, sizeof(uh
->source
));
162 /* destination port */
163 lowpan_push_hc_data(hc_ptr
, &uh
->dest
, sizeof(uh
->dest
));
166 /* checksum is always inline */
167 lowpan_push_hc_data(hc_ptr
, &uh
->check
, sizeof(uh
->check
));
172 static void udp_nhid_setup(struct lowpan_nhc
*nhc
)
174 nhc
->id
[0] = LOWPAN_NHC_UDP_ID
;
175 nhc
->idmask
[0] = LOWPAN_NHC_UDP_MASK
;
178 LOWPAN_NHC(nhc_udp
, "RFC6282 UDP", NEXTHDR_UDP
, sizeof(struct udphdr
),
179 udp_nhid_setup
, LOWPAN_NHC_UDP_IDLEN
, udp_uncompress
, udp_compress
);
181 module_lowpan_nhc(nhc_udp
);
182 MODULE_DESCRIPTION("6LoWPAN next header RFC6282 UDP compression");
183 MODULE_LICENSE("GPL");