xref: /linux/net/6lowpan/nhc_udp.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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