xref: /linux/net/6lowpan/nhc_udp.c (revision bd628c1bed7902ec1f24ba0fe70758949146abbe)
1 /*
2  *	6LoWPAN IPv6 UDP compression according to RFC6282
3  *
4  *
5  *	Authors:
6  *	Alexander Aring	<aar@pengutronix.de>
7  *
8  *	Orignal written by:
9  *	Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
10  *	Jon Smirl <jonsmirl@gmail.com>
11  *
12  *	This program is free software; you can redistribute it and/or
13  *	modify it under the terms of the GNU General Public License
14  *	as published by the Free Software Foundation; either version
15  *	2 of the License, or (at your option) any later version.
16  */
17 
18 #include "nhc.h"
19 
20 #define LOWPAN_NHC_UDP_MASK		0xF8
21 #define LOWPAN_NHC_UDP_ID		0xF0
22 #define LOWPAN_NHC_UDP_IDLEN		1
23 
24 #define LOWPAN_NHC_UDP_4BIT_PORT	0xF0B0
25 #define LOWPAN_NHC_UDP_4BIT_MASK	0xFFF0
26 #define LOWPAN_NHC_UDP_8BIT_PORT	0xF000
27 #define LOWPAN_NHC_UDP_8BIT_MASK	0xFF00
28 
29 /* values for port compression, _with checksum_ ie bit 5 set to 0 */
30 
31 /* all inline */
32 #define LOWPAN_NHC_UDP_CS_P_00	0xF0
33 /* source 16bit inline, dest = 0xF0 + 8 bit inline */
34 #define LOWPAN_NHC_UDP_CS_P_01	0xF1
35 /* source = 0xF0 + 8bit inline, dest = 16 bit inline */
36 #define LOWPAN_NHC_UDP_CS_P_10	0xF2
37 /* source & dest = 0xF0B + 4bit inline */
38 #define LOWPAN_NHC_UDP_CS_P_11	0xF3
39 /* checksum elided */
40 #define LOWPAN_NHC_UDP_CS_C	0x04
41 
42 static int udp_uncompress(struct sk_buff *skb, size_t needed)
43 {
44 	u8 tmp = 0, val = 0;
45 	struct udphdr uh;
46 	bool fail;
47 	int err;
48 
49 	fail = lowpan_fetch_skb(skb, &tmp, sizeof(tmp));
50 
51 	pr_debug("UDP header uncompression\n");
52 	switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
53 	case LOWPAN_NHC_UDP_CS_P_00:
54 		fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source));
55 		fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest));
56 		break;
57 	case LOWPAN_NHC_UDP_CS_P_01:
58 		fail |= lowpan_fetch_skb(skb, &uh.source, sizeof(uh.source));
59 		fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
60 		uh.dest = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
61 		break;
62 	case LOWPAN_NHC_UDP_CS_P_10:
63 		fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
64 		uh.source = htons(val + LOWPAN_NHC_UDP_8BIT_PORT);
65 		fail |= lowpan_fetch_skb(skb, &uh.dest, sizeof(uh.dest));
66 		break;
67 	case LOWPAN_NHC_UDP_CS_P_11:
68 		fail |= lowpan_fetch_skb(skb, &val, sizeof(val));
69 		uh.source = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val >> 4));
70 		uh.dest = htons(LOWPAN_NHC_UDP_4BIT_PORT + (val & 0x0f));
71 		break;
72 	default:
73 		BUG();
74 	}
75 
76 	pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
77 		 ntohs(uh.source), ntohs(uh.dest));
78 
79 	/* checksum */
80 	if (tmp & LOWPAN_NHC_UDP_CS_C) {
81 		pr_debug_ratelimited("checksum elided currently not supported\n");
82 		fail = true;
83 	} else {
84 		fail |= lowpan_fetch_skb(skb, &uh.check, sizeof(uh.check));
85 	}
86 
87 	if (fail)
88 		return -EINVAL;
89 
90 	/* UDP length needs to be infered from the lower layers
91 	 * here, we obtain the hint from the remaining size of the
92 	 * frame
93 	 */
94 	switch (lowpan_dev(skb->dev)->lltype) {
95 	case LOWPAN_LLTYPE_IEEE802154:
96 		if (lowpan_802154_cb(skb)->d_size)
97 			uh.len = htons(lowpan_802154_cb(skb)->d_size -
98 				       sizeof(struct ipv6hdr));
99 		else
100 			uh.len = htons(skb->len + sizeof(struct udphdr));
101 		break;
102 	default:
103 		uh.len = htons(skb->len + sizeof(struct udphdr));
104 		break;
105 	}
106 	pr_debug("uncompressed UDP length: src = %d", ntohs(uh.len));
107 
108 	/* replace the compressed UDP head by the uncompressed UDP
109 	 * header
110 	 */
111 	err = skb_cow(skb, needed);
112 	if (unlikely(err))
113 		return err;
114 
115 	skb_push(skb, sizeof(struct udphdr));
116 	skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
117 
118 	return 0;
119 }
120 
121 static int udp_compress(struct sk_buff *skb, u8 **hc_ptr)
122 {
123 	const struct udphdr *uh = udp_hdr(skb);
124 	u8 tmp;
125 
126 	if (((ntohs(uh->source) & LOWPAN_NHC_UDP_4BIT_MASK) ==
127 	     LOWPAN_NHC_UDP_4BIT_PORT) &&
128 	    ((ntohs(uh->dest) & LOWPAN_NHC_UDP_4BIT_MASK) ==
129 	     LOWPAN_NHC_UDP_4BIT_PORT)) {
130 		pr_debug("UDP header: both ports compression to 4 bits\n");
131 		/* compression value */
132 		tmp = LOWPAN_NHC_UDP_CS_P_11;
133 		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
134 		/* source and destination port */
135 		tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_4BIT_PORT +
136 		      ((ntohs(uh->source) - LOWPAN_NHC_UDP_4BIT_PORT) << 4);
137 		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
138 	} else if ((ntohs(uh->dest) & LOWPAN_NHC_UDP_8BIT_MASK) ==
139 			LOWPAN_NHC_UDP_8BIT_PORT) {
140 		pr_debug("UDP header: remove 8 bits of dest\n");
141 		/* compression value */
142 		tmp = LOWPAN_NHC_UDP_CS_P_01;
143 		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
144 		/* source port */
145 		lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
146 		/* destination port */
147 		tmp = ntohs(uh->dest) - LOWPAN_NHC_UDP_8BIT_PORT;
148 		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
149 	} else if ((ntohs(uh->source) & LOWPAN_NHC_UDP_8BIT_MASK) ==
150 			LOWPAN_NHC_UDP_8BIT_PORT) {
151 		pr_debug("UDP header: remove 8 bits of source\n");
152 		/* compression value */
153 		tmp = LOWPAN_NHC_UDP_CS_P_10;
154 		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
155 		/* source port */
156 		tmp = ntohs(uh->source) - LOWPAN_NHC_UDP_8BIT_PORT;
157 		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
158 		/* destination port */
159 		lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
160 	} else {
161 		pr_debug("UDP header: can't compress\n");
162 		/* compression value */
163 		tmp = LOWPAN_NHC_UDP_CS_P_00;
164 		lowpan_push_hc_data(hc_ptr, &tmp, sizeof(tmp));
165 		/* source port */
166 		lowpan_push_hc_data(hc_ptr, &uh->source, sizeof(uh->source));
167 		/* destination port */
168 		lowpan_push_hc_data(hc_ptr, &uh->dest, sizeof(uh->dest));
169 	}
170 
171 	/* checksum is always inline */
172 	lowpan_push_hc_data(hc_ptr, &uh->check, sizeof(uh->check));
173 
174 	return 0;
175 }
176 
177 static void udp_nhid_setup(struct lowpan_nhc *nhc)
178 {
179 	nhc->id[0] = LOWPAN_NHC_UDP_ID;
180 	nhc->idmask[0] = LOWPAN_NHC_UDP_MASK;
181 }
182 
183 LOWPAN_NHC(nhc_udp, "RFC6282 UDP", NEXTHDR_UDP, sizeof(struct udphdr),
184 	   udp_nhid_setup, LOWPAN_NHC_UDP_IDLEN, udp_uncompress, udp_compress);
185 
186 module_lowpan_nhc(nhc_udp);
187 MODULE_DESCRIPTION("6LoWPAN next header RFC6282 UDP compression");
188 MODULE_LICENSE("GPL");
189