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