1281494Sandrew/* $NetBSD: in_cksum.c,v 1.7 1997/09/02 13:18:15 thorpej Exp $ */
2281494Sandrew
3281494Sandrew/*-
4281494Sandrew * Copyright (c) 1988, 1992, 1993
5281494Sandrew *	The Regents of the University of California.  All rights reserved.
6281494Sandrew * Copyright (c) 1996
7281494Sandrew *	Matt Thomas <matt@3am-software.com>
8281494Sandrew *
9281494Sandrew * Redistribution and use in source and binary forms, with or without
10281494Sandrew * modification, are permitted provided that the following conditions
11281494Sandrew * are met:
12281494Sandrew * 1. Redistributions of source code must retain the above copyright
13281494Sandrew *    notice, this list of conditions and the following disclaimer.
14281494Sandrew * 2. Redistributions in binary form must reproduce the above copyright
15281494Sandrew *    notice, this list of conditions and the following disclaimer in the
16281494Sandrew *    documentation and/or other materials provided with the distribution.
17281494Sandrew * 3. All advertising materials mentioning features or use of this software
18281494Sandrew *    must display the following acknowledgement:
19281494Sandrew *	This product includes software developed by the University of
20281494Sandrew *	California, Berkeley and its contributors.
21281494Sandrew * 4. Neither the name of the University nor the names of its contributors
22281494Sandrew *    may be used to endorse or promote products derived from this software
23281494Sandrew *    without specific prior written permission.
24281494Sandrew *
25281494Sandrew * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26281494Sandrew * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27281494Sandrew * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28281494Sandrew * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29281494Sandrew * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30281494Sandrew * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31281494Sandrew * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32281494Sandrew * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33281494Sandrew * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34281494Sandrew * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35281494Sandrew * SUCH DAMAGE.
36281494Sandrew *
37281494Sandrew *	@(#)in_cksum.c	8.1 (Berkeley) 6/10/93
38281494Sandrew */
39281494Sandrew
40281494Sandrew#include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
41281494Sandrew__FBSDID("$FreeBSD$");
42281494Sandrew
43281494Sandrew#include <sys/param.h>
44281494Sandrew#include <sys/mbuf.h>
45281494Sandrew#include <sys/systm.h>
46281494Sandrew#include <netinet/in_systm.h>
47281494Sandrew#include <netinet/in.h>
48281494Sandrew#include <netinet/ip.h>
49281494Sandrew#include <machine/in_cksum.h>
50281494Sandrew
51281494Sandrew/*
52281494Sandrew * Checksum routine for Internet Protocol family headers
53281494Sandrew *    (Portable Alpha version).
54281494Sandrew *
55281494Sandrew * This routine is very heavily used in the network
56281494Sandrew * code and should be modified for each CPU to be as fast as possible.
57281494Sandrew */
58281494Sandrew
59281494Sandrew#define ADDCARRY(x)  (x > 65535 ? x -= 65535 : x)
60281494Sandrew#define REDUCE32							  \
61281494Sandrew    {									  \
62281494Sandrew	q_util.q = sum;							  \
63281494Sandrew	sum = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3];	  \
64281494Sandrew    }
65281494Sandrew#define REDUCE16							  \
66281494Sandrew    {									  \
67281494Sandrew	q_util.q = sum;							  \
68281494Sandrew	l_util.l = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \
69281494Sandrew	sum = l_util.s[0] + l_util.s[1];				  \
70281494Sandrew	ADDCARRY(sum);							  \
71281494Sandrew    }
72281494Sandrew
73281494Sandrewstatic const u_int32_t in_masks[] = {
74281494Sandrew	/*0 bytes*/ /*1 byte*/	/*2 bytes*/ /*3 bytes*/
75281494Sandrew	0x00000000, 0x000000FF, 0x0000FFFF, 0x00FFFFFF,	/* offset 0 */
76281494Sandrew	0x00000000, 0x0000FF00, 0x00FFFF00, 0xFFFFFF00,	/* offset 1 */
77281494Sandrew	0x00000000, 0x00FF0000, 0xFFFF0000, 0xFFFF0000,	/* offset 2 */
78281494Sandrew	0x00000000, 0xFF000000, 0xFF000000, 0xFF000000,	/* offset 3 */
79281494Sandrew};
80281494Sandrew
81281494Sandrewunion l_util {
82281494Sandrew	u_int16_t s[2];
83281494Sandrew	u_int32_t l;
84281494Sandrew};
85281494Sandrewunion q_util {
86281494Sandrew	u_int16_t s[4];
87281494Sandrew	u_int32_t l[2];
88281494Sandrew	u_int64_t q;
89281494Sandrew};
90281494Sandrew
91281494Sandrewstatic u_int64_t
92281494Sandrewin_cksumdata(const void *buf, int len)
93281494Sandrew{
94281494Sandrew	const u_int32_t *lw = (const u_int32_t *) buf;
95281494Sandrew	u_int64_t sum = 0;
96281494Sandrew	u_int64_t prefilled;
97281494Sandrew	int offset;
98281494Sandrew	union q_util q_util;
99281494Sandrew
100281494Sandrew	if ((3 & (long) lw) == 0 && len == 20) {
101281494Sandrew	     sum = (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3] + lw[4];
102281494Sandrew	     REDUCE32;
103281494Sandrew	     return sum;
104281494Sandrew	}
105281494Sandrew
106281494Sandrew	if ((offset = 3 & (long) lw) != 0) {
107281494Sandrew		const u_int32_t *masks = in_masks + (offset << 2);
108281494Sandrew		lw = (u_int32_t *) (((long) lw) - offset);
109281494Sandrew		sum = *lw++ & masks[len >= 3 ? 3 : len];
110281494Sandrew		len -= 4 - offset;
111281494Sandrew		if (len <= 0) {
112281494Sandrew			REDUCE32;
113281494Sandrew			return sum;
114281494Sandrew		}
115281494Sandrew	}
116281494Sandrew#if 0
117281494Sandrew	/*
118281494Sandrew	 * Force to cache line boundary.
119281494Sandrew	 */
120281494Sandrew	offset = 32 - (0x1f & (long) lw);
121281494Sandrew	if (offset < 32 && len > offset) {
122281494Sandrew		len -= offset;
123281494Sandrew		if (4 & offset) {
124281494Sandrew			sum += (u_int64_t) lw[0];
125281494Sandrew			lw += 1;
126281494Sandrew		}
127281494Sandrew		if (8 & offset) {
128281494Sandrew			sum += (u_int64_t) lw[0] + lw[1];
129281494Sandrew			lw += 2;
130281494Sandrew		}
131281494Sandrew		if (16 & offset) {
132281494Sandrew			sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
133281494Sandrew			lw += 4;
134281494Sandrew		}
135281494Sandrew	}
136281494Sandrew#endif
137281494Sandrew	/*
138281494Sandrew	 * access prefilling to start load of next cache line.
139281494Sandrew	 * then add current cache line
140281494Sandrew	 * save result of prefilling for loop iteration.
141281494Sandrew	 */
142281494Sandrew	prefilled = lw[0];
143281494Sandrew	while ((len -= 32) >= 4) {
144281494Sandrew		u_int64_t prefilling = lw[8];
145281494Sandrew		sum += prefilled + lw[1] + lw[2] + lw[3]
146281494Sandrew			+ lw[4] + lw[5] + lw[6] + lw[7];
147281494Sandrew		lw += 8;
148281494Sandrew		prefilled = prefilling;
149281494Sandrew	}
150281494Sandrew	if (len >= 0) {
151281494Sandrew		sum += prefilled + lw[1] + lw[2] + lw[3]
152281494Sandrew			+ lw[4] + lw[5] + lw[6] + lw[7];
153281494Sandrew		lw += 8;
154281494Sandrew	} else {
155281494Sandrew		len += 32;
156281494Sandrew	}
157281494Sandrew	while ((len -= 16) >= 0) {
158281494Sandrew		sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
159281494Sandrew		lw += 4;
160281494Sandrew	}
161281494Sandrew	len += 16;
162281494Sandrew	while ((len -= 4) >= 0) {
163281494Sandrew		sum += (u_int64_t) *lw++;
164281494Sandrew	}
165281494Sandrew	len += 4;
166281494Sandrew	if (len > 0)
167281494Sandrew		sum += (u_int64_t) (in_masks[len] & *lw);
168281494Sandrew	REDUCE32;
169281494Sandrew	return sum;
170281494Sandrew}
171281494Sandrew
172281494Sandrewu_short
173281494Sandrewin_addword(u_short a, u_short b)
174281494Sandrew{
175281494Sandrew	u_int64_t sum = a + b;
176281494Sandrew
177281494Sandrew	ADDCARRY(sum);
178281494Sandrew	return (sum);
179281494Sandrew}
180281494Sandrew
181281494Sandrewu_short
182281494Sandrewin_pseudo(u_int32_t a, u_int32_t b, u_int32_t c)
183281494Sandrew{
184281494Sandrew	u_int64_t sum;
185281494Sandrew	union q_util q_util;
186281494Sandrew	union l_util l_util;
187281494Sandrew
188281494Sandrew	sum = (u_int64_t) a + b + c;
189281494Sandrew	REDUCE16;
190281494Sandrew	return (sum);
191281494Sandrew}
192281494Sandrew
193281494Sandrewu_short
194281494Sandrewin_cksum_skip(struct mbuf *m, int len, int skip)
195281494Sandrew{
196281494Sandrew	u_int64_t sum = 0;
197281494Sandrew	int mlen = 0;
198281494Sandrew	int clen = 0;
199281494Sandrew	caddr_t addr;
200281494Sandrew	union q_util q_util;
201281494Sandrew	union l_util l_util;
202281494Sandrew
203281494Sandrew        len -= skip;
204281494Sandrew        for (; skip && m; m = m->m_next) {
205281494Sandrew                if (m->m_len > skip) {
206281494Sandrew                        mlen = m->m_len - skip;
207281494Sandrew			addr = mtod(m, caddr_t) + skip;
208281494Sandrew                        goto skip_start;
209281494Sandrew                } else {
210281494Sandrew                        skip -= m->m_len;
211281494Sandrew                }
212281494Sandrew        }
213281494Sandrew
214281494Sandrew	for (; m && len; m = m->m_next) {
215281494Sandrew		if (m->m_len == 0)
216281494Sandrew			continue;
217281494Sandrew		mlen = m->m_len;
218281494Sandrew		addr = mtod(m, caddr_t);
219281494Sandrewskip_start:
220281494Sandrew		if (len < mlen)
221281494Sandrew			mlen = len;
222281494Sandrew		if ((clen ^ (long) addr) & 1)
223281494Sandrew		    sum += in_cksumdata(addr, mlen) << 8;
224281494Sandrew		else
225281494Sandrew		    sum += in_cksumdata(addr, mlen);
226281494Sandrew
227281494Sandrew		clen += mlen;
228281494Sandrew		len -= mlen;
229281494Sandrew	}
230281494Sandrew	REDUCE16;
231281494Sandrew	return (~sum & 0xffff);
232281494Sandrew}
233281494Sandrew
234281494Sandrewu_int in_cksum_hdr(const struct ip *ip)
235281494Sandrew{
236281494Sandrew    u_int64_t sum = in_cksumdata(ip, sizeof(struct ip));
237281494Sandrew    union q_util q_util;
238281494Sandrew    union l_util l_util;
239281494Sandrew    REDUCE16;
240281494Sandrew    return (~sum & 0xffff);
241281494Sandrew}
242