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