xref: /openbmc/linux/net/6lowpan/nhc_udp.c (revision 8dda2eac)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *	6LoWPAN IPv6 UDP compression according to RFC6282
4  *
5  *	Authors:
6  *	Alexander Aring	<aar@pengutronix.de>
7  *
8  *	Original written by:
9  *	Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
10  *	Jon Smirl <jonsmirl@gmail.com>
11  */
12 
13 #include "nhc.h"
14 
15 #define LOWPAN_NHC_UDP_MASK		0xF8
16 #define LOWPAN_NHC_UDP_ID		0xF0
17 #define LOWPAN_NHC_UDP_IDLEN		1
18 
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
23 
24 /* values for port compression, _with checksum_ ie bit 5 set to 0 */
25 
26 /* all inline */
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
34 /* checksum elided */
35 #define LOWPAN_NHC_UDP_CS_C	0x04
36 
37 static int udp_uncompress(struct sk_buff *skb, size_t needed)
38 {
39 	u8 tmp = 0, val = 0;
40 	struct udphdr uh;
41 	bool fail;
42 	int err;
43 
44 	fail = lowpan_fetch_skb(skb, &tmp, sizeof(tmp));
45 
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));
51 		break;
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);
56 		break;
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));
61 		break;
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));
66 		break;
67 	default:
68 		BUG();
69 	}
70 
71 	pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
72 		 ntohs(uh.source), ntohs(uh.dest));
73 
74 	/* checksum */
75 	if (tmp & LOWPAN_NHC_UDP_CS_C) {
76 		pr_debug_ratelimited("checksum elided currently not supported\n");
77 		fail = true;
78 	} else {
79 		fail |= lowpan_fetch_skb(skb, &uh.check, sizeof(uh.check));
80 	}
81 
82 	if (fail)
83 		return -EINVAL;
84 
85 	/* UDP length needs to be inferred from the lower layers
86 	 * here, we obtain the hint from the remaining size of the
87 	 * frame
88 	 */
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));
94 		else
95 			uh.len = htons(skb->len + sizeof(struct udphdr));
96 		break;
97 	default:
98 		uh.len = htons(skb->len + sizeof(struct udphdr));
99 		break;
100 	}
101 	pr_debug("uncompressed UDP length: src = %d", ntohs(uh.len));
102 
103 	/* replace the compressed UDP head by the uncompressed UDP
104 	 * header
105 	 */
106 	err = skb_cow(skb, needed);
107 	if (unlikely(err))
108 		return err;
109 
110 	skb_push(skb, sizeof(struct udphdr));
111 	skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
112 
113 	return 0;
114 }
115 
116 static int udp_compress(struct sk_buff *skb, u8 **hc_ptr)
117 {
118 	const struct udphdr *uh = udp_hdr(skb);
119 	u8 tmp;
120 
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));
139 		/* source port */
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));
150 		/* source port */
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));
155 	} else {
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));
160 		/* source port */
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));
164 	}
165 
166 	/* checksum is always inline */
167 	lowpan_push_hc_data(hc_ptr, &uh->check, sizeof(uh->check));
168 
169 	return 0;
170 }
171 
172 static void udp_nhid_setup(struct lowpan_nhc *nhc)
173 {
174 	nhc->id[0] = LOWPAN_NHC_UDP_ID;
175 	nhc->idmask[0] = LOWPAN_NHC_UDP_MASK;
176 }
177 
178 LOWPAN_NHC(nhc_udp, "RFC6282 UDP", NEXTHDR_UDP, sizeof(struct udphdr),
179 	   udp_nhid_setup, LOWPAN_NHC_UDP_IDLEN, udp_uncompress, udp_compress);
180 
181 module_lowpan_nhc(nhc_udp);
182 MODULE_DESCRIPTION("6LoWPAN next header RFC6282 UDP compression");
183 MODULE_LICENSE("GPL");
184