166458Sdfr/* $FreeBSD$ */ 266458Sdfr/* $NetBSD: in_cksum.c,v 1.7 1997/09/02 13:18:15 thorpej Exp $ */ 366458Sdfr 4139790Simp/*- 566458Sdfr * Copyright (c) 1988, 1992, 1993 666458Sdfr * The Regents of the University of California. All rights reserved. 766458Sdfr * Copyright (c) 1996 866458Sdfr * Matt Thomas <matt@3am-software.com> 966458Sdfr * 1066458Sdfr * Redistribution and use in source and binary forms, with or without 1166458Sdfr * modification, are permitted provided that the following conditions 1266458Sdfr * are met: 1366458Sdfr * 1. Redistributions of source code must retain the above copyright 1466458Sdfr * notice, this list of conditions and the following disclaimer. 1566458Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1666458Sdfr * notice, this list of conditions and the following disclaimer in the 1766458Sdfr * documentation and/or other materials provided with the distribution. 1866458Sdfr * 3. All advertising materials mentioning features or use of this software 1966458Sdfr * must display the following acknowledgement: 2066458Sdfr * This product includes software developed by the University of 2166458Sdfr * California, Berkeley and its contributors. 2266458Sdfr * 4. Neither the name of the University nor the names of its contributors 2366458Sdfr * may be used to endorse or promote products derived from this software 2466458Sdfr * without specific prior written permission. 2566458Sdfr * 2666458Sdfr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2766458Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2866458Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2966458Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 3066458Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 3166458Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3266458Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3366458Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3466458Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3566458Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3666458Sdfr * SUCH DAMAGE. 3766458Sdfr * 3866458Sdfr * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93 3966458Sdfr */ 4066458Sdfr 4166458Sdfr#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 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