199040Sbenno/* $FreeBSD$ */
299040Sbenno/* $NetBSD: in_cksum.c,v 1.7 1997/09/02 13:18:15 thorpej Exp $ */
399040Sbenno
4139825Simp/*-
599040Sbenno * Copyright (c) 1988, 1992, 1993
699040Sbenno *	The Regents of the University of California.  All rights reserved.
799040Sbenno * Copyright (c) 1996
899040Sbenno *	Matt Thomas <matt@3am-software.com>
999040Sbenno *
1099040Sbenno * Redistribution and use in source and binary forms, with or without
1199040Sbenno * modification, are permitted provided that the following conditions
1299040Sbenno * are met:
1399040Sbenno * 1. Redistributions of source code must retain the above copyright
1499040Sbenno *    notice, this list of conditions and the following disclaimer.
1599040Sbenno * 2. Redistributions in binary form must reproduce the above copyright
1699040Sbenno *    notice, this list of conditions and the following disclaimer in the
1799040Sbenno *    documentation and/or other materials provided with the distribution.
1899040Sbenno * 3. All advertising materials mentioning features or use of this software
1999040Sbenno *    must display the following acknowledgement:
2099040Sbenno *	This product includes software developed by the University of
2199040Sbenno *	California, Berkeley and its contributors.
2299040Sbenno * 4. Neither the name of the University nor the names of its contributors
2399040Sbenno *    may be used to endorse or promote products derived from this software
2499040Sbenno *    without specific prior written permission.
2599040Sbenno *
2699040Sbenno * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2799040Sbenno * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2899040Sbenno * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2999040Sbenno * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3099040Sbenno * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3199040Sbenno * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3299040Sbenno * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3399040Sbenno * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3499040Sbenno * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3599040Sbenno * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3699040Sbenno * SUCH DAMAGE.
3799040Sbenno *
3899040Sbenno *	@(#)in_cksum.c	8.1 (Berkeley) 6/10/93
3999040Sbenno */
4099040Sbenno
4199040Sbenno#include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
4299040Sbenno
4399040Sbenno#include <sys/param.h>
4499040Sbenno#include <sys/mbuf.h>
4599040Sbenno#include <sys/systm.h>
4699040Sbenno#include <netinet/in_systm.h>
4799040Sbenno#include <netinet/in.h>
4899040Sbenno#include <netinet/ip.h>
4999040Sbenno#include <machine/in_cksum.h>
5099040Sbenno
5199040Sbenno/*
5299040Sbenno * Checksum routine for Internet Protocol family headers
5399040Sbenno *    (Portable Alpha version).
5499040Sbenno *
5599040Sbenno * This routine is very heavily used in the network
5699040Sbenno * code and should be modified for each CPU to be as fast as possible.
5799040Sbenno */
5899040Sbenno
5999040Sbenno#define ADDCARRY(x)  (x > 65535 ? x -= 65535 : x)
6099040Sbenno#define REDUCE32							  \
6199040Sbenno    {									  \
6299040Sbenno	q_util.q = sum;							  \
6399040Sbenno	sum = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3];	  \
6499040Sbenno    }
6599040Sbenno#define REDUCE16							  \
6699040Sbenno    {									  \
6799040Sbenno	q_util.q = sum;							  \
6899040Sbenno	l_util.l = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \
6999040Sbenno	sum = l_util.s[0] + l_util.s[1];				  \
7099040Sbenno	ADDCARRY(sum);							  \
7199040Sbenno    }
7299040Sbenno
7399040Sbennostatic const u_int32_t in_masks[] = {
7499040Sbenno#if 0
7599040Sbenno	/*0 bytes*/ /*1 byte*/	/*2 bytes*/ /*3 bytes*/
7699040Sbenno	0x00000000, 0x000000FF, 0x0000FFFF, 0x00FFFFFF,	/* offset 0 */
7799040Sbenno	0x00000000, 0x0000FF00, 0x00FFFF00, 0xFFFFFF00,	/* offset 1 */
7899040Sbenno	0x00000000, 0x00FF0000, 0xFFFF0000, 0xFFFF0000,	/* offset 2 */
7999040Sbenno	0x00000000, 0xFF000000, 0xFF000000, 0xFF000000,	/* offset 3 */
8099040Sbenno#else
8199040Sbenno	/*0 bytes*/ /*1 byte*/	/*2 bytes*/ /*3 bytes*/
8299040Sbenno	0x00000000, 0xFF000000, 0xFFFF0000, 0xFFFFFF00,	/* offset 0 */
8399040Sbenno	0x00000000, 0x00FF0000, 0x00FFFF00, 0x00FFFFFF,	/* offset 1 */
8499040Sbenno	0x00000000, 0x0000FF00, 0x0000FFFF, 0x0000FFFF,	/* offset 2 */
8599040Sbenno	0x00000000, 0x000000FF, 0x000000FF, 0x000000FF,	/* offset 3 */
8699040Sbenno#endif
8799040Sbenno};
8899040Sbenno
8999040Sbennounion l_util {
9099040Sbenno	u_int16_t s[2];
9199040Sbenno	u_int32_t l;
9299040Sbenno};
9399040Sbennounion q_util {
9499040Sbenno	u_int16_t s[4];
9599040Sbenno	u_int32_t l[2];
9699040Sbenno	u_int64_t q;
9799040Sbenno};
9899040Sbenno
9999040Sbennostatic u_int64_t
10099040Sbennoin_cksumdata(const void *buf, int len)
10199040Sbenno{
10299040Sbenno	const u_int32_t *lw = (const u_int32_t *) buf;
10399040Sbenno	u_int64_t sum = 0;
10499040Sbenno	u_int64_t prefilled;
10599040Sbenno	int offset;
10699040Sbenno	union q_util q_util;
10799040Sbenno
10899040Sbenno	if ((3 & (long) lw) == 0 && len == 20) {
10999040Sbenno	     sum = (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3] + lw[4];
11099040Sbenno	     REDUCE32;
11199040Sbenno	     return sum;
11299040Sbenno	}
11399040Sbenno
11499040Sbenno	if ((offset = 3 & (long) lw) != 0) {
11599040Sbenno		const u_int32_t *masks = in_masks + (offset << 2);
11699040Sbenno		lw = (u_int32_t *) (((long) lw) - offset);
11799040Sbenno		sum = *lw++ & masks[len >= 3 ? 3 : len];
11899040Sbenno		len -= 4 - offset;
11999040Sbenno		if (len <= 0) {
12099040Sbenno			REDUCE32;
12199040Sbenno			return sum;
12299040Sbenno		}
12399040Sbenno	}
12499040Sbenno#if 0
12599040Sbenno	/*
12699040Sbenno	 * Force to cache line boundary.
12799040Sbenno	 */
12899040Sbenno	offset = 32 - (0x1f & (long) lw);
12999040Sbenno	if (offset < 32 && len > offset) {
13099040Sbenno		len -= offset;
13199040Sbenno		if (4 & offset) {
13299040Sbenno			sum += (u_int64_t) lw[0];
13399040Sbenno			lw += 1;
13499040Sbenno		}
13599040Sbenno		if (8 & offset) {
13699040Sbenno			sum += (u_int64_t) lw[0] + lw[1];
13799040Sbenno			lw += 2;
13899040Sbenno		}
13999040Sbenno		if (16 & offset) {
14099040Sbenno			sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
14199040Sbenno			lw += 4;
14299040Sbenno		}
14399040Sbenno	}
14499040Sbenno#endif
14599040Sbenno	/*
14699040Sbenno	 * access prefilling to start load of next cache line.
14799040Sbenno	 * then add current cache line
14899040Sbenno	 * save result of prefilling for loop iteration.
14999040Sbenno	 */
15099040Sbenno	prefilled = lw[0];
15199040Sbenno	while ((len -= 32) >= 4) {
15299040Sbenno		u_int64_t prefilling = lw[8];
15399040Sbenno		sum += prefilled + lw[1] + lw[2] + lw[3]
15499040Sbenno			+ lw[4] + lw[5] + lw[6] + lw[7];
15599040Sbenno		lw += 8;
15699040Sbenno		prefilled = prefilling;
15799040Sbenno	}
15899040Sbenno	if (len >= 0) {
15999040Sbenno		sum += prefilled + lw[1] + lw[2] + lw[3]
16099040Sbenno			+ lw[4] + lw[5] + lw[6] + lw[7];
16199040Sbenno		lw += 8;
16299040Sbenno	} else {
16399040Sbenno		len += 32;
16499040Sbenno	}
16599040Sbenno	while ((len -= 16) >= 0) {
16699040Sbenno		sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
16799040Sbenno		lw += 4;
16899040Sbenno	}
16999040Sbenno	len += 16;
17099040Sbenno	while ((len -= 4) >= 0) {
17199040Sbenno		sum += (u_int64_t) *lw++;
17299040Sbenno	}
17399040Sbenno	len += 4;
17499040Sbenno	if (len > 0)
17599040Sbenno		sum += (u_int64_t) (in_masks[len] & *lw);
17699040Sbenno	REDUCE32;
17799040Sbenno	return sum;
17899040Sbenno}
17999040Sbenno
18099040Sbennou_short
18199040Sbennoin_addword(u_short a, u_short b)
18299040Sbenno{
18399040Sbenno	u_int64_t sum = a + b;
18499040Sbenno
18599040Sbenno	ADDCARRY(sum);
18699040Sbenno	return (sum);
18799040Sbenno}
18899040Sbenno
18999040Sbennou_short
19099040Sbennoin_pseudo(u_int32_t a, u_int32_t b, u_int32_t c)
19199040Sbenno{
19299040Sbenno	u_int64_t sum;
19399040Sbenno	union q_util q_util;
19499040Sbenno	union l_util l_util;
19599040Sbenno
19699040Sbenno	sum = (u_int64_t) a + b + c;
19799040Sbenno	REDUCE16;
19899040Sbenno	return (sum);
19999040Sbenno}
20099040Sbenno
20199040Sbennou_short
20299040Sbennoin_cksum_skip(struct mbuf *m, int len, int skip)
20399040Sbenno{
20499040Sbenno	u_int64_t sum = 0;
20599040Sbenno	int mlen = 0;
20699040Sbenno	int clen = 0;
20799040Sbenno	caddr_t addr;
20899040Sbenno	union q_util q_util;
20999040Sbenno	union l_util l_util;
21099040Sbenno
21199040Sbenno        len -= skip;
21299040Sbenno        for (; skip && m; m = m->m_next) {
21399040Sbenno                if (m->m_len > skip) {
21499040Sbenno                        mlen = m->m_len - skip;
21599040Sbenno			addr = mtod(m, caddr_t) + skip;
21699040Sbenno                        goto skip_start;
21799040Sbenno                } else {
21899040Sbenno                        skip -= m->m_len;
21999040Sbenno                }
22099040Sbenno        }
22199040Sbenno
22299040Sbenno	for (; m && len; m = m->m_next) {
22399040Sbenno		if (m->m_len == 0)
22499040Sbenno			continue;
22599040Sbenno		mlen = m->m_len;
22699040Sbenno		addr = mtod(m, caddr_t);
22799040Sbennoskip_start:
22899040Sbenno		if (len < mlen)
22999040Sbenno			mlen = len;
23099040Sbenno
231209975Snwhitehorn		if ((clen ^ (long) addr) & 1)
23299040Sbenno		    sum += in_cksumdata(addr, mlen) << 8;
23399040Sbenno		else
23499040Sbenno		    sum += in_cksumdata(addr, mlen);
23599040Sbenno
23699040Sbenno		clen += mlen;
23799040Sbenno		len -= mlen;
23899040Sbenno	}
23999040Sbenno	REDUCE16;
24099040Sbenno	return (~sum & 0xffff);
24199040Sbenno}
24299040Sbenno
24399040Sbennou_int in_cksum_hdr(const struct ip *ip)
24499040Sbenno{
24599040Sbenno    u_int64_t sum = in_cksumdata(ip, sizeof(struct ip));
24699040Sbenno    union q_util q_util;
24799040Sbenno    union l_util l_util;
24899040Sbenno    REDUCE16;
24999040Sbenno    return (~sum & 0xffff);
25099040Sbenno}
251