1295041Sbr/* $NetBSD: in_cksum.c,v 1.7 1997/09/02 13:18:15 thorpej Exp $ */
2295041Sbr
3295041Sbr/*-
4295041Sbr * Copyright (c) 1988, 1992, 1993
5295041Sbr *	The Regents of the University of California.  All rights reserved.
6295041Sbr * Copyright (c) 1996
7295041Sbr *	Matt Thomas <matt@3am-software.com>
8295041Sbr *
9295041Sbr * Redistribution and use in source and binary forms, with or without
10295041Sbr * modification, are permitted provided that the following conditions
11295041Sbr * are met:
12295041Sbr * 1. Redistributions of source code must retain the above copyright
13295041Sbr *    notice, this list of conditions and the following disclaimer.
14295041Sbr * 2. Redistributions in binary form must reproduce the above copyright
15295041Sbr *    notice, this list of conditions and the following disclaimer in the
16295041Sbr *    documentation and/or other materials provided with the distribution.
17295041Sbr * 3. All advertising materials mentioning features or use of this software
18295041Sbr *    must display the following acknowledgement:
19295041Sbr *	This product includes software developed by the University of
20295041Sbr *	California, Berkeley and its contributors.
21295041Sbr * 4. Neither the name of the University nor the names of its contributors
22295041Sbr *    may be used to endorse or promote products derived from this software
23295041Sbr *    without specific prior written permission.
24295041Sbr *
25295041Sbr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26295041Sbr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27295041Sbr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28295041Sbr * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29295041Sbr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30295041Sbr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31295041Sbr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32295041Sbr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33295041Sbr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34295041Sbr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35295041Sbr * SUCH DAMAGE.
36295041Sbr *
37295041Sbr *	@(#)in_cksum.c	8.1 (Berkeley) 6/10/93
38295041Sbr */
39295041Sbr
40295041Sbr#include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
41295041Sbr__FBSDID("$FreeBSD$");
42295041Sbr
43295041Sbr#include <sys/param.h>
44295041Sbr#include <sys/mbuf.h>
45295041Sbr#include <sys/systm.h>
46295041Sbr#include <netinet/in_systm.h>
47295041Sbr#include <netinet/in.h>
48295041Sbr#include <netinet/ip.h>
49295041Sbr#include <machine/in_cksum.h>
50295041Sbr
51295041Sbr/*
52295041Sbr * Checksum routine for Internet Protocol family headers
53295041Sbr *    (Portable Alpha version).
54295041Sbr *
55295041Sbr * This routine is very heavily used in the network
56295041Sbr * code and should be modified for each CPU to be as fast as possible.
57295041Sbr */
58295041Sbr
59295041Sbr#define ADDCARRY(x)  (x > 65535 ? x -= 65535 : x)
60295041Sbr#define REDUCE32							  \
61295041Sbr    {									  \
62295041Sbr	q_util.q = sum;							  \
63295041Sbr	sum = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3];	  \
64295041Sbr    }
65295041Sbr#define REDUCE16							  \
66295041Sbr    {									  \
67295041Sbr	q_util.q = sum;							  \
68295041Sbr	l_util.l = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \
69295041Sbr	sum = l_util.s[0] + l_util.s[1];				  \
70295041Sbr	ADDCARRY(sum);							  \
71295041Sbr    }
72295041Sbr
73295041Sbrstatic const u_int32_t in_masks[] = {
74295041Sbr	/*0 bytes*/ /*1 byte*/	/*2 bytes*/ /*3 bytes*/
75295041Sbr	0x00000000, 0x000000FF, 0x0000FFFF, 0x00FFFFFF,	/* offset 0 */
76295041Sbr	0x00000000, 0x0000FF00, 0x00FFFF00, 0xFFFFFF00,	/* offset 1 */
77295041Sbr	0x00000000, 0x00FF0000, 0xFFFF0000, 0xFFFF0000,	/* offset 2 */
78295041Sbr	0x00000000, 0xFF000000, 0xFF000000, 0xFF000000,	/* offset 3 */
79295041Sbr};
80295041Sbr
81295041Sbrunion l_util {
82295041Sbr	u_int16_t s[2];
83295041Sbr	u_int32_t l;
84295041Sbr};
85295041Sbrunion q_util {
86295041Sbr	u_int16_t s[4];
87295041Sbr	u_int32_t l[2];
88295041Sbr	u_int64_t q;
89295041Sbr};
90295041Sbr
91295041Sbrstatic u_int64_t
92295041Sbrin_cksumdata(const void *buf, int len)
93295041Sbr{
94295041Sbr	const u_int32_t *lw = (const u_int32_t *) buf;
95295041Sbr	u_int64_t sum = 0;
96295041Sbr	u_int64_t prefilled;
97295041Sbr	int offset;
98295041Sbr	union q_util q_util;
99295041Sbr
100295041Sbr	if ((3 & (long) lw) == 0 && len == 20) {
101295041Sbr	     sum = (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3] + lw[4];
102295041Sbr	     REDUCE32;
103295041Sbr	     return sum;
104295041Sbr	}
105295041Sbr
106295041Sbr	if ((offset = 3 & (long) lw) != 0) {
107295041Sbr		const u_int32_t *masks = in_masks + (offset << 2);
108295041Sbr		lw = (u_int32_t *) (((long) lw) - offset);
109295041Sbr		sum = *lw++ & masks[len >= 3 ? 3 : len];
110295041Sbr		len -= 4 - offset;
111295041Sbr		if (len <= 0) {
112295041Sbr			REDUCE32;
113295041Sbr			return sum;
114295041Sbr		}
115295041Sbr	}
116295041Sbr#if 0
117295041Sbr	/*
118295041Sbr	 * Force to cache line boundary.
119295041Sbr	 */
120295041Sbr	offset = 32 - (0x1f & (long) lw);
121295041Sbr	if (offset < 32 && len > offset) {
122295041Sbr		len -= offset;
123295041Sbr		if (4 & offset) {
124295041Sbr			sum += (u_int64_t) lw[0];
125295041Sbr			lw += 1;
126295041Sbr		}
127295041Sbr		if (8 & offset) {
128295041Sbr			sum += (u_int64_t) lw[0] + lw[1];
129295041Sbr			lw += 2;
130295041Sbr		}
131295041Sbr		if (16 & offset) {
132295041Sbr			sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
133295041Sbr			lw += 4;
134295041Sbr		}
135295041Sbr	}
136295041Sbr#endif
137295041Sbr	/*
138295041Sbr	 * access prefilling to start load of next cache line.
139295041Sbr	 * then add current cache line
140295041Sbr	 * save result of prefilling for loop iteration.
141295041Sbr	 */
142295041Sbr	prefilled = lw[0];
143295041Sbr	while ((len -= 32) >= 4) {
144295041Sbr		u_int64_t prefilling = lw[8];
145295041Sbr		sum += prefilled + lw[1] + lw[2] + lw[3]
146295041Sbr			+ lw[4] + lw[5] + lw[6] + lw[7];
147295041Sbr		lw += 8;
148295041Sbr		prefilled = prefilling;
149295041Sbr	}
150295041Sbr	if (len >= 0) {
151295041Sbr		sum += prefilled + lw[1] + lw[2] + lw[3]
152295041Sbr			+ lw[4] + lw[5] + lw[6] + lw[7];
153295041Sbr		lw += 8;
154295041Sbr	} else {
155295041Sbr		len += 32;
156295041Sbr	}
157295041Sbr	while ((len -= 16) >= 0) {
158295041Sbr		sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
159295041Sbr		lw += 4;
160295041Sbr	}
161295041Sbr	len += 16;
162295041Sbr	while ((len -= 4) >= 0) {
163295041Sbr		sum += (u_int64_t) *lw++;
164295041Sbr	}
165295041Sbr	len += 4;
166295041Sbr	if (len > 0)
167295041Sbr		sum += (u_int64_t) (in_masks[len] & *lw);
168295041Sbr	REDUCE32;
169295041Sbr	return sum;
170295041Sbr}
171295041Sbr
172295041Sbru_short
173295041Sbrin_addword(u_short a, u_short b)
174295041Sbr{
175295041Sbr	u_int64_t sum = a + b;
176295041Sbr
177295041Sbr	ADDCARRY(sum);
178295041Sbr	return (sum);
179295041Sbr}
180295041Sbr
181295041Sbru_short
182295041Sbrin_pseudo(u_int32_t a, u_int32_t b, u_int32_t c)
183295041Sbr{
184295041Sbr	u_int64_t sum;
185295041Sbr	union q_util q_util;
186295041Sbr	union l_util l_util;
187295041Sbr
188295041Sbr	sum = (u_int64_t) a + b + c;
189295041Sbr	REDUCE16;
190295041Sbr	return (sum);
191295041Sbr}
192295041Sbr
193295041Sbru_short
194295041Sbrin_cksum_skip(struct mbuf *m, int len, int skip)
195295041Sbr{
196295041Sbr	u_int64_t sum = 0;
197295041Sbr	int mlen = 0;
198295041Sbr	int clen = 0;
199295041Sbr	caddr_t addr;
200295041Sbr	union q_util q_util;
201295041Sbr	union l_util l_util;
202295041Sbr
203295041Sbr        len -= skip;
204295041Sbr        for (; skip && m; m = m->m_next) {
205295041Sbr                if (m->m_len > skip) {
206295041Sbr                        mlen = m->m_len - skip;
207295041Sbr			addr = mtod(m, caddr_t) + skip;
208295041Sbr                        goto skip_start;
209295041Sbr                } else {
210295041Sbr                        skip -= m->m_len;
211295041Sbr                }
212295041Sbr        }
213295041Sbr
214295041Sbr	for (; m && len; m = m->m_next) {
215295041Sbr		if (m->m_len == 0)
216295041Sbr			continue;
217295041Sbr		mlen = m->m_len;
218295041Sbr		addr = mtod(m, caddr_t);
219295041Sbrskip_start:
220295041Sbr		if (len < mlen)
221295041Sbr			mlen = len;
222295041Sbr		if ((clen ^ (long) addr) & 1)
223295041Sbr		    sum += in_cksumdata(addr, mlen) << 8;
224295041Sbr		else
225295041Sbr		    sum += in_cksumdata(addr, mlen);
226295041Sbr
227295041Sbr		clen += mlen;
228295041Sbr		len -= mlen;
229295041Sbr	}
230295041Sbr	REDUCE16;
231295041Sbr	return (~sum & 0xffff);
232295041Sbr}
233295041Sbr
234295041Sbru_int in_cksum_hdr(const struct ip *ip)
235295041Sbr{
236295041Sbr    u_int64_t sum = in_cksumdata(ip, sizeof(struct ip));
237295041Sbr    union q_util q_util;
238295041Sbr    union l_util l_util;
239295041Sbr    REDUCE16;
240295041Sbr    return (~sum & 0xffff);
241295041Sbr}
242