xref: /freebsd/sys/netinet/in_cksum.c (revision ecbbe831445a4b94279e5932828d60076e4ca444)
1*ecbbe831SMark Johnston /*-
2*ecbbe831SMark Johnston  * SPDX-License-Identifier: BSD-4-Clause
3*ecbbe831SMark Johnston  *
4*ecbbe831SMark Johnston  * Copyright (c) 1988, 1992, 1993
5*ecbbe831SMark Johnston  *	The Regents of the University of California.  All rights reserved.
6*ecbbe831SMark Johnston  * Copyright (c) 1996
7*ecbbe831SMark Johnston  *	Matt Thomas <matt@3am-software.com>
8*ecbbe831SMark Johnston  *
9*ecbbe831SMark Johnston  * Redistribution and use in source and binary forms, with or without
10*ecbbe831SMark Johnston  * modification, are permitted provided that the following conditions
11*ecbbe831SMark Johnston  * are met:
12*ecbbe831SMark Johnston  * 1. Redistributions of source code must retain the above copyright
13*ecbbe831SMark Johnston  *    notice, this list of conditions and the following disclaimer.
14*ecbbe831SMark Johnston  * 2. Redistributions in binary form must reproduce the above copyright
15*ecbbe831SMark Johnston  *    notice, this list of conditions and the following disclaimer in the
16*ecbbe831SMark Johnston  *    documentation and/or other materials provided with the distribution.
17*ecbbe831SMark Johnston  * 3. All advertising materials mentioning features or use of this software
18*ecbbe831SMark Johnston  *    must display the following acknowledgement:
19*ecbbe831SMark Johnston  *	This product includes software developed by the University of
20*ecbbe831SMark Johnston  *	California, Berkeley and its contributors.
21*ecbbe831SMark Johnston  * 4. Neither the name of the University nor the names of its contributors
22*ecbbe831SMark Johnston  *    may be used to endorse or promote products derived from this software
23*ecbbe831SMark Johnston  *    without specific prior written permission.
24*ecbbe831SMark Johnston  *
25*ecbbe831SMark Johnston  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26*ecbbe831SMark Johnston  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27*ecbbe831SMark Johnston  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28*ecbbe831SMark Johnston  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29*ecbbe831SMark Johnston  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30*ecbbe831SMark Johnston  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31*ecbbe831SMark Johnston  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32*ecbbe831SMark Johnston  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33*ecbbe831SMark Johnston  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34*ecbbe831SMark Johnston  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35*ecbbe831SMark Johnston  * SUCH DAMAGE.
36*ecbbe831SMark Johnston  *
37*ecbbe831SMark Johnston  *	@(#)in_cksum.c	8.1 (Berkeley) 6/10/93
38*ecbbe831SMark Johnston  */
39*ecbbe831SMark Johnston 
40*ecbbe831SMark Johnston #include <sys/cdefs.h>
41*ecbbe831SMark Johnston __FBSDID("$FreeBSD$");
42*ecbbe831SMark Johnston 
43*ecbbe831SMark Johnston #include <sys/param.h>
44*ecbbe831SMark Johnston #include <sys/mbuf.h>
45*ecbbe831SMark Johnston #include <sys/systm.h>
46*ecbbe831SMark Johnston #include <netinet/in_systm.h>
47*ecbbe831SMark Johnston #include <netinet/in.h>
48*ecbbe831SMark Johnston #include <netinet/ip.h>
49*ecbbe831SMark Johnston #include <machine/in_cksum.h>
50*ecbbe831SMark Johnston 
51*ecbbe831SMark Johnston /*
52*ecbbe831SMark Johnston  * These implementations may be overridden on a per-platform basis.
53*ecbbe831SMark Johnston  */
54*ecbbe831SMark Johnston #ifndef HAVE_MD_IN_CKSUM
55*ecbbe831SMark Johnston 
56*ecbbe831SMark Johnston /*
57*ecbbe831SMark Johnston  * Checksum routine for Internet Protocol family headers
58*ecbbe831SMark Johnston  *    (Portable Alpha version).
59*ecbbe831SMark Johnston  *
60*ecbbe831SMark Johnston  * This routine is very heavily used in the network
61*ecbbe831SMark Johnston  * code and should be modified for each CPU to be as fast as possible.
62*ecbbe831SMark Johnston  */
63*ecbbe831SMark Johnston 
64*ecbbe831SMark Johnston #define ADDCARRY(x)  (x > 65535 ? x -= 65535 : x)
65*ecbbe831SMark Johnston #define REDUCE32							  \
66*ecbbe831SMark Johnston     {									  \
67*ecbbe831SMark Johnston 	q_util.q = sum;							  \
68*ecbbe831SMark Johnston 	sum = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3];	  \
69*ecbbe831SMark Johnston     }
70*ecbbe831SMark Johnston #define REDUCE16							  \
71*ecbbe831SMark Johnston     {									  \
72*ecbbe831SMark Johnston 	q_util.q = sum;							  \
73*ecbbe831SMark Johnston 	l_util.l = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \
74*ecbbe831SMark Johnston 	sum = l_util.s[0] + l_util.s[1];				  \
75*ecbbe831SMark Johnston 	ADDCARRY(sum);							  \
76*ecbbe831SMark Johnston     }
77*ecbbe831SMark Johnston 
78*ecbbe831SMark Johnston static const u_int32_t in_masks[] = {
79*ecbbe831SMark Johnston #if _BYTE_ORDER == _LITTLE_ENDIAN
80*ecbbe831SMark Johnston 	/*0 bytes*/ /*1 byte*/	/*2 bytes*/ /*3 bytes*/
81*ecbbe831SMark Johnston 	0x00000000, 0x000000FF, 0x0000FFFF, 0x00FFFFFF,	/* offset 0 */
82*ecbbe831SMark Johnston 	0x00000000, 0x0000FF00, 0x00FFFF00, 0xFFFFFF00,	/* offset 1 */
83*ecbbe831SMark Johnston 	0x00000000, 0x00FF0000, 0xFFFF0000, 0xFFFF0000,	/* offset 2 */
84*ecbbe831SMark Johnston 	0x00000000, 0xFF000000, 0xFF000000, 0xFF000000,	/* offset 3 */
85*ecbbe831SMark Johnston #else
86*ecbbe831SMark Johnston 	/*0 bytes*/ /*1 byte*/	/*2 bytes*/ /*3 bytes*/
87*ecbbe831SMark Johnston 	0x00000000, 0xFF000000, 0xFFFF0000, 0xFFFFFF00,	/* offset 0 */
88*ecbbe831SMark Johnston 	0x00000000, 0x00FF0000, 0x00FFFF00, 0x00FFFFFF,	/* offset 1 */
89*ecbbe831SMark Johnston 	0x00000000, 0x0000FF00, 0x0000FFFF, 0x0000FFFF,	/* offset 2 */
90*ecbbe831SMark Johnston 	0x00000000, 0x000000FF, 0x000000FF, 0x000000FF,	/* offset 3 */
91*ecbbe831SMark Johnston #endif
92*ecbbe831SMark Johnston };
93*ecbbe831SMark Johnston 
94*ecbbe831SMark Johnston union l_util {
95*ecbbe831SMark Johnston 	u_int16_t s[2];
96*ecbbe831SMark Johnston 	u_int32_t l;
97*ecbbe831SMark Johnston };
98*ecbbe831SMark Johnston union q_util {
99*ecbbe831SMark Johnston 	u_int16_t s[4];
100*ecbbe831SMark Johnston 	u_int32_t l[2];
101*ecbbe831SMark Johnston 	u_int64_t q;
102*ecbbe831SMark Johnston };
103*ecbbe831SMark Johnston 
104*ecbbe831SMark Johnston static u_int64_t
105*ecbbe831SMark Johnston in_cksumdata(const void *buf, int len)
106*ecbbe831SMark Johnston {
107*ecbbe831SMark Johnston 	const u_int32_t *lw = (const u_int32_t *) buf;
108*ecbbe831SMark Johnston 	u_int64_t sum = 0;
109*ecbbe831SMark Johnston 	u_int64_t prefilled;
110*ecbbe831SMark Johnston 	int offset;
111*ecbbe831SMark Johnston 	union q_util q_util;
112*ecbbe831SMark Johnston 
113*ecbbe831SMark Johnston 	if ((3 & (long) lw) == 0 && len == 20) {
114*ecbbe831SMark Johnston 		sum = (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3] + lw[4];
115*ecbbe831SMark Johnston 		REDUCE32;
116*ecbbe831SMark Johnston 		return sum;
117*ecbbe831SMark Johnston 	}
118*ecbbe831SMark Johnston 
119*ecbbe831SMark Johnston 	if ((offset = 3 & (long) lw) != 0) {
120*ecbbe831SMark Johnston 		const u_int32_t *masks = in_masks + (offset << 2);
121*ecbbe831SMark Johnston 		lw = (u_int32_t *) (((long) lw) - offset);
122*ecbbe831SMark Johnston 		sum = *lw++ & masks[len >= 3 ? 3 : len];
123*ecbbe831SMark Johnston 		len -= 4 - offset;
124*ecbbe831SMark Johnston 		if (len <= 0) {
125*ecbbe831SMark Johnston 			REDUCE32;
126*ecbbe831SMark Johnston 			return sum;
127*ecbbe831SMark Johnston 		}
128*ecbbe831SMark Johnston 	}
129*ecbbe831SMark Johnston #if 0
130*ecbbe831SMark Johnston 	/*
131*ecbbe831SMark Johnston 	 * Force to cache line boundary.
132*ecbbe831SMark Johnston 	 */
133*ecbbe831SMark Johnston 	offset = 32 - (0x1f & (long) lw);
134*ecbbe831SMark Johnston 	if (offset < 32 && len > offset) {
135*ecbbe831SMark Johnston 		len -= offset;
136*ecbbe831SMark Johnston 		if (4 & offset) {
137*ecbbe831SMark Johnston 			sum += (u_int64_t) lw[0];
138*ecbbe831SMark Johnston 			lw += 1;
139*ecbbe831SMark Johnston 		}
140*ecbbe831SMark Johnston 		if (8 & offset) {
141*ecbbe831SMark Johnston 			sum += (u_int64_t) lw[0] + lw[1];
142*ecbbe831SMark Johnston 			lw += 2;
143*ecbbe831SMark Johnston 		}
144*ecbbe831SMark Johnston 		if (16 & offset) {
145*ecbbe831SMark Johnston 			sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
146*ecbbe831SMark Johnston 			lw += 4;
147*ecbbe831SMark Johnston 		}
148*ecbbe831SMark Johnston 	}
149*ecbbe831SMark Johnston #endif
150*ecbbe831SMark Johnston 	/*
151*ecbbe831SMark Johnston 	 * access prefilling to start load of next cache line.
152*ecbbe831SMark Johnston 	 * then add current cache line
153*ecbbe831SMark Johnston 	 * save result of prefilling for loop iteration.
154*ecbbe831SMark Johnston 	 */
155*ecbbe831SMark Johnston 	prefilled = lw[0];
156*ecbbe831SMark Johnston 	while ((len -= 32) >= 4) {
157*ecbbe831SMark Johnston 		u_int64_t prefilling = lw[8];
158*ecbbe831SMark Johnston 		sum += prefilled + lw[1] + lw[2] + lw[3]
159*ecbbe831SMark Johnston 			+ lw[4] + lw[5] + lw[6] + lw[7];
160*ecbbe831SMark Johnston 		lw += 8;
161*ecbbe831SMark Johnston 		prefilled = prefilling;
162*ecbbe831SMark Johnston 	}
163*ecbbe831SMark Johnston 	if (len >= 0) {
164*ecbbe831SMark Johnston 		sum += prefilled + lw[1] + lw[2] + lw[3]
165*ecbbe831SMark Johnston 			+ lw[4] + lw[5] + lw[6] + lw[7];
166*ecbbe831SMark Johnston 		lw += 8;
167*ecbbe831SMark Johnston 	} else {
168*ecbbe831SMark Johnston 		len += 32;
169*ecbbe831SMark Johnston 	}
170*ecbbe831SMark Johnston 	while ((len -= 16) >= 0) {
171*ecbbe831SMark Johnston 		sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
172*ecbbe831SMark Johnston 		lw += 4;
173*ecbbe831SMark Johnston 	}
174*ecbbe831SMark Johnston 	len += 16;
175*ecbbe831SMark Johnston 	while ((len -= 4) >= 0) {
176*ecbbe831SMark Johnston 		sum += (u_int64_t) *lw++;
177*ecbbe831SMark Johnston 	}
178*ecbbe831SMark Johnston 	len += 4;
179*ecbbe831SMark Johnston 	if (len > 0)
180*ecbbe831SMark Johnston 		sum += (u_int64_t) (in_masks[len] & *lw);
181*ecbbe831SMark Johnston 	REDUCE32;
182*ecbbe831SMark Johnston 	return sum;
183*ecbbe831SMark Johnston }
184*ecbbe831SMark Johnston 
185*ecbbe831SMark Johnston u_short
186*ecbbe831SMark Johnston in_addword(u_short a, u_short b)
187*ecbbe831SMark Johnston {
188*ecbbe831SMark Johnston 	u_int64_t sum = a + b;
189*ecbbe831SMark Johnston 
190*ecbbe831SMark Johnston 	ADDCARRY(sum);
191*ecbbe831SMark Johnston 	return (sum);
192*ecbbe831SMark Johnston }
193*ecbbe831SMark Johnston 
194*ecbbe831SMark Johnston u_short
195*ecbbe831SMark Johnston in_pseudo(u_int32_t a, u_int32_t b, u_int32_t c)
196*ecbbe831SMark Johnston {
197*ecbbe831SMark Johnston 	u_int64_t sum;
198*ecbbe831SMark Johnston 	union q_util q_util;
199*ecbbe831SMark Johnston 	union l_util l_util;
200*ecbbe831SMark Johnston 
201*ecbbe831SMark Johnston 	sum = (u_int64_t) a + b + c;
202*ecbbe831SMark Johnston 	REDUCE16;
203*ecbbe831SMark Johnston 	return (sum);
204*ecbbe831SMark Johnston }
205*ecbbe831SMark Johnston 
206*ecbbe831SMark Johnston u_short
207*ecbbe831SMark Johnston in_cksum_skip(struct mbuf *m, int len, int skip)
208*ecbbe831SMark Johnston {
209*ecbbe831SMark Johnston 	u_int64_t sum = 0;
210*ecbbe831SMark Johnston 	int mlen = 0;
211*ecbbe831SMark Johnston 	int clen = 0;
212*ecbbe831SMark Johnston 	caddr_t addr;
213*ecbbe831SMark Johnston 	union q_util q_util;
214*ecbbe831SMark Johnston 	union l_util l_util;
215*ecbbe831SMark Johnston 
216*ecbbe831SMark Johnston 	len -= skip;
217*ecbbe831SMark Johnston 	for (; skip && m; m = m->m_next) {
218*ecbbe831SMark Johnston 		if (m->m_len > skip) {
219*ecbbe831SMark Johnston 			mlen = m->m_len - skip;
220*ecbbe831SMark Johnston 			addr = mtod(m, caddr_t) + skip;
221*ecbbe831SMark Johnston 			goto skip_start;
222*ecbbe831SMark Johnston 		} else {
223*ecbbe831SMark Johnston 			skip -= m->m_len;
224*ecbbe831SMark Johnston 		}
225*ecbbe831SMark Johnston 	}
226*ecbbe831SMark Johnston 
227*ecbbe831SMark Johnston 	for (; m && len; m = m->m_next) {
228*ecbbe831SMark Johnston 		if (m->m_len == 0)
229*ecbbe831SMark Johnston 			continue;
230*ecbbe831SMark Johnston 		mlen = m->m_len;
231*ecbbe831SMark Johnston 		addr = mtod(m, caddr_t);
232*ecbbe831SMark Johnston skip_start:
233*ecbbe831SMark Johnston 		if (len < mlen)
234*ecbbe831SMark Johnston 			mlen = len;
235*ecbbe831SMark Johnston 
236*ecbbe831SMark Johnston 		if ((clen ^ (uintptr_t) addr) & 1)
237*ecbbe831SMark Johnston 			sum += in_cksumdata(addr, mlen) << 8;
238*ecbbe831SMark Johnston 		else
239*ecbbe831SMark Johnston 			sum += in_cksumdata(addr, mlen);
240*ecbbe831SMark Johnston 
241*ecbbe831SMark Johnston 		clen += mlen;
242*ecbbe831SMark Johnston 		len -= mlen;
243*ecbbe831SMark Johnston 	}
244*ecbbe831SMark Johnston 	REDUCE16;
245*ecbbe831SMark Johnston 	return (~sum & 0xffff);
246*ecbbe831SMark Johnston }
247*ecbbe831SMark Johnston 
248*ecbbe831SMark Johnston u_int in_cksum_hdr(const struct ip *ip)
249*ecbbe831SMark Johnston {
250*ecbbe831SMark Johnston 	u_int64_t sum = in_cksumdata(ip, sizeof(struct ip));
251*ecbbe831SMark Johnston 	union q_util q_util;
252*ecbbe831SMark Johnston 	union l_util l_util;
253*ecbbe831SMark Johnston 	REDUCE16;
254*ecbbe831SMark Johnston 	return (~sum & 0xffff);
255*ecbbe831SMark Johnston }
256*ecbbe831SMark Johnston 
257*ecbbe831SMark Johnston #endif /* !HAVE_MD_IN_CKSUM */
258