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