1139826Simp/*- 253541Sshin * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 353541Sshin * All rights reserved. 453541Sshin * 553541Sshin * Redistribution and use in source and binary forms, with or without 653541Sshin * modification, are permitted provided that the following conditions 753541Sshin * are met: 853541Sshin * 1. Redistributions of source code must retain the above copyright 953541Sshin * notice, this list of conditions and the following disclaimer. 1053541Sshin * 2. Redistributions in binary form must reproduce the above copyright 1153541Sshin * notice, this list of conditions and the following disclaimer in the 1253541Sshin * documentation and/or other materials provided with the distribution. 1353541Sshin * 3. Neither the name of the project nor the names of its contributors 1453541Sshin * may be used to endorse or promote products derived from this software 1553541Sshin * without specific prior written permission. 1653541Sshin * 1753541Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 1853541Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1953541Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2053541Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 2153541Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2253541Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2353541Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2453541Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2553541Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2653541Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2753541Sshin * SUCH DAMAGE. 28174510Sobrien * 29174510Sobrien * $KAME: in6_cksum.c,v 1.10 2000/12/03 00:53:59 itojun Exp $ 3053541Sshin */ 3153541Sshin 32139826Simp/*- 3353541Sshin * Copyright (c) 1988, 1992, 1993 3453541Sshin * The Regents of the University of California. All rights reserved. 3553541Sshin * 3653541Sshin * Redistribution and use in source and binary forms, with or without 3753541Sshin * modification, are permitted provided that the following conditions 3853541Sshin * are met: 3953541Sshin * 1. Redistributions of source code must retain the above copyright 4053541Sshin * notice, this list of conditions and the following disclaimer. 4153541Sshin * 2. Redistributions in binary form must reproduce the above copyright 4253541Sshin * notice, this list of conditions and the following disclaimer in the 4353541Sshin * documentation and/or other materials provided with the distribution. 4453541Sshin * 4. Neither the name of the University nor the names of its contributors 4553541Sshin * may be used to endorse or promote products derived from this software 4653541Sshin * without specific prior written permission. 4753541Sshin * 4853541Sshin * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 4953541Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 5053541Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 5153541Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 5253541Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 5353541Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 5453541Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 5553541Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 5653541Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 5753541Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 5853541Sshin * SUCH DAMAGE. 5953541Sshin * 6053541Sshin * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93 6153541Sshin */ 6253541Sshin 63174510Sobrien#include <sys/cdefs.h> 64174510Sobrien__FBSDID("$FreeBSD$"); 65174510Sobrien 6653541Sshin#include <sys/param.h> 6753541Sshin#include <sys/mbuf.h> 6853541Sshin#include <sys/systm.h> 6953541Sshin#include <netinet/in.h> 7062587Sitojun#include <netinet/ip6.h> 71148385Sume#include <netinet6/scope6_var.h> 7253541Sshin 7353541Sshin/* 7453541Sshin * Checksum routine for Internet Protocol family headers (Portable Version). 7553541Sshin * 7653541Sshin * This routine is very heavily used in the network 7753541Sshin * code and should be modified for each CPU to be as fast as possible. 7853541Sshin */ 7953541Sshin 8062587Sitojun#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x) 81213766Srpaulo#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; (void)ADDCARRY(sum);} 8253541Sshin 83235924Sbzstatic int 84235924Sbz_in6_cksum_pseudo(struct ip6_hdr *ip6, uint32_t len, uint8_t nxt, uint16_t csum) 85235924Sbz{ 86235924Sbz int sum; 87235924Sbz uint16_t scope, *w; 88235924Sbz union { 89235924Sbz u_int16_t phs[4]; 90235924Sbz struct { 91235924Sbz u_int32_t ph_len; 92235924Sbz u_int8_t ph_zero[3]; 93235924Sbz u_int8_t ph_nxt; 94235924Sbz } __packed ph; 95235924Sbz } uph; 96235924Sbz 97235924Sbz sum = csum; 98235924Sbz 99235924Sbz /* 100235924Sbz * First create IP6 pseudo header and calculate a summary. 101235924Sbz */ 102235924Sbz uph.ph.ph_len = htonl(len); 103235924Sbz uph.ph.ph_zero[0] = uph.ph.ph_zero[1] = uph.ph.ph_zero[2] = 0; 104235924Sbz uph.ph.ph_nxt = nxt; 105235924Sbz 106235924Sbz /* Payload length and upper layer identifier. */ 107235924Sbz sum += uph.phs[0]; sum += uph.phs[1]; 108235924Sbz sum += uph.phs[2]; sum += uph.phs[3]; 109235924Sbz 110235924Sbz /* IPv6 source address. */ 111235924Sbz scope = in6_getscope(&ip6->ip6_src); 112235924Sbz w = (u_int16_t *)&ip6->ip6_src; 113235924Sbz sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 114235924Sbz sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; 115235924Sbz if (scope != 0) 116235924Sbz sum -= scope; 117235924Sbz 118235924Sbz /* IPv6 destination address. */ 119235924Sbz scope = in6_getscope(&ip6->ip6_dst); 120235924Sbz w = (u_int16_t *)&ip6->ip6_dst; 121235924Sbz sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 122235924Sbz sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; 123235924Sbz if (scope != 0) 124235924Sbz sum -= scope; 125235924Sbz 126235924Sbz return (sum); 127235924Sbz} 128235924Sbz 129235924Sbzint 130235924Sbzin6_cksum_pseudo(struct ip6_hdr *ip6, uint32_t len, uint8_t nxt, uint16_t csum) 131235924Sbz{ 132235924Sbz int sum; 133235924Sbz union { 134235924Sbz u_int16_t s[2]; 135235924Sbz u_int32_t l; 136235924Sbz } l_util; 137235924Sbz 138235924Sbz sum = _in6_cksum_pseudo(ip6, len, nxt, csum); 139235924Sbz REDUCE; 140235924Sbz return (sum); 141235924Sbz} 142235924Sbz 14353541Sshin/* 144215956Sbrucec * m MUST contain a contiguous IP6 header. 145108533Sschweikh * off is an offset where TCP/UDP/ICMP6 header starts. 14653541Sshin * len is a total length of a transport segment. 14753541Sshin * (e.g. TCP header + TCP payload) 148272404Stuexen * cov is the number of bytes to be taken into account for the checksum 14953541Sshin */ 15053541Sshinint 151272404Stuexenin6_cksum_partial(struct mbuf *m, u_int8_t nxt, u_int32_t off, 152272404Stuexen u_int32_t len, u_int32_t cov) 15353541Sshin{ 154114205Ssuz struct ip6_hdr *ip6; 155235921Sbz u_int16_t *w, scope; 156235921Sbz int byte_swapped, mlen; 157235921Sbz int sum; 15853541Sshin union { 15965637Sitojun u_int16_t phs[4]; 16065637Sitojun struct { 16165637Sitojun u_int32_t ph_len; 16265637Sitojun u_int8_t ph_zero[3]; 16365637Sitojun u_int8_t ph_nxt; 164165965Simp } __packed ph; 16565637Sitojun } uph; 16665637Sitojun union { 16753541Sshin u_int8_t c[2]; 16853541Sshin u_int16_t s; 16953541Sshin } s_util; 17053541Sshin union { 17153541Sshin u_int16_t s[2]; 17253541Sshin u_int32_t l; 17353541Sshin } l_util; 17453541Sshin 175235921Sbz /* Sanity check. */ 176235921Sbz KASSERT(m->m_pkthdr.len >= off + len, ("%s: mbuf len (%d) < off(%d)+" 177235921Sbz "len(%d)", __func__, m->m_pkthdr.len, off, len)); 17853541Sshin 17953541Sshin /* 18053541Sshin * First create IP6 pseudo header and calculate a summary. 18153541Sshin */ 18253541Sshin uph.ph.ph_len = htonl(len); 183235921Sbz uph.ph.ph_zero[0] = uph.ph.ph_zero[1] = uph.ph.ph_zero[2] = 0; 18453541Sshin uph.ph.ph_nxt = nxt; 18553541Sshin 186235921Sbz /* Payload length and upper layer identifier. */ 187235921Sbz sum = uph.phs[0]; sum += uph.phs[1]; 188235921Sbz sum += uph.phs[2]; sum += uph.phs[3]; 189235921Sbz 190235921Sbz ip6 = mtod(m, struct ip6_hdr *); 191235921Sbz 192235921Sbz /* IPv6 source address. */ 193235921Sbz scope = in6_getscope(&ip6->ip6_src); 194235921Sbz w = (u_int16_t *)&ip6->ip6_src; 195148385Sume sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 196148385Sume sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; 197235921Sbz if (scope != 0) 198235921Sbz sum -= scope; 199148385Sume 200235921Sbz /* IPv6 destination address. */ 201235921Sbz scope = in6_getscope(&ip6->ip6_dst); 202235921Sbz w = (u_int16_t *)&ip6->ip6_dst; 203148385Sume sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 204148385Sume sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; 205235921Sbz if (scope != 0) 206235921Sbz sum -= scope; 207148385Sume 20853541Sshin /* 20953541Sshin * Secondly calculate a summary of the first mbuf excluding offset. 21053541Sshin */ 211160051Syar while (off > 0) { 21253541Sshin if (m->m_len <= off) 21353541Sshin off -= m->m_len; 21453541Sshin else 21553541Sshin break; 21653541Sshin m = m->m_next; 21753541Sshin } 21853541Sshin w = (u_int16_t *)(mtod(m, u_char *) + off); 21953541Sshin mlen = m->m_len - off; 220272404Stuexen if (cov < mlen) 221272404Stuexen mlen = cov; 222272404Stuexen cov -= mlen; 22353541Sshin /* 22453541Sshin * Force to even boundary. 22553541Sshin */ 226235921Sbz if ((1 & (long)w) && (mlen > 0)) { 22753541Sshin REDUCE; 22853541Sshin sum <<= 8; 22953541Sshin s_util.c[0] = *(u_char *)w; 23053541Sshin w = (u_int16_t *)((char *)w + 1); 23153541Sshin mlen--; 23253541Sshin byte_swapped = 1; 233235921Sbz } else 234235921Sbz byte_swapped = 0; 235235921Sbz 23653541Sshin /* 23753541Sshin * Unroll the loop to make overhead from 23853541Sshin * branches &c small. 23953541Sshin */ 24053541Sshin while ((mlen -= 32) >= 0) { 24153541Sshin sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 24253541Sshin sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; 24353541Sshin sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11]; 24453541Sshin sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15]; 24553541Sshin w += 16; 24653541Sshin } 24753541Sshin mlen += 32; 24853541Sshin while ((mlen -= 8) >= 0) { 24953541Sshin sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 25053541Sshin w += 4; 25153541Sshin } 25253541Sshin mlen += 8; 25353541Sshin if (mlen == 0 && byte_swapped == 0) 25453541Sshin goto next; 25553541Sshin REDUCE; 25653541Sshin while ((mlen -= 2) >= 0) { 25753541Sshin sum += *w++; 25853541Sshin } 25953541Sshin if (byte_swapped) { 26053541Sshin REDUCE; 26153541Sshin sum <<= 8; 26253541Sshin byte_swapped = 0; 26353541Sshin if (mlen == -1) { 26453541Sshin s_util.c[1] = *(char *)w; 26553541Sshin sum += s_util.s; 26653541Sshin mlen = 0; 26753541Sshin } else 26853541Sshin mlen = -1; 26953541Sshin } else if (mlen == -1) 27053541Sshin s_util.c[0] = *(char *)w; 27153541Sshin next: 27253541Sshin m = m->m_next; 27353541Sshin 27453541Sshin /* 27553541Sshin * Lastly calculate a summary of the rest of mbufs. 27653541Sshin */ 277114205Ssuz 278272404Stuexen for (;m && cov; m = m->m_next) { 27953541Sshin if (m->m_len == 0) 28053541Sshin continue; 28153541Sshin w = mtod(m, u_int16_t *); 28253541Sshin if (mlen == -1) { 28353541Sshin /* 28453541Sshin * The first byte of this mbuf is the continuation 28553541Sshin * of a word spanning between this mbuf and the 28653541Sshin * last mbuf. 28753541Sshin * 28853541Sshin * s_util.c[0] is already saved when scanning previous 28953541Sshin * mbuf. 29053541Sshin */ 29153541Sshin s_util.c[1] = *(char *)w; 29253541Sshin sum += s_util.s; 29353541Sshin w = (u_int16_t *)((char *)w + 1); 29453541Sshin mlen = m->m_len - 1; 295272404Stuexen cov--; 29653541Sshin } else 29753541Sshin mlen = m->m_len; 298272404Stuexen if (cov < mlen) 299272404Stuexen mlen = cov; 300272404Stuexen cov -= mlen; 30153541Sshin /* 30253541Sshin * Force to even boundary. 30353541Sshin */ 30453541Sshin if ((1 & (long) w) && (mlen > 0)) { 30553541Sshin REDUCE; 30653541Sshin sum <<= 8; 30753541Sshin s_util.c[0] = *(u_char *)w; 30853541Sshin w = (u_int16_t *)((char *)w + 1); 30953541Sshin mlen--; 31053541Sshin byte_swapped = 1; 31153541Sshin } 31253541Sshin /* 31353541Sshin * Unroll the loop to make overhead from 31453541Sshin * branches &c small. 31553541Sshin */ 31653541Sshin while ((mlen -= 32) >= 0) { 31753541Sshin sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 31853541Sshin sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7]; 31953541Sshin sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11]; 32053541Sshin sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15]; 32153541Sshin w += 16; 32253541Sshin } 32353541Sshin mlen += 32; 32453541Sshin while ((mlen -= 8) >= 0) { 32553541Sshin sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3]; 32653541Sshin w += 4; 32753541Sshin } 32853541Sshin mlen += 8; 32953541Sshin if (mlen == 0 && byte_swapped == 0) 33053541Sshin continue; 33153541Sshin REDUCE; 33253541Sshin while ((mlen -= 2) >= 0) { 33353541Sshin sum += *w++; 33453541Sshin } 33553541Sshin if (byte_swapped) { 33653541Sshin REDUCE; 33753541Sshin sum <<= 8; 33853541Sshin byte_swapped = 0; 33953541Sshin if (mlen == -1) { 34053541Sshin s_util.c[1] = *(char *)w; 34153541Sshin sum += s_util.s; 34253541Sshin mlen = 0; 34353541Sshin } else 34453541Sshin mlen = -1; 34553541Sshin } else if (mlen == -1) 34653541Sshin s_util.c[0] = *(char *)w; 34753541Sshin } 348272404Stuexen if (cov) 349114205Ssuz panic("in6_cksum: out of data"); 35053541Sshin if (mlen == -1) { 35153541Sshin /* The last mbuf has odd # of bytes. Follow the 35253541Sshin standard (the odd byte may be shifted left by 8 bits 35353541Sshin or not as determined by endian-ness of the machine) */ 35453541Sshin s_util.c[1] = 0; 35553541Sshin sum += s_util.s; 35653541Sshin } 35753541Sshin REDUCE; 35853541Sshin return (~sum & 0xffff); 35953541Sshin} 360272404Stuexen 361272404Stuexenint 362272404Stuexenin6_cksum(struct mbuf *m, u_int8_t nxt, u_int32_t off, u_int32_t len) 363272404Stuexen{ 364272404Stuexen return (in6_cksum_partial(m, nxt, off, len, len)); 365272404Stuexen} 366