12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2cc6ed268SAlexander Aring /*
3cc6ed268SAlexander Aring * 6LoWPAN IPv6 UDP compression according to RFC6282
4cc6ed268SAlexander Aring *
5cc6ed268SAlexander Aring * Authors:
6cc6ed268SAlexander Aring * Alexander Aring <aar@pengutronix.de>
7cc6ed268SAlexander Aring *
8da1da87fSWang Hai * Original written by:
9cc6ed268SAlexander Aring * Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
10cc6ed268SAlexander Aring * Jon Smirl <jonsmirl@gmail.com>
11cc6ed268SAlexander Aring */
12cc6ed268SAlexander Aring
13cc6ed268SAlexander Aring #include "nhc.h"
14cc6ed268SAlexander Aring
156350047eSAlexander Aring #define LOWPAN_NHC_UDP_MASK 0xF8
166350047eSAlexander Aring #define LOWPAN_NHC_UDP_ID 0xF0
17cc6ed268SAlexander Aring
186350047eSAlexander Aring #define LOWPAN_NHC_UDP_4BIT_PORT 0xF0B0
196350047eSAlexander Aring #define LOWPAN_NHC_UDP_4BIT_MASK 0xFFF0
206350047eSAlexander Aring #define LOWPAN_NHC_UDP_8BIT_PORT 0xF000
216350047eSAlexander Aring #define LOWPAN_NHC_UDP_8BIT_MASK 0xFF00
226350047eSAlexander Aring
236350047eSAlexander Aring /* values for port compression, _with checksum_ ie bit 5 set to 0 */
246350047eSAlexander Aring
256350047eSAlexander Aring /* all inline */
266350047eSAlexander Aring #define LOWPAN_NHC_UDP_CS_P_00 0xF0
276350047eSAlexander Aring /* source 16bit inline, dest = 0xF0 + 8 bit inline */
286350047eSAlexander Aring #define LOWPAN_NHC_UDP_CS_P_01 0xF1
296350047eSAlexander Aring /* source = 0xF0 + 8bit inline, dest = 16 bit inline */
306350047eSAlexander Aring #define LOWPAN_NHC_UDP_CS_P_10 0xF2
316350047eSAlexander Aring /* source & dest = 0xF0B + 4bit inline */
326350047eSAlexander Aring #define LOWPAN_NHC_UDP_CS_P_11 0xF3
336350047eSAlexander Aring /* checksum elided */
346350047eSAlexander Aring #define LOWPAN_NHC_UDP_CS_C 0x04
356350047eSAlexander Aring
udp_uncompress(struct sk_buff * skb,size_t needed)36cc6ed268SAlexander Aring static int udp_uncompress(struct sk_buff *skb, size_t needed)
37cc6ed268SAlexander Aring {
38cc6ed268SAlexander Aring u8 tmp = 0, val = 0;
39cc6ed268SAlexander Aring struct udphdr uh;
40cc6ed268SAlexander Aring bool fail;
41cc6ed268SAlexander Aring int err;
42cc6ed268SAlexander Aring
43cc6ed268SAlexander Aring fail = lowpan_fetch_skb(skb, &tmp, sizeof(tmp));
44cc6ed268SAlexander Aring
45cc6ed268SAlexander Aring pr_debug("UDP header uncompression\n");
46cc6ed268SAlexander Aring switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
47cc6ed268SAlexander Aring case LOWPAN_NHC_UDP_CS_P_00:
48cc6ed268SAlexander Aring fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source));
49cc6ed268SAlexander Aring fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest));
50cc6ed268SAlexander Aring break;
51cc6ed268SAlexander Aring case LOWPAN_NHC_UDP_CS_P_01:
52cc6ed268SAlexander Aring fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source));
53cc6ed268SAlexander Aring fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
54cc6ed268SAlexander Aring uh.dest = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
55cc6ed268SAlexander Aring break;
56cc6ed268SAlexander Aring case LOWPAN_NHC_UDP_CS_P_10:
57cc6ed268SAlexander Aring fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
58cc6ed268SAlexander Aring uh.source = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
59cc6ed268SAlexander Aring fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest));
60cc6ed268SAlexander Aring break;
61cc6ed268SAlexander Aring case LOWPAN_NHC_UDP_CS_P_11:
62cc6ed268SAlexander Aring fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
63cc6ed268SAlexander Aring uh.source = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val >> 4));
64cc6ed268SAlexander Aring uh.dest = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val & 0x0f));
65cc6ed268SAlexander Aring break;
66cc6ed268SAlexander Aring default:
67cc6ed268SAlexander Aring BUG();
68cc6ed268SAlexander Aring }
69cc6ed268SAlexander Aring
70cc6ed268SAlexander Aring pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
71cc6ed268SAlexander Aring ntohs(uh.source), ntohs(uh.dest));
72cc6ed268SAlexander Aring
73cc6ed268SAlexander Aring /* checksum */
74cc6ed268SAlexander Aring if (tmp & LOWPAN_NHC_UDP_CS_C) {
75cc6ed268SAlexander Aring pr_debug_ratelimited("checksum elided currently not supported\n");
76cc6ed268SAlexander Aring fail = true;
77cc6ed268SAlexander Aring } else {
78cc6ed268SAlexander Aring fail |= lowpan_fetch_skb(skb, &uh.check, sizeof(uh.check));
79cc6ed268SAlexander Aring }
80cc6ed268SAlexander Aring
81cc6ed268SAlexander Aring if (fail)
82cc6ed268SAlexander Aring return -EINVAL;
83cc6ed268SAlexander Aring
84da1da87fSWang Hai /* UDP length needs to be inferred from the lower layers
85cc6ed268SAlexander Aring * here, we obtain the hint from the remaining size of the
86cc6ed268SAlexander Aring * frame
87cc6ed268SAlexander Aring */
882e4d60cbSAlexander Aring switch (lowpan_dev(skb->dev)->lltype) {
8972a5e6bbSAlexander Aring case LOWPAN_LLTYPE_IEEE802154:
9072a5e6bbSAlexander Aring if (lowpan_802154_cb(skb)->d_size)
9172a5e6bbSAlexander Aring uh.len = htons(lowpan_802154_cb(skb)->d_size -
9272a5e6bbSAlexander Aring sizeof(struct ipv6hdr));
9372a5e6bbSAlexander Aring else
94cc6ed268SAlexander Aring uh.len = htons(skb->len + sizeof(struct udphdr));
9572a5e6bbSAlexander Aring break;
9672a5e6bbSAlexander Aring default:
9772a5e6bbSAlexander Aring uh.len = htons(skb->len + sizeof(struct udphdr));
9872a5e6bbSAlexander Aring break;
9972a5e6bbSAlexander Aring }
100cc6ed268SAlexander Aring pr_debug("uncompressed UDP length: src = %d", ntohs(uh.len));
101cc6ed268SAlexander Aring
102cc6ed268SAlexander Aring /* replace the compressed UDP head by the uncompressed UDP
103cc6ed268SAlexander Aring * header
104cc6ed268SAlexander Aring */
105cc6ed268SAlexander Aring err = skb_cow(skb, needed);
106cc6ed268SAlexander Aring if (unlikely(err))
107cc6ed268SAlexander Aring return err;
108cc6ed268SAlexander Aring
109cc6ed268SAlexander Aring skb_push(skb, sizeof(struct udphdr));
110cc6ed268SAlexander Aring skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
111cc6ed268SAlexander Aring
112cc6ed268SAlexander Aring return 0;
113cc6ed268SAlexander Aring }
114cc6ed268SAlexander Aring
udp_compress(struct sk_buff * skb,u8 ** hc_ptr)115cc6ed268SAlexander Aring static int udp_compress(struct sk_buff *skb, u8 **hc_ptr)
116cc6ed268SAlexander Aring {
117cc6ed268SAlexander Aring const struct udphdr *uh = udp_hdr(skb);
118cc6ed268SAlexander Aring u8 tmp;
119cc6ed268SAlexander Aring
120cc6ed268SAlexander Aring if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) ==
121cc6ed268SAlexander Aring LOWPAN_NHC_UDP_4BIT_PORT) &&
122cc6ed268SAlexander Aring ((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) ==
123cc6ed268SAlexander Aring LOWPAN_NHC_UDP_4BIT_PORT)) {
124cc6ed268SAlexander Aring pr_debug("UDP header: both ports compression to 4 bits\n");
125cc6ed268SAlexander Aring /* compression value */
126cc6ed268SAlexander Aring tmp = LOWPAN_NHC_UDP_CS_P_11;
127cc6ed268SAlexander Aring lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
128cc6ed268SAlexander Aring /* source and destination port */
129cc6ed268SAlexander Aring tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_4BIT_PORT +
130cc6ed268SAlexander Aring ((ntohs(uh->source) - LOWPAN_NHC_UDP_4BIT_PORT) << 4);
131cc6ed268SAlexander Aring lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
132cc6ed268SAlexander Aring } else if ((ntohs(uh->dest) & LOWPAN_NHC_UDP_8BIT_MASK) ==
133cc6ed268SAlexander Aring LOWPAN_NHC_UDP_8BIT_PORT) {
134cc6ed268SAlexander Aring pr_debug("UDP header: remove 8 bits of dest\n");
135cc6ed268SAlexander Aring /* compression value */
136cc6ed268SAlexander Aring tmp = LOWPAN_NHC_UDP_CS_P_01;
137cc6ed268SAlexander Aring lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
138cc6ed268SAlexander Aring /* source port */
139cc6ed268SAlexander Aring lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
140cc6ed268SAlexander Aring /* destination port */
141cc6ed268SAlexander Aring tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_8BIT_PORT;
142cc6ed268SAlexander Aring lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
143cc6ed268SAlexander Aring } else if ((ntohs(uh->source) & LOWPAN_NHC_UDP_8BIT_MASK) ==
144cc6ed268SAlexander Aring LOWPAN_NHC_UDP_8BIT_PORT) {
145cc6ed268SAlexander Aring pr_debug("UDP header: remove 8 bits of source\n");
146cc6ed268SAlexander Aring /* compression value */
147cc6ed268SAlexander Aring tmp = LOWPAN_NHC_UDP_CS_P_10;
148cc6ed268SAlexander Aring lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
149cc6ed268SAlexander Aring /* source port */
150cc6ed268SAlexander Aring tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT;
151cc6ed268SAlexander Aring lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
152cc6ed268SAlexander Aring /* destination port */
153cc6ed268SAlexander Aring lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
154cc6ed268SAlexander Aring } else {
155cc6ed268SAlexander Aring pr_debug("UDP header: can't compress\n");
156cc6ed268SAlexander Aring /* compression value */
157cc6ed268SAlexander Aring tmp = LOWPAN_NHC_UDP_CS_P_00;
158cc6ed268SAlexander Aring lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
159cc6ed268SAlexander Aring /* source port */
160cc6ed268SAlexander Aring lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
161cc6ed268SAlexander Aring /* destination port */
162cc6ed268SAlexander Aring lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
163cc6ed268SAlexander Aring }
164cc6ed268SAlexander Aring
165cc6ed268SAlexander Aring /* checksum is always inline */
166cc6ed268SAlexander Aring lowpan_push_hc_data(hc_ptr, &uh->check, sizeof(uh->check));
167cc6ed268SAlexander Aring
168cc6ed268SAlexander Aring return 0;
169cc6ed268SAlexander Aring }
170cc6ed268SAlexander Aring
171cc6ed268SAlexander Aring LOWPAN_NHC(nhc_udp, "RFC6282 UDP", NEXTHDR_UDP, sizeof(struct udphdr),
172*31264f95SAlexander Aring LOWPAN_NHC_UDP_ID, LOWPAN_NHC_UDP_MASK, udp_uncompress, udp_compress);
173cc6ed268SAlexander Aring
174cc6ed268SAlexander Aring module_lowpan_nhc(nhc_udp);
175cc6ed268SAlexander Aring MODULE_DESCRIPTION("6LoWPAN next header RFC6282 UDP compression");
176cc6ed268SAlexander Aring MODULE_LICENSE("GPL");
177