xref: /freebsd/sys/netinet6/in6_cksum.c (revision 3706af423ff257540dfe88bcb91fb0ab54e60946)
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  *	@(#)in_cksum.c	8.1 (Berkeley) 6/10/93
63  */
64 
65 #include <sys/cdefs.h>
66 __FBSDID("$FreeBSD$");
67 
68 #include <sys/param.h>
69 #include <sys/mbuf.h>
70 #include <sys/systm.h>
71 #include <netinet/in.h>
72 #include <netinet/ip6.h>
73 #include <netinet6/scope6_var.h>
74 
75 /*
76  * Checksum routine for Internet Protocol family headers (Portable Version).
77  *
78  * This routine is very heavily used in the network
79  * code and should be modified for each CPU to be as fast as possible.
80  */
81 
82 #define ADDCARRY(x)  (x > 65535 ? x -= 65535 : x)
83 #define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; (void)ADDCARRY(sum);}
84 
85 static int
86 _in6_cksum_pseudo(struct ip6_hdr *ip6, uint32_t len, uint8_t nxt, uint16_t csum)
87 {
88 	int sum;
89 	uint16_t scope, *w;
90 	union {
91 		u_int16_t phs[4];
92 		struct {
93 			u_int32_t	ph_len;
94 			u_int8_t	ph_zero[3];
95 			u_int8_t	ph_nxt;
96 		} __packed ph;
97 	} uph;
98 
99 	sum = csum;
100 
101 	/*
102 	 * First create IP6 pseudo header and calculate a summary.
103 	 */
104 	uph.ph.ph_len = htonl(len);
105 	uph.ph.ph_zero[0] = uph.ph.ph_zero[1] = uph.ph.ph_zero[2] = 0;
106 	uph.ph.ph_nxt = nxt;
107 
108 	/* Payload length and upper layer identifier. */
109 	sum += uph.phs[0];  sum += uph.phs[1];
110 	sum += uph.phs[2];  sum += uph.phs[3];
111 
112 	/* IPv6 source address. */
113 	scope = in6_getscope(&ip6->ip6_src);
114 	w = (u_int16_t *)&ip6->ip6_src;
115 	sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
116 	sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
117 	if (scope != 0)
118 		sum -= scope;
119 
120 	/* IPv6 destination address. */
121 	scope = in6_getscope(&ip6->ip6_dst);
122 	w = (u_int16_t *)&ip6->ip6_dst;
123 	sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
124 	sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
125 	if (scope != 0)
126 		sum -= scope;
127 
128 	return (sum);
129 }
130 
131 int
132 in6_cksum_pseudo(struct ip6_hdr *ip6, uint32_t len, uint8_t nxt, uint16_t csum)
133 {
134 	int sum;
135 	union {
136 		u_int16_t s[2];
137 		u_int32_t l;
138 	} l_util;
139 
140 	sum = _in6_cksum_pseudo(ip6, len, nxt, csum);
141 	REDUCE;
142 	return (sum);
143 }
144 
145 /*
146  * m MUST contain a contiguous IP6 header.
147  * off is an offset where TCP/UDP/ICMP6 header starts.
148  * len is a total length of a transport segment.
149  * (e.g. TCP header + TCP payload)
150  * cov is the number of bytes to be taken into account for the checksum
151  */
152 int
153 in6_cksum_partial(struct mbuf *m, u_int8_t nxt, u_int32_t off,
154     u_int32_t len, u_int32_t cov)
155 {
156 	struct ip6_hdr *ip6;
157 	u_int16_t *w, scope;
158 	int byte_swapped, mlen;
159 	int sum;
160 	union {
161 		u_int16_t phs[4];
162 		struct {
163 			u_int32_t	ph_len;
164 			u_int8_t	ph_zero[3];
165 			u_int8_t	ph_nxt;
166 		} __packed ph;
167 	} uph;
168 	union {
169 		u_int8_t	c[2];
170 		u_int16_t	s;
171 	} s_util;
172 	union {
173 		u_int16_t s[2];
174 		u_int32_t l;
175 	} l_util;
176 
177 	/* Sanity check. */
178 	KASSERT(m->m_pkthdr.len >= off + len, ("%s: mbuf len (%d) < off(%d)+"
179 	    "len(%d)", __func__, m->m_pkthdr.len, off, len));
180 
181 	/*
182 	 * First create IP6 pseudo header and calculate a summary.
183 	 */
184 	uph.ph.ph_len = htonl(len);
185 	uph.ph.ph_zero[0] = uph.ph.ph_zero[1] = uph.ph.ph_zero[2] = 0;
186 	uph.ph.ph_nxt = nxt;
187 
188 	/* Payload length and upper layer identifier. */
189 	sum = uph.phs[0];  sum += uph.phs[1];
190 	sum += uph.phs[2];  sum += uph.phs[3];
191 
192 	ip6 = mtod(m, struct ip6_hdr *);
193 
194 	/* IPv6 source address. */
195 	scope = in6_getscope(&ip6->ip6_src);
196 	w = (u_int16_t *)&ip6->ip6_src;
197 	sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
198 	sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
199 	if (scope != 0)
200 		sum -= scope;
201 
202 	/* IPv6 destination address. */
203 	scope = in6_getscope(&ip6->ip6_dst);
204 	w = (u_int16_t *)&ip6->ip6_dst;
205 	sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
206 	sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
207 	if (scope != 0)
208 		sum -= scope;
209 
210 	/*
211 	 * Secondly calculate a summary of the first mbuf excluding offset.
212 	 */
213 	while (off > 0) {
214 		if (m->m_len <= off)
215 			off -= m->m_len;
216 		else
217 			break;
218 		m = m->m_next;
219 	}
220 	w = (u_int16_t *)(mtod(m, u_char *) + off);
221 	mlen = m->m_len - off;
222 	if (cov < mlen)
223 		mlen = cov;
224 	cov -= mlen;
225 	/*
226 	 * Force to even boundary.
227 	 */
228 	if ((1 & (long)w) && (mlen > 0)) {
229 		REDUCE;
230 		sum <<= 8;
231 		s_util.c[0] = *(u_char *)w;
232 		w = (u_int16_t *)((char *)w + 1);
233 		mlen--;
234 		byte_swapped = 1;
235 	} else
236 		byte_swapped = 0;
237 
238 	/*
239 	 * Unroll the loop to make overhead from
240 	 * branches &c small.
241 	 */
242 	while ((mlen -= 32) >= 0) {
243 		sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
244 		sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
245 		sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
246 		sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
247 		w += 16;
248 	}
249 	mlen += 32;
250 	while ((mlen -= 8) >= 0) {
251 		sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
252 		w += 4;
253 	}
254 	mlen += 8;
255 	if (mlen == 0 && byte_swapped == 0)
256 		goto next;
257 	REDUCE;
258 	while ((mlen -= 2) >= 0) {
259 		sum += *w++;
260 	}
261 	if (byte_swapped) {
262 		REDUCE;
263 		sum <<= 8;
264 		byte_swapped = 0;
265 		if (mlen == -1) {
266 			s_util.c[1] = *(char *)w;
267 			sum += s_util.s;
268 			mlen = 0;
269 		} else
270 			mlen = -1;
271 	} else if (mlen == -1)
272 		s_util.c[0] = *(char *)w;
273  next:
274 	m = m->m_next;
275 
276 	/*
277 	 * Lastly calculate a summary of the rest of mbufs.
278 	 */
279 
280 	for (;m && cov; m = m->m_next) {
281 		if (m->m_len == 0)
282 			continue;
283 		w = mtod(m, u_int16_t *);
284 		if (mlen == -1) {
285 			/*
286 			 * The first byte of this mbuf is the continuation
287 			 * of a word spanning between this mbuf and the
288 			 * last mbuf.
289 			 *
290 			 * s_util.c[0] is already saved when scanning previous
291 			 * mbuf.
292 			 */
293 			s_util.c[1] = *(char *)w;
294 			sum += s_util.s;
295 			w = (u_int16_t *)((char *)w + 1);
296 			mlen = m->m_len - 1;
297 			cov--;
298 		} else
299 			mlen = m->m_len;
300 		if (cov < mlen)
301 			mlen = cov;
302 		cov -= mlen;
303 		/*
304 		 * Force to even boundary.
305 		 */
306 		if ((1 & (long) w) && (mlen > 0)) {
307 			REDUCE;
308 			sum <<= 8;
309 			s_util.c[0] = *(u_char *)w;
310 			w = (u_int16_t *)((char *)w + 1);
311 			mlen--;
312 			byte_swapped = 1;
313 		}
314 		/*
315 		 * Unroll the loop to make overhead from
316 		 * branches &c small.
317 		 */
318 		while ((mlen -= 32) >= 0) {
319 			sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
320 			sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
321 			sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
322 			sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
323 			w += 16;
324 		}
325 		mlen += 32;
326 		while ((mlen -= 8) >= 0) {
327 			sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
328 			w += 4;
329 		}
330 		mlen += 8;
331 		if (mlen == 0 && byte_swapped == 0)
332 			continue;
333 		REDUCE;
334 		while ((mlen -= 2) >= 0) {
335 			sum += *w++;
336 		}
337 		if (byte_swapped) {
338 			REDUCE;
339 			sum <<= 8;
340 			byte_swapped = 0;
341 			if (mlen == -1) {
342 				s_util.c[1] = *(char *)w;
343 				sum += s_util.s;
344 				mlen = 0;
345 			} else
346 				mlen = -1;
347 		} else if (mlen == -1)
348 			s_util.c[0] = *(char *)w;
349 	}
350 	if (cov)
351 		panic("in6_cksum: out of data");
352 	if (mlen == -1) {
353 		/* The last mbuf has odd # of bytes. Follow the
354 		   standard (the odd byte may be shifted left by 8 bits
355 		   or not as determined by endian-ness of the machine) */
356 		s_util.c[1] = 0;
357 		sum += s_util.s;
358 	}
359 	REDUCE;
360 	return (~sum & 0xffff);
361 }
362 
363 int
364 in6_cksum(struct mbuf *m, u_int8_t nxt, u_int32_t off, u_int32_t len)
365 {
366 	return (in6_cksum_partial(m, nxt, off, len, len));
367 }
368