xref: /freebsd/sys/netinet6/in6_cksum.c (revision fcf81de12f27d34a5c18168fd0c756c371a62076)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the project nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  *	$KAME: in6_cksum.c,v 1.10 2000/12/03 00:53:59 itojun Exp $
32  */
33 
34 /*-
35  * Copyright (c) 1988, 1992, 1993
36  *	The Regents of the University of California.  All rights reserved.
37  *
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions
40  * are met:
41  * 1. Redistributions of source code must retain the above copyright
42  *    notice, this list of conditions and the following disclaimer.
43  * 2. Redistributions in binary form must reproduce the above copyright
44  *    notice, this list of conditions and the following disclaimer in the
45  *    documentation and/or other materials provided with the distribution.
46  * 3. Neither the name of the University nor the names of its contributors
47  *    may be used to endorse or promote products derived from this software
48  *    without specific prior written permission.
49  *
50  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
51  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
54  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
55  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
56  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
57  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
58  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
59  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
60  * SUCH DAMAGE.
61  */
62 
63 #include <sys/param.h>
64 #include <sys/mbuf.h>
65 #include <sys/systm.h>
66 #include <netinet/in.h>
67 #include <netinet/ip6.h>
68 #include <netinet6/scope6_var.h>
69 
70 /*
71  * Checksum routine for Internet Protocol family headers (Portable Version).
72  *
73  * This routine is very heavily used in the network
74  * code and should be modified for each CPU to be as fast as possible.
75  */
76 
77 #define ADDCARRY(x)  (x > 65535 ? x -= 65535 : x)
78 #define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; (void)ADDCARRY(sum);}
79 
80 union l_util {
81 	uint16_t	s[2];
82 	uint32_t	l;
83 };
84 
85 union s_util {
86 	uint8_t		c[2];
87 	uint16_t	s;
88 };
89 
90 static int
_in6_cksum_pseudo(struct ip6_hdr * ip6,uint32_t len,uint8_t nxt,uint16_t csum)91 _in6_cksum_pseudo(struct ip6_hdr *ip6, uint32_t len, uint8_t nxt, uint16_t csum)
92 {
93 	int sum;
94 	uint16_t scope, *w;
95 	union {
96 		u_int16_t phs[4];
97 		struct {
98 			u_int32_t	ph_len;
99 			u_int8_t	ph_zero[3];
100 			u_int8_t	ph_nxt;
101 		} __packed ph;
102 	} uph;
103 
104 	sum = csum;
105 
106 	/*
107 	 * First create IP6 pseudo header and calculate a summary.
108 	 */
109 	uph.ph.ph_len = htonl(len);
110 	uph.ph.ph_zero[0] = uph.ph.ph_zero[1] = uph.ph.ph_zero[2] = 0;
111 	uph.ph.ph_nxt = nxt;
112 
113 	/* Payload length and upper layer identifier. */
114 	sum += uph.phs[0];  sum += uph.phs[1];
115 	sum += uph.phs[2];  sum += uph.phs[3];
116 
117 	/* IPv6 source address. */
118 	scope = in6_getscope(&ip6->ip6_src);
119 	w = (u_int16_t *)&ip6->ip6_src;
120 	sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
121 	sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
122 	if (scope != 0)
123 		sum -= scope;
124 
125 	/* IPv6 destination address. */
126 	scope = in6_getscope(&ip6->ip6_dst);
127 	w = (u_int16_t *)&ip6->ip6_dst;
128 	sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
129 	sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
130 	if (scope != 0)
131 		sum -= scope;
132 
133 	return (sum);
134 }
135 
136 int
in6_cksum_pseudo(struct ip6_hdr * ip6,uint32_t len,uint8_t nxt,uint16_t csum)137 in6_cksum_pseudo(struct ip6_hdr *ip6, uint32_t len, uint8_t nxt, uint16_t csum)
138 {
139 	union l_util l_util;
140 	int sum;
141 
142 	sum = _in6_cksum_pseudo(ip6, len, nxt, csum);
143 	REDUCE;
144 	return (sum);
145 }
146 
147 static int
in6_cksumdata(void * data,int * lenp,uint8_t * residp,int rlen)148 in6_cksumdata(void *data, int *lenp, uint8_t *residp, int rlen)
149 {
150 	union l_util l_util;
151 	union s_util s_util;
152 	uint16_t *w;
153 	int len, sum;
154 	bool byte_swapped;
155 
156 	KASSERT(*lenp >= 0, ("%s: negative len %d", __func__, *lenp));
157 	KASSERT(rlen == 0 || rlen == 1, ("%s: rlen %d", __func__, rlen));
158 
159 	len = *lenp;
160 	sum = 0;
161 
162 	if (len == 0) {
163 		len = rlen;
164 		goto out;
165 	}
166 
167 	byte_swapped = false;
168 	w = data;
169 
170 	/*
171 	 * Do we have a residual byte left over from the previous buffer?
172 	 */
173 	if (rlen == 1) {
174 		s_util.c[0] = *residp;
175 		s_util.c[1] = *(uint8_t *)w;
176 		sum += s_util.s;
177 		w = (uint16_t *)((uint8_t *)w + 1);
178 		len--;
179 		rlen = 0;
180 	}
181 
182 	/*
183 	 * Force to even boundary.
184 	 */
185 	if ((1 & (uintptr_t)w) && len > 0) {
186 		REDUCE;
187 		sum <<= 8;
188 		s_util.c[0] = *(uint8_t *)w;
189 		w = (uint16_t *)((uint8_t *)w + 1);
190 		len--;
191 		byte_swapped = true;
192 	}
193 
194 	/*
195 	 * Unroll the loop to make overhead from branches &c small.
196 	 */
197 	while ((len -= 32) >= 0) {
198 		sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
199 		sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
200 		sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
201 		sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
202 		w += 16;
203 	}
204 	len += 32;
205 	while ((len -= 8) >= 0) {
206 		sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
207 		w += 4;
208 	}
209 	len += 8;
210 	if (len == 0 && !byte_swapped)
211 		goto out;
212 	REDUCE;
213 	while ((len -= 2) >= 0) {
214 		sum += *w++;
215 	}
216 	if (byte_swapped) {
217 		REDUCE;
218 		sum <<= 8;
219 		if (len == -1) {
220 			s_util.c[1] = *(uint8_t *)w;
221 			sum += s_util.s;
222 		} else /* len == -2 */
223 			*residp = s_util.c[0];
224 		len++;
225 	} else if (len == -1)
226 		*residp = *(uint8_t *)w;
227 out:
228 	*lenp = len & 1;
229 	return (sum);
230 }
231 
232 struct in6_cksum_partial_arg {
233 	int	sum;
234 	int	rlen;
235 	uint8_t	resid;
236 };
237 
238 static int
in6_cksum_partial_one(void * _arg,void * data,u_int len)239 in6_cksum_partial_one(void *_arg, void *data, u_int len)
240 {
241 	struct in6_cksum_partial_arg *arg = _arg;
242 
243 	arg->sum += in6_cksumdata(data, &len, &arg->resid, arg->rlen);
244 	arg->rlen = len;
245 	return (0);
246 }
247 
248 /*
249  * m MUST contain a contiguous IP6 header.
250  * off_l3 is an offset where ipv6 header starts.
251  * off_l4 is an offset where TCP/UDP/ICMP6 header starts.
252  * len is a total length of a transport segment.
253  * (e.g. TCP header + TCP payload)
254  * cov is the number of bytes to be taken into account for the checksum
255  */
256 int
in6_cksum_partial_l2(struct mbuf * m,uint8_t nxt,uint32_t off_l3,uint32_t off_l4,uint32_t len,uint32_t cov)257 in6_cksum_partial_l2(struct mbuf *m, uint8_t nxt, uint32_t off_l3,
258     uint32_t off_l4, uint32_t len, uint32_t cov)
259 {
260 	struct in6_cksum_partial_arg arg;
261 	union l_util l_util;
262 	union s_util s_util;
263 	struct ip6_hdr *ip6;
264 	uint16_t *w, scope;
265 	int sum;
266 	union {
267 		uint16_t phs[4];
268 		struct {
269 			uint32_t	ph_len;
270 			uint8_t		ph_zero[3];
271 			uint8_t		ph_nxt;
272 		} __packed ph;
273 	} uph;
274 
275 	/* Sanity check. */
276 	KASSERT(m->m_pkthdr.len >= off_l4 + len,
277 	    ("%s: mbuf len (%d) < off(%d)+len(%d)",
278 	    __func__, m->m_pkthdr.len, off_l4, len));
279 	KASSERT(m->m_len >= off_l3 + sizeof(*ip6),
280 	    ("%s: mbuf len %d < sizeof(ip6)", __func__, m->m_len));
281 
282 	/*
283 	 * First create IP6 pseudo header and calculate a summary.
284 	 */
285 	uph.ph.ph_len = htonl(len);
286 	uph.ph.ph_zero[0] = uph.ph.ph_zero[1] = uph.ph.ph_zero[2] = 0;
287 	uph.ph.ph_nxt = nxt;
288 
289 	/* Payload length and upper layer identifier. */
290 	sum = uph.phs[0];  sum += uph.phs[1];
291 	sum += uph.phs[2];  sum += uph.phs[3];
292 
293 	ip6 = mtodo(m, off_l3);
294 
295 	/* IPv6 source address. */
296 	scope = in6_getscope(&ip6->ip6_src);
297 	w = (uint16_t *)&ip6->ip6_src;
298 	sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
299 	sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
300 	if (scope != 0)
301 		sum -= scope;
302 
303 	/* IPv6 destination address. */
304 	scope = in6_getscope(&ip6->ip6_dst);
305 	w = (uint16_t *)&ip6->ip6_dst;
306 	sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
307 	sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
308 	if (scope != 0)
309 		sum -= scope;
310 
311 	/*
312 	 * Loop over the rest of the mbuf chain and compute the rest of the
313 	 * checksum.  m_apply() handles unmapped mbufs.
314 	 */
315 	arg.sum = sum;
316 	arg.rlen = 0;
317 	(void)m_apply(m, off_l4, cov, in6_cksum_partial_one, &arg);
318 	sum = arg.sum;
319 
320 	/*
321 	 * Handle a residual byte.
322 	 */
323 	if (arg.rlen == 1) {
324 		s_util.c[0] = arg.resid;
325 		s_util.c[1] = 0;
326 		sum += s_util.s;
327 	}
328 	REDUCE;
329 	return (~sum & 0xffff);
330 }
331 
332 int
in6_cksum_partial(struct mbuf * m,uint8_t nxt,uint32_t off,uint32_t len,uint32_t cov)333 in6_cksum_partial(struct mbuf *m, uint8_t nxt, uint32_t off, uint32_t len,
334     uint32_t cov)
335 {
336 	return (in6_cksum_partial_l2(m, nxt, 0, off, len, cov));
337 }
338 
339 int
in6_cksum(struct mbuf * m,uint8_t nxt,uint32_t off,uint32_t len)340 in6_cksum(struct mbuf *m, uint8_t nxt, uint32_t off, uint32_t len)
341 {
342 	return (in6_cksum_partial(m, nxt, off, len, len));
343 }
344