166458Sdfr/* $NetBSD: in_cksum.c,v 1.7 1997/09/02 13:18:15 thorpej Exp $ */
266458Sdfr
3139731Simp/*-
466458Sdfr * Copyright (c) 1988, 1992, 1993
566458Sdfr *	The Regents of the University of California.  All rights reserved.
666458Sdfr * Copyright (c) 1996
766458Sdfr *	Matt Thomas <matt@3am-software.com>
866458Sdfr *
966458Sdfr * Redistribution and use in source and binary forms, with or without
1066458Sdfr * modification, are permitted provided that the following conditions
1166458Sdfr * are met:
1266458Sdfr * 1. Redistributions of source code must retain the above copyright
1366458Sdfr *    notice, this list of conditions and the following disclaimer.
1466458Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1566458Sdfr *    notice, this list of conditions and the following disclaimer in the
1666458Sdfr *    documentation and/or other materials provided with the distribution.
1766458Sdfr * 3. All advertising materials mentioning features or use of this software
1866458Sdfr *    must display the following acknowledgement:
1966458Sdfr *	This product includes software developed by the University of
2066458Sdfr *	California, Berkeley and its contributors.
2166458Sdfr * 4. Neither the name of the University nor the names of its contributors
2266458Sdfr *    may be used to endorse or promote products derived from this software
2366458Sdfr *    without specific prior written permission.
2466458Sdfr *
2566458Sdfr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2666458Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2766458Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2866458Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2966458Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3066458Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3166458Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3266458Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3366458Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3466458Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3566458Sdfr * SUCH DAMAGE.
3666458Sdfr *
3766458Sdfr *	@(#)in_cksum.c	8.1 (Berkeley) 6/10/93
3866458Sdfr */
3966458Sdfr
4066458Sdfr#include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
41118031Sobrien__FBSDID("$FreeBSD$");
4266458Sdfr
4366458Sdfr#include <sys/param.h>
4466458Sdfr#include <sys/mbuf.h>
4566458Sdfr#include <sys/systm.h>
4666458Sdfr#include <netinet/in_systm.h>
4766458Sdfr#include <netinet/in.h>
4866458Sdfr#include <netinet/ip.h>
4966458Sdfr#include <machine/in_cksum.h>
5066458Sdfr
5166458Sdfr/*
5266458Sdfr * Checksum routine for Internet Protocol family headers
5366458Sdfr *    (Portable Alpha version).
5466458Sdfr *
5566458Sdfr * This routine is very heavily used in the network
5666458Sdfr * code and should be modified for each CPU to be as fast as possible.
5766458Sdfr */
5866458Sdfr
5966458Sdfr#define ADDCARRY(x)  (x > 65535 ? x -= 65535 : x)
6066458Sdfr#define REDUCE32							  \
6166458Sdfr    {									  \
6266458Sdfr	q_util.q = sum;							  \
6366458Sdfr	sum = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3];	  \
6466458Sdfr    }
6566458Sdfr#define REDUCE16							  \
6666458Sdfr    {									  \
6766458Sdfr	q_util.q = sum;							  \
6866458Sdfr	l_util.l = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \
6966458Sdfr	sum = l_util.s[0] + l_util.s[1];				  \
7066458Sdfr	ADDCARRY(sum);							  \
7166458Sdfr    }
7266458Sdfr
7366458Sdfrstatic const u_int32_t in_masks[] = {
7466458Sdfr	/*0 bytes*/ /*1 byte*/	/*2 bytes*/ /*3 bytes*/
7566458Sdfr	0x00000000, 0x000000FF, 0x0000FFFF, 0x00FFFFFF,	/* offset 0 */
7666458Sdfr	0x00000000, 0x0000FF00, 0x00FFFF00, 0xFFFFFF00,	/* offset 1 */
7766458Sdfr	0x00000000, 0x00FF0000, 0xFFFF0000, 0xFFFF0000,	/* offset 2 */
7866458Sdfr	0x00000000, 0xFF000000, 0xFF000000, 0xFF000000,	/* offset 3 */
7966458Sdfr};
8066458Sdfr
8166458Sdfrunion l_util {
8266458Sdfr	u_int16_t s[2];
8366458Sdfr	u_int32_t l;
8466458Sdfr};
8566458Sdfrunion q_util {
8666458Sdfr	u_int16_t s[4];
8766458Sdfr	u_int32_t l[2];
8866458Sdfr	u_int64_t q;
8966458Sdfr};
9066458Sdfr
9166464Sdfrstatic u_int64_t
9284553Sdfrin_cksumdata(const void *buf, int len)
9366458Sdfr{
9466464Sdfr	const u_int32_t *lw = (const u_int32_t *) buf;
9566458Sdfr	u_int64_t sum = 0;
9666458Sdfr	u_int64_t prefilled;
9766458Sdfr	int offset;
9866458Sdfr	union q_util q_util;
9966458Sdfr
10066458Sdfr	if ((3 & (long) lw) == 0 && len == 20) {
10166458Sdfr	     sum = (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3] + lw[4];
10266458Sdfr	     REDUCE32;
10366458Sdfr	     return sum;
10466458Sdfr	}
10566458Sdfr
10666458Sdfr	if ((offset = 3 & (long) lw) != 0) {
10766458Sdfr		const u_int32_t *masks = in_masks + (offset << 2);
10866458Sdfr		lw = (u_int32_t *) (((long) lw) - offset);
10966458Sdfr		sum = *lw++ & masks[len >= 3 ? 3 : len];
11066458Sdfr		len -= 4 - offset;
11166458Sdfr		if (len <= 0) {
11266458Sdfr			REDUCE32;
11366458Sdfr			return sum;
11466458Sdfr		}
11566458Sdfr	}
11666458Sdfr#if 0
11766458Sdfr	/*
11866458Sdfr	 * Force to cache line boundary.
11966458Sdfr	 */
12066458Sdfr	offset = 32 - (0x1f & (long) lw);
12166458Sdfr	if (offset < 32 && len > offset) {
12266458Sdfr		len -= offset;
12366458Sdfr		if (4 & offset) {
12466458Sdfr			sum += (u_int64_t) lw[0];
12566458Sdfr			lw += 1;
12666458Sdfr		}
12766458Sdfr		if (8 & offset) {
12866458Sdfr			sum += (u_int64_t) lw[0] + lw[1];
12966458Sdfr			lw += 2;
13066458Sdfr		}
13166458Sdfr		if (16 & offset) {
13266458Sdfr			sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
13366458Sdfr			lw += 4;
13466458Sdfr		}
13566458Sdfr	}
13666458Sdfr#endif
13766458Sdfr	/*
13866458Sdfr	 * access prefilling to start load of next cache line.
13966458Sdfr	 * then add current cache line
14066458Sdfr	 * save result of prefilling for loop iteration.
14166458Sdfr	 */
14266458Sdfr	prefilled = lw[0];
14366458Sdfr	while ((len -= 32) >= 4) {
14466458Sdfr		u_int64_t prefilling = lw[8];
14566458Sdfr		sum += prefilled + lw[1] + lw[2] + lw[3]
14666458Sdfr			+ lw[4] + lw[5] + lw[6] + lw[7];
14766458Sdfr		lw += 8;
14866458Sdfr		prefilled = prefilling;
14966458Sdfr	}
15066458Sdfr	if (len >= 0) {
15166458Sdfr		sum += prefilled + lw[1] + lw[2] + lw[3]
15266458Sdfr			+ lw[4] + lw[5] + lw[6] + lw[7];
15366458Sdfr		lw += 8;
15466458Sdfr	} else {
15566458Sdfr		len += 32;
15666458Sdfr	}
15766458Sdfr	while ((len -= 16) >= 0) {
15866458Sdfr		sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
15966458Sdfr		lw += 4;
16066458Sdfr	}
16166458Sdfr	len += 16;
16266458Sdfr	while ((len -= 4) >= 0) {
16366458Sdfr		sum += (u_int64_t) *lw++;
16466458Sdfr	}
16566458Sdfr	len += 4;
16666458Sdfr	if (len > 0)
16766458Sdfr		sum += (u_int64_t) (in_masks[len] & *lw);
16866458Sdfr	REDUCE32;
16966458Sdfr	return sum;
17066458Sdfr}
17166458Sdfr
17266458Sdfru_short
17366458Sdfrin_addword(u_short a, u_short b)
17466458Sdfr{
17566458Sdfr	u_int64_t sum = a + b;
17666458Sdfr
17766458Sdfr	ADDCARRY(sum);
17866458Sdfr	return (sum);
17966458Sdfr}
18066458Sdfr
18166458Sdfru_short
18266458Sdfrin_pseudo(u_int32_t a, u_int32_t b, u_int32_t c)
18366458Sdfr{
18466458Sdfr	u_int64_t sum;
18566458Sdfr	union q_util q_util;
18666458Sdfr	union l_util l_util;
18766458Sdfr
18866458Sdfr	sum = (u_int64_t) a + b + c;
18966458Sdfr	REDUCE16;
19066458Sdfr	return (sum);
19166458Sdfr}
19266458Sdfr
19366458Sdfru_short
19466464Sdfrin_cksum_skip(struct mbuf *m, int len, int skip)
19566458Sdfr{
19666458Sdfr	u_int64_t sum = 0;
19766458Sdfr	int mlen = 0;
19866458Sdfr	int clen = 0;
19966458Sdfr	caddr_t addr;
20066458Sdfr	union q_util q_util;
20166458Sdfr	union l_util l_util;
20266458Sdfr
20366458Sdfr        len -= skip;
20466458Sdfr        for (; skip && m; m = m->m_next) {
20566458Sdfr                if (m->m_len > skip) {
20666458Sdfr                        mlen = m->m_len - skip;
20766458Sdfr			addr = mtod(m, caddr_t) + skip;
20866458Sdfr                        goto skip_start;
20966458Sdfr                } else {
21066458Sdfr                        skip -= m->m_len;
21166458Sdfr                }
21266458Sdfr        }
21366458Sdfr
21466458Sdfr	for (; m && len; m = m->m_next) {
21566458Sdfr		if (m->m_len == 0)
21666458Sdfr			continue;
21766458Sdfr		mlen = m->m_len;
21866458Sdfr		addr = mtod(m, caddr_t);
21966458Sdfrskip_start:
22066458Sdfr		if (len < mlen)
22166458Sdfr			mlen = len;
22266458Sdfr		if ((clen ^ (long) addr) & 1)
22366458Sdfr		    sum += in_cksumdata(addr, mlen) << 8;
22466458Sdfr		else
22566458Sdfr		    sum += in_cksumdata(addr, mlen);
22666458Sdfr
22766458Sdfr		clen += mlen;
22866458Sdfr		len -= mlen;
22966458Sdfr	}
23066458Sdfr	REDUCE16;
23166458Sdfr	return (~sum & 0xffff);
23266458Sdfr}
23366458Sdfr
23466464Sdfru_int in_cksum_hdr(const struct ip *ip)
23566458Sdfr{
23666464Sdfr    u_int64_t sum = in_cksumdata(ip, sizeof(struct ip));
23766458Sdfr    union q_util q_util;
23866458Sdfr    union l_util l_util;
23966458Sdfr    REDUCE16;
24066458Sdfr    return (~sum & 0xffff);
24166458Sdfr}
242